function TViewMainMenu.Press(const Event: TInputPressRelease): Boolean;
begin
Result := inherited;
if Event.IsKey(keyEnter) then
begin
Container.View := ViewPlay;
Exit(ExclusiveEvents);
end;
end;
View is a class descending from TCastleView
. It determines what is currently displayed, and is the primary place where you can react to events (handle user input, passage of time, clicks on buttons and more).
In a larger application you will likely have more than one view. If you want to display something "completely different" (like a game, or main menu, or a "game over screen") then it’s most natural to do this by switching to a different view.
You can add new view to your application using the menu item Code → New Unit → View… in CGE editor. It just creates a new Pascal unit that defines a new TCastleView
descendant and loads a new user interface design.
At runtime, you can change from one view into another using:
You can set Container.View := ViewXxx
to make the new view the one and only currently active view.
This is the simplest way to change current view. For example use this to change from main menu, to loading, to playing game, to game over views.
Usually the implementation of one view has code to change it into another view. For example, this is how ViewMainMenu
can react to user pressing Enter to switch to ViewPlay
:
function TViewMainMenu.Press(const Event: TInputPressRelease): Boolean;
begin
Result := inherited;
if Event.IsKey(keyEnter) then
begin
Container.View := ViewPlay;
Exit(ExclusiveEvents);
end;
end;
You can alternatively use Container.PushView
to push new view on top of the stack, making it the front-most view (but not necessarily the only view active right now). Such view will usually pop itself from the stack, using Container.PopView
, although you can also set Container.View
to just change whole stack into a single new view.
Using a view stack makes sense when you want to display one view on top of another. For example, you may want to push options view to make options UI visible on top of the game. The game can even be still animated underneath (it is up to you to pause the game if you want, e.g. by changing Viewport.Items.Paused
).
For example, this is how ViewPlay
could display ViewOptions
on top, when user presses Escape:
function TViewPlay.Press(const Event: TInputPressRelease): Boolean;
begin
Result := inherited;
if Result then Exit; // allow the ancestor to handle keys
if Event.IsKey(keyEscape) and
(Container.FrontView = ViewPlay) then
begin
Container.PushView(ViewOptions);
Exit(true);
end;
end;
The ViewOptions
can in turn pop itself from the stack when user presses Escape again:
function TViewOptions.Press(const Event: TInputPressRelease): Boolean;
begin
Result := inherited;
if Result then Exit; // allow the ancestor to handle keys
if Event.IsKey(keyEscape) then
begin
// parameter Self is optional here, just allows to make additional check
Container.PopView(Self);
Exit(true);
end;
end;
While in theory you can create instances of the TCastleView
at any point, in practice it is usually most comfortable to create all of them at the beginning of the application, in Application.OnInitialize
handler.
If you use the "Code → New Unit → View…" editor menu item, it will automatically edit your Application.OnInitialize
handler in (by default) gameinitialize.pas
unit to create the new view. So it will look like this:
{ One-time initialization of resources. }
procedure ApplicationInitialize;
begin
...
{ Create game views and set initial view }
ViewPlay := TViewPlay.Create(Application);
ViewMainMenu := TViewMainMenu.Create(Application);
Window.Container.View := ViewMenu;
end;
Each view loads the user interface appropriate for the given view. The advised way to do this is to set TCastleView.DesignUrl
in the overridden view constructor, like this:
constructor TViewMain.Create(AOwner: TComponent);
begin
inherited;
DesignUrl := 'castle-data:/gameviewmain.castle-user-interface';
end;
If you use the "Code → New Unit → View…" editor menu item, the above code is also automatically created for you.
You can override a number of view methods to react to the view becoming active (when it is started) and resumed (when it is started and it is the top of view stack).
TCastleView.Start
is executed when the view starts. This is your typical place to initialize things for this view.
To create a component that lives only until the view stops, you can assign a special owner TCastleView.FreeAtStop
to this component. This is essentially equivalent to just using owner nil
and manually freeing the component in TCastleView.Stop
.
TCastleView.Stop
is executed when the view stops.
TCastleView.Resume
is executed when the view is started, and moreover it becomes the top view on the stack.
TCastleView.Pause
is executed when the view is started, but it is no longer the top view on the stack.
Note that the view is not automatically paused for the user in any way, i.e. a paused view can still animate anything, process inputs and generally act like a normal view. It is your responsibility to pause any animations you want in the TCastleView.Pause
method, if you want it.
The paused view will also continue to receive user input (mouse and key) that was not processed by the views higher on the stack. The higher view should return true
from their input methods, like Press
, to mark the input as handled. You can also set TCastleView.InterceptInput
to true
on a higher view to make it pretend that it handles all inputs, thus the inputs will not reach views lower on the stack.
Explore the "3D FPS game" and "2D game" templates, by creating 2 new projects from these templates. Each of these templates creates 2 views, "MainMenu" and "Play". They follow the same pattern:
Class TViewMainMenu
, unit code/gameviewmainmenu.pas
, instance ViewMainMenu
, design data/gameviewmainmenu.castle-user-interface
.
Class TViewPlay
, unit code/gameviewplay.pas
, instance ViewPlay
, design data/gameviewplay.castle-user-interface
.
Many examples in the engine show even more complicates views setup:
Platformer demo in examples/platformer/ has views for:
main menu,
options (with volume configuration),
pause,
credits,
game over,
and of course the actual game.
Strategy game examples/tiled/strategy_game_demo has multiple views, including:
main menu,
game,
"win" views.
"Zombie fighter" demo examples/user_interface/zombie_fighter has multiple views, including:
main menu,
loading (that displays progress),
actual game,
modal dialog box.
To improve this documentation just edit this page and create a pull request to cge-www repository.