Class TUIState
Unit
Declaration
type TUIState = class(TCastleUserInterface)
Description
UI state, to manage the state of your game UI. See also https://castle-engine.io/states for an overview of using TUIState
.
In simple cases, only one state is current at a given time, and it can be get or set using the TUIState.Current property. In more complex cases, you can use TUIState.Push and TUIState.Pop to build a stack of states, and in effect multiple states are active at the same time. All of the states on stack are started, but only the top-most is resumed.
Each state has Start and Stop methods that you can override to perform work when state becomes part of the current state stack, or stops being part of it. You can also override Resume and Pause methods, to perform work when the state becomes the top-most state or is no longer the top-most state. The distinction becomes important once you play around with pushing/popping states.
To define state user interface:
It is simplest to set DesignUrl to the design file you created using CGE editor. Such user interface controls will be created right before Start and destroyed right after Stop (so the state UI always "resets" when state starts).
You can always create more UI controls and add them to the state at any point. The state is a TCastleUserInterface descendant and you can add UI to it just by using TCastleUserInterface.InsertFront.
UI children can be added anytime you want, e.g. in Start or in overridden constructor.
UI children can be removed or destroyed anytime you want as well. You can use FreeAtStop as an owner for anything you want to be automatically destroyed at Stop.
Current state is placed on the list of container controls. This way state is notified about UI events, and can react to them. Note that our children will handle events before the state itself is notified about them, following TCastleUserInterface events behavior. This way state can:
See the TCastleUserInterface class for a lot of useful methods that you can override in your state descendants to capture various events.
Hierarchy
- TObject
- TPersistent
- TComponent
- TCastleComponent
- TCastleUserInterface
- TUIState
Overview
Fields
class var Log: boolean; |
Methods
function ContainerWidth: Cardinal; override; |
|
function ContainerHeight: Cardinal; override; |
|
function ContainerRect: TRectangle; override; |
|
function ContainerSizeKnown: boolean; override; |
|
function StateContainer: TCastleContainer; virtual; |
|
function InsertAtPosition: Integer; virtual; |
|
function FreeAtStop: TComponent; |
|
class procedure Push(const NewState: TUIState); |
|
class procedure Pop; overload; |
|
class procedure Pop(const CurrentTopMostState: TUIState); overload; |
|
class function StateStackCount: Integer; |
|
constructor Create(AOwner: TComponent); override; |
|
destructor Destroy; override; |
|
constructor CreateUntilStopped; |
|
procedure Start; virtual; |
|
procedure Stop; virtual; |
|
procedure Resume; virtual; |
|
procedure Pause; virtual; |
|
procedure Finish; virtual; deprecated 'use Stop'; |
|
function Active: boolean; |
|
function Press(const Event: TInputPressRelease): boolean; override; |
|
function Release(const Event: TInputPressRelease): boolean; override; |
|
function Motion(const Event: TInputMotion): boolean; override; |
|
procedure Update(const SecondsPassed: Single; var HandleInput: boolean); override; |
|
procedure Render; override; |
|
function UIScale: Single; override; |
|
procedure InsertUserInterface(const ADesignUrl: String; const FinalOwner: TComponent; out Ui: TCastleUserInterface; out UiOwner: TComponent); overload; deprecated 'instead of this, set DesignUrl in constructor'; |
|
procedure InsertUserInterface(const ADesignUrl: String; const FinalOwner: TComponent; out UiOwner: TComponent); overload; deprecated 'instead of this, set DesignUrl in constructor'; |
|
procedure WaitForRenderAndCall(const Event: TNotifyEvent); |
|
function DesignedComponent(const ComponentName: String): TComponent; |
Properties
class property Current: TUIState read GetCurrent write SetCurrent; |
|
class property CurrentTop: TUIState read GetCurrentTop; |
|
class property StateStack [constIndex:Integer]: TUIState read GetStateStack; |
|
property InterceptInput: boolean read FInterceptInput write FInterceptInput
default false; |
|
property DesignUrl: String read FDesignUrl write SetDesignUrl; |
|
property DesignPreload: Boolean read FDesignPreload write SetDesignPreload default false; |
|
property FullSize default true; |
Description
Fields
class var Log: boolean; |
|
When |
Methods
function ContainerWidth: Cardinal; override; |
|
As the state knows about the container it will be put in (StateContainer), the state size is always known. It is known regardless if we are between Start / Stop, regardless if the state is already added to some Container.Items. This makes all other routines, like ParentRect, EffectiveRect, EffectiveWidth, EffectiveHeight also work. |
function ContainerHeight: Cardinal; override; |
|
function ContainerRect: TRectangle; override; |
|
function ContainerSizeKnown: boolean; override; |
|
function StateContainer: TCastleContainer; virtual; |
|
Container on which state works. By default, this is Application.MainWindow if you use CastleWindow or TCastleControl.MainControl if you use CastleControl. When the state is current, then Container property (from ancestor, see TCastleUserInterface.Container) is equal to this. |
function FreeAtStop: TComponent; |
|
Assign this component as owner for your controls, to make them freed during nearest Stop. |
class procedure Push(const NewState: TUIState); |
|
Pushing the state adds it at the top of the state stack, this makes new state to be displayed on top of previous ones. The state known as Current is conceptually at the bottom of state stack, always. When it is nil, then pushing new state sets the Current state. Otherwise Current state is left as-it-is, new state is added on top. |
class procedure Pop; overload; |
|
Pop the current top-most state, reversing the Push operation. |
class procedure Pop(const CurrentTopMostState: TUIState); overload; |
|
Pop the current top-most state, reversing the Push operation, also checking whether the current top-most state is as expected. Makes a warning, and does nothing, if the current top-most state is different than indicated. This is usually a safer (more chance to easily catch bugs) version of Pop than the parameter-less version. |
class function StateStackCount: Integer; |
|
Count of states in the state stack. State stack is managed using Start / Push / Pop. |
constructor Create(AOwner: TComponent); override; |
|
Create an instance of the state. You willl typically create one instance of each state class (like TStateMain, TStatePlay) at the application initialization (e.g. in Application.OnInitialize callback), like StateMainMenu := TStateMainMenu.Create(Application); StatePlay := TStatePlay.Create(Application);
Later you switch between states using Current or Push or Pop, like this: TUIState.Current := StateMain;
See https://castle-engine.io/states and numerous engine examples. |
destructor Destroy; override; |
|
constructor CreateUntilStopped; |
|
Create the instance TUIState that will be automatically freed when the state is stopped. This allows alternative usage of states (as opposed to the ones described in Create docs), where you create short-lived instances of state classes. Use it like this: TUIState.Current := TStateMain.CreateUntilStopped;
The advantages:
The disadvantage is that you cannot store in state fields anything that should "survive" the state Stop. You can instead use "class variables" in state class, or any global variable. |
procedure Start; virtual; |
|
Executed when state becomes active, it's now part of the state stack. Started state is part of the StateStack, and will soon become running (top-most on the stack). When the state is set to be current, by
|
procedure Stop; virtual; |
|
Executed when state is no longer active, no longer part of state stack. When the state stops becoming active, this happens:
This is always called to finalize the started state. When the state is destroyed, it's Pause and Stop are called too, so you can use this method to reliably finalize whatever you initialized in Start. |
procedure Resume; virtual; |
|
Executed when state is now the top-most state. See Start and Stop docs about state lifecycle methods. This is called after Start, it is also called when you pop another state, making this state the top-most. |
procedure Pause; virtual; |
|
Executed when state is no longer the top-most state. See Start and Stop docs about state lifecycle methods. This is called before Stop, it is also called when another state is pushed over this state, so this stops being the the top-most state. |
procedure Finish; virtual; deprecated 'use Stop'; |
|
Warning: this symbol is deprecated: use Stop |
function Active: boolean; |
|
State is right now part of the state stack, which means it's between Start and Stop calls. The state is added to the stack before the Start call, and removed after the Stop call, so this returns |
function Press(const Event: TInputPressRelease): boolean; override; |
|
function Release(const Event: TInputPressRelease): boolean; override; |
|
function Motion(const Event: TInputMotion): boolean; override; |
|
procedure Update(const SecondsPassed: Single; var HandleInput: boolean); override; |
|
procedure Render; override; |
|
function UIScale: Single; override; |
|
procedure InsertUserInterface(const ADesignUrl: String; const FinalOwner: TComponent; out Ui: TCastleUserInterface; out UiOwner: TComponent); overload; deprecated 'instead of this, set DesignUrl in constructor'; |
|
Warning: this symbol is deprecated: instead of this, set DesignUrl in constructor Load and show a user interface from a .castle-user-interface file, designed in Castle Game Engine Editor. This is an utility method, loading a UI in a typical way into the TUIState. It is not the only way to load a .castle-user-interface file, a more general approach is to use UserInterfaceLoad from CastleComponentSerialize unit, and manually call InsertFront to add it to the state UI. The loaded UI is returned under the It is owned by ButtonQuit := UiOwner.FindRequiredComponent('ButtonQuit') as TCastleButton;
The UiOwner, in turn, is owned by the FinalOwner. You typically use this method inside overridden Start, and as FinalOwner you pass FreeAtStop – this way the user interface is freed when the state is stopped. |
procedure WaitForRenderAndCall(const Event: TNotifyEvent); |
|
Wait until the render event happens (to redraw current state), and then call Event. The scheduled Event will be called at the Update time. If the state stopped before the scheduled events fired, then the remaining events are ignored. That is, the scheduled events are cleared every time you start the state. One use-case of this is to show a loading state, where you load things, but also update the loading progress from time to time. Be careful in this case to not call this too often, as then your loading time will be tied to rendering time. For example, when the monitor refresh rate is 60, and the "vertical sync" is "on", then the render events happen at most 60 times per second. So if you call |
function DesignedComponent(const ComponentName: String): TComponent; |
|
When the DesignUrl is set, and the state is started, you can use this method to find loaded components. Like this: MyButton := DesignedComponent('MyButton') as TCastleButton;
See DesignUrl for full usage example. See also
|
Properties
class property Current: TUIState read GetCurrent write SetCurrent; |
|
Current state. Simply assign to this property to change the current state. In case multiple states are active (only possible if you used the Push method), this property returns the bottom state (use CurrentTop to get top state). Setting this property resets whole state stack. When is it allowed to change the state? While in theory you can change current state stack (assigning TUIState.Current or using TUIState.Push / TUIState.Pop) at any moment, but remember that stopping the state frees also the state UI. So you should not change the current state stack within events/overriden methods of classes like TCastleUserInterface, TCastleTransform, TCastleBehavior that could be destroyed by the state stop. The simpler advise is: Assign to TUIState.Current or use TUIState.Push / TUIState.Pop only from the overridden TUIState methods. Like TMyState.Update or TMyState.Press. Note that you cannot change current state stack when another change is in progress. That is, you cannot change state from within TMyState.Start/Resume/Pause/Stop. |
class property CurrentTop: TUIState read GetCurrentTop; |
|
The top-most state. In case you used Push, this returns the top-most (most recently pushed) state. If there is only one (or none) state, e.g. because you never used Push, then this property returns the same thing as Current. |
class property StateStack [constIndex:Integer]: TUIState read GetStateStack; |
|
Access any state within the state stack. Use with indexes between 0 and StateStackCount - 1. State stack is managed using Start / Push / Pop. |
property InterceptInput: boolean read FInterceptInput write FInterceptInput
default false; |
|
Prevents passing mouse/keyboard events to the states underneath. More precisely, when this property is Note that setting this to function TMyState.Press(const Event: TInputPressRelease): Boolean; begin Result := inherited; // ignore the ancestor result, as we use InterceptInput, so ancestor always returns true // if Result the Exit; if Event.IsMouseButton(buttonLeft) then begin ... Exit(true); end; end; |
property DesignUrl: String read FDesignUrl write SetDesignUrl; |
|
Load a designed user interface (from .castle-user-interface file) when this state is started. You typically set this property in overridden constructor, and in effect the given design file will be loaded right before Start and it will be freed right after Stop. Typical use-case looks like this: constructor TStatePlay.Create(AOwner: TComponent); begin inherited; DesignUrl := 'castle-data:/gamestateplay.castle-user-interface'; // DesignPreload := true; // to make future "TUIState.Current := StatePlay" faster end; procedure TStatePlay.Start; begin inherited; MyButton := DesignedComponent('MyButton') as TCastleButton; end;
You can also modify this property when the state has already started (after Start and before Stop) in which case the previous design will be freed and new one will be loaded immediately. Set this to empty string (default) to mean "no design should be loaded". Note that this is not the only way to load a .castle-user-interface file. A more general approach is to use UserInterfaceLoad, and call InsertFront to add it to the state UI. Using this property just adds some comfortable automatic behavior (state is freed at stop, and you can use comfortable DesignedComponent or DesignPreload).
See also
|
property DesignPreload: Boolean read FDesignPreload write SetDesignPreload default false; |
|
Preload the design file (specified in DesignUrl) as soon as possible, making starting the state much faster. Using this property means that we "preload" the design file, to cache the referenced images / TCastleScene instances etc. as soon as possible, to make the future loading of this design (when state starts) very quick. Typically you set this property in overridden TUIState constructor, right after (or before, it doesn't matter) setting DesignUrl. It will mean that constructor takes a longer time (which typically means that Application.OnInitialize takes a longer time) but in exchange future starting of the state (when you do e.g. No functional difference should be visible, regardless of the DesignPreload value. Internally the designed component is added/removed from state at the same time. So think of this property as pure optimization – you decide whether to slow down the state constructor, or state start. See also
|
property FullSize default true; |
|
TUIState control makes most sense when it is This way it always captures events on the whole container. And the child controls (anchored to this) behave like anchored to the whole container. |
Generated by PasDoc 0.16.0.