Adding Delay to Event

Hi!

How can I add methods to DesignedComponents after loading them in code?
For instance
Button1 := DesignedComponent (‘Button1’) as TCastleButton;
I would like to add a small delay so when I click a button it takes for instance 2 seconds before the action is performed.
The idea is that when I give a command to a transform (scene sprite) there will be a small delay before it actually responds.

To register events on components, follow engine examples/templates like “3D Model Viewer” template:

procedure TStateMain.Start;
begin
  inherited;

  { Find components, by name, that we need to access from code }
  ButtonStopAnimation := DesignedComponent('ButtonStopAnimation') as TCastleButton;

  { Assign OnClick handler to buttons }
  ButtonStopAnimation.OnClick := @ClickStopAnimation;
end;

procedure TStateMain.ClickStopAnimation(Sender: TObject);
begin
  ....
end;

To run something with delay, use a variable controlling the timeout. That is,

  • add a variable like TimeToDoSomething: TFloatTime

  • when user clicks a button, do TimeToDoSomething := 2.0 (delay in 2 seconds)

  • in state Update method decrease it , and watch when it drops to zero, like

procedure TStateMain.Update(const SecondsPassed: Single; var HandleInput: Boolean);
begin
  inherited;
  if TimeToDoSomething > 0 then
  begin
    TimeToDoSomething := TimeToDoSomething - SecondsPassed;
    if TimeToDoSomething <= 0 then
    begin
      // do something ...
    end;
  end;
end;

Thanks!
And if I want to run a sequence of events and only execute the next event when the previous one is finished?
For instance: a character sprite is walking off screen and the screen has to fade out before the next location and sprites are loaded?

And if I want to run a sequence of events and only execute the next event when the previous one is finished?

Depends. If you want to do that in a simple way - just create two timeouts. E.g. adding to @michalis example:

procedure TStateMain.Update(const SecondsPassed: Single; var HandleInput: Boolean);
begin
  inherited;
  if TimeToDoSomething > 0 then
  begin
    TimeToDoSomething := TimeToDoSomething - SecondsPassed;
    if TimeToDoSomething <= 0 then
    begin
      StartSomeAnimation; // it should check if the animation started already not to start it twice
      TimeToDoSomethingNext := TimeToDoSomethingNext - SecondsPassed;
      if TimeToDoSomethingNext <= 0 then
      begin
         ChangeLocation;
    end;
  end;
end;

Note, that while simple, this is not the best solution for a larger scale. There you’ll have to create some event manager otherwise you’ll drown in timeouts and ifs. But this is not an easy task, so if you don’t plan to significantly extend the usecase, I’d go with the simple solution.

Thanks.
I don’t want to make this too difficult ( I have problems enough already with the screen coordination) :wink: so this is a fine approach.
For an action following fading out a screen I think I should use a routine that has no countdown but executes after all screen colors are faded to black?

I’m not sure if I follow. If you have “fade out” animation and loading starts at its end, then indeed, you can do without the second timer (sorry, I think I’ve misunderstood your first post). As in:

procedure TStateMain.Update(const SecondsPassed: Single; var HandleInput: Boolean);
begin
  inherited;
  if TimeToDoSomething > 0 then
  begin
    if SceneIsFadingOut then
    begin
      TimeToFadeOut := TimeToFadeOut - SecondsPassed;
      SetFadedColors(1 - TimeToFadeOut / FadeOutTimeout);
      if TimeToDoSomething <= 0 then
        StartLoading;
  end;
end;

Thanks.
I will try this out as soon I have finally sorted out the screen coordination problems.
:slight_smile: