Timed events and waiting

Hi!

How can I show LabelCaption Text for a few seconds and then clear it automatically?

And how can I let the program wait for finishing an event before going to the next event or executing piece of code?

Hello! :slight_smile: To do that you should either use TUiState.Update or TCastleWindowsBase.OnUpdate or even TCastleLabel.Update. E.g.

TDisappearingLabel = class(TCastleLabel)
public
  Timeout: Single;
  procedure Update(const SecondsPassed: Single;
      var HandleInput: boolean); override;
end;

procedure TDisappearingLabel .Update(const SecondsPassed: Single;
      var HandleInput: boolean);
begin
  inherited;
  Timeout -= SecondsPassed;
  if Timeout < 0 then
    Exists := false;
end;

This will make the label disappear after Timeout seconds.

That depends on what are you trying to do. Overall you can just use the same method as above - through Update. Or you can have a similar approach with “events” e.g.

TDisappearingLabel = class(TCastleLabel)
public
  OnDone: TNotifyEvent;
  function Press(const Event: TInputPressRelease): Boolean; override;
end;

function TDisappearingLabel.Press(const Event: TInputPressRelease): Boolean;
begin
  Result := inherited;
  if Event.Key = keyEnter then
  begin
    Result := true;
    Exists := false;
    if Assigned(OnDone) then
      OnDone(Self);
  end;
end;

Thanks!
It tried the first one but it is confusing to me how to implement this in updating my current Label.

I have created a label TalkTextLabel.
So how can I make it display a text for a few seconds or make it dependent of the length of the text so larger lines are displayed longer?

TalkTextLabel.Text.Add(‘test’);
TalkTextLabel.Update(); ??

That depends on how you are handling your current Label. The pseudocode above was given for the case when you create the label manually:

procedure TMyState.Start;
begin
  inherited;
  ... (load and create other UI elements here) ...
  MyLabel := TDisappearingLabel.Create;
  MyLabel.Caption := Text;
  MyLabel.Timeout := 5;
  InsertFront(MyLabel);
end;

It will insert the MyLabel in front of other UIs and will automatically call Update on it - it will disappear in 5 seconds after Start.
You’ll need a few additional improvements to have this “special” TDisappearingLabel available in the Castle Editor, so it’s easier to create it by code for now.

Thanks.
I am obviously doing something wrong here as I get Identifier not found “Timeout” error?

I have:

TDisappearingLabel = class(TCastleLabel)
public
Timeout: Single;
procedure Update(const SecondsPassed: Single;
var HandleInput: boolean); override;
end;

procedure TDisappearingLabel.Update(const SecondsPassed: Single;
var HandleInput: boolean);
begin
inherited;
Timeout -= SecondsPassed;
if Timeout < 0 then
Exists := false;
end;

procedure TalkLabel;
begin
TalkTextLabel := TDisappearingLabel.Create(Application);
with TalkTextLabel do
begin
Color := White;
Frame:= true;
FrameColor := White;
Outline := 2;
OutlineHighQuality := true;
BorderColor := Red;
Font.OutlineColor := Black;
FontSize := 18;
MaxWidth:= 500;
CustomFont := MyTextureFont;
Timeout := 5;
InsertFront(TalkTextLabel);
end;
end;

Make sure that TalkTextLabel is declared as TalkTextLabel: TDisappearingLabel.

However, note that procedure TalkLabel; is not a procedure of some TUiContainer (i.e. it doesn’t know about the UI) - and therefore the next error you will get is InsertFront not found.

To insert UI elements you need to insert them somewhere. This may be either TSomeUiState.InsertFront (this is the recommended approach) or TCastleWindowBase.Controls.InsertFront. I.e. your procedure must be aware of “where to insert the new label”.

As you are adding a lot of stuff by-hand you’ll get a lot of help here from Castle Editor where you can properly insert the label and fine-tune its parameters in WYSIWYG way. Yes, defining a TDisappearingLabel is a bit more complicated in this case, see: Editor | Manual | Castle Game Engine . You can look at a custom label properly defined for use with Castle Editor here : code/typinglabel.pas · master · EugeneLoza / Without Form and Void · GitLab just instead of TypingSpeed you will have your Timeout.

Thanks. I had forgotten to declare TalkTextLabel as TDisappearingLabel.

Okay, it works!
But… only once. I cannot get the Text shown again once it disappears after 5 seconds.
Probably because TimeOut stays 0?
How can I reset this so that I can call the procedure again?

To show the text again you should reset the timeout and show the label again (Exists property):

TalkTextLabel.Caption := 'Some new text';
TalkTextLabel.Timeout := 5;
TalkTextLabel.Exists := true; // because we just "disabled" it before

Thanks.
Almost there, but now every time I call this procedure the display time of the text / timeout is shorter?

procedure PlayerTalk;
begin
TalkTextLabel.Frame:= False;
TalkTextLabel.Text.Clear;
Window.Controls.InsertFront(TalkTextLabel);
TalkTextLabel.Timeout:= 5;
TalkTextLabel.Text.Add(‘some text’);
TalkTextLabel.Exists := true;
end;

Hmmm… I’m not 100% sure if this is the case, but as you’re using Exist you shouldn’t insert the label again by Window.Controls.InsertFront(TalkTextLabel);. In other words - you insert it multiple times into Window.Controls. I’d expect it to crash :smiley: But if it doesn’t for some reason - it will indeed call Update many times on the label, each time forcing it to disappear faster (1x, 2x, 3x, 4x - as many times as it was inserted into the controls, and as many times as Update was called on it).

Who said it did not crash?
:wink:
But how can I avoid multiple InsertFront(TalkTextLabel)?
I removed it from the procedure and moved it to initial design part of the code (right after TalkTextLabel := TDisappearingLabel.Create(Application);
but then it did not show up at all.

Hmmm… that’s probably the place where it should go. But why doesn’t it show up?

Maybe some other UI element covers it? Like background image? What exactly do you mean by “initial design part of the code” - you create some other UI there, right?

Or as the last resort - it’s not a good and clean code but it should do the job:

procedure PlayerTalk;
begin
  if TalkTextLabel = nil then
  begin
    TalkTextLabel := TDisappearingLabel.Create(Application);
    Window.Controls.InsertFront(TalkTextLabel);
  end;
  TalkTextLabel.Frame:= False;
  TalkTextLabel.Text.Clear;
  TalkTextLabel.Timeout:= 5;
  TalkTextLabel.Text.Add(‘some text’);
  TalkTextLabel.Exists := true;
end;

and obviously remove TalkTextLabel := TDisappearingLabel.Create(Application); from “initial design part of the code” - this way the label will be created when PlayerTalk is called for the first time.

Just a quick addition to what @eugeneloza writes: Indeed you cannot insert the same instance of TCastleUserInterface (like TCastleLabel) multiple times to an UI hierarchy. It may not crash, but you will definitely see a warning like

U: Inserting to the UI list (InsertFront, InsertBack) an item that is already a part of other UI list: ..... The result is undefined, you cannot insert the same TCastleUserInterface instance multiple times.

(the warning will be in log, Logging | Manual | Castle Game Engine )

Indeed, the most probable result is that time will “seem” to fly faster, as the control will have Update virtual method called many times.

So you should either toggle the Exists property (and do not add the same instance many times), or you should create a new instance of TDisappearingLabel each time.

Thanks!
So here is the final procedure.
And I changed TimeOut time depending on the length of the text, so longer testlines are displayed longer as they take more time to read, obvious.
:wink:

procedure PlayerTalk;
begin
if TalkTextLabel = nil then
begin
TalkTextLabel := TDisappearingLabel.Create(Application);
Window.Controls.InsertFront(TalkTextLabel);
end;
with TalkTextLabel do
begin
Color := White;
Font.OutlineColor := Black;
FontSize := 18;
MaxWidth:= 500;
CustomFont := MyTextureFont;
Frame:= false;
Text.Clear;
TimeOut := Length(NPC[Location.NPC.NR].Appearance.Description);
Timeout := TimeOut/15;
Text.Add(NPC[Location.NPC.NR].Appearance.Description);
Exists := true;
end;
end;

1 Like