On-screen menu

On-screen menu in "The Castle" - main menu
On-screen menu in "The Castle" - configure controls
On-screen menu in "The Castle" - pause menu
Lights editor in "view3dscene" - also an on-screen menu
Terrain parameters (from engine "terrain" demo) are also an on-screen menu

The TCastleOnScreenMenu is a user interface control (TUIControl descendant) displaying an on-screen menu. All the menu items are displayed vertically on the screen. You can click on menu items, or choose them using the keyboard. Each menu item is a full-featured UI control. In the common case, a menu item is an instance of TCastleMenuButton, which descends from TCastleButton so you have available the event OnClick. In general, menu item is any UI control.

Menu items may also have attached an "accessory" which is often used as an extra label (like a "Yes" / "No" state of some configuration option), a slider (e.g. to control sound volume or texture quality). In general, an "accessory" is just a child UI control of the menu-item, and can be any UI control (TUIControl). So you can really insert any convoluted things inside the on-screen menu:)

You create and insert the on-screen menu instance just like all other UI controls.

Once created, you should add menu items using the TCastleOnScreenMenu.Add method. It has a couple of overloaded versions. In the simplest case, you can use the Add(string) or Add(string, TNotifyEvent) methods, that add a simplest menu item, and optionally register your callback to handle it's click event.

A simplest example program:

uses CastleWindow, CastleUIControls, CastleOnScreenMenu;
 
var
  Window: TCastleWindow;
  OnScreenMenu1: TCastleOnScreenMenu;
 
type
  TEventHandler = class
    class procedure NewGameClick(Sender: TObject);
    class procedure LoadGameClick(Sender: TObject);
  end;
 
class procedure TEventHandler.NewGameClick(Sender: TObject);
begin
  // ... new game
end;
 
class procedure TEventHandler.LoadGameClick(Sender: TObject);
begin
  // ... load game from disk
end;
 
begin
  Window := TCastleWindow.Create(Application);
 
  OnScreenMenu1 := TCastleOnScreenMenu.Create(Application);
  OnScreenMenu1.Add('New game', @TEventHandler(nil).NewGameClick);
  OnScreenMenu1.Add('Load game', @TEventHandler(nil).LoadGameClick);
  OnScreenMenu1.Anchor(hpMiddle);
  OnScreenMenu1.Anchor(vpMiddle);
  Window.Controls.InsertFront(OnScreenMenu1);
 
  Window.OpenAndRun;
end.

There are examples of using this class in

  • examples/lazarus/model_3d_with_2d_controls/
  • examples/2d_standard_ui/
  • examples/terrain/
  • Inside The Castle game.

On-screen menu over a 3D world

You can use various UI controls on top of each other. So you can have TCastleOnScreenMenu displayed on top of a 3D world (by default, scene manager already acts as a viewport). You can control the existence of any UI control either by removing/adding it from the Controls list, or by changing it's Exists property.

If the game is already started, in single player games, you usually want to pause the game when the on-screen menu is displayed. You can do this easily by SceneManager.Paused property. Like this:

...
{ global / static variables }
var
  GameMenu: TCastleOnScreeMenu;
  GameMenuClosed: boolean;
 
... // use this at initialization:
  { somewhere at the beginning prepare the menu }
  GameMenu := TCastleOnScreeMenu.Create(...);
  { see example above for how to initialize and implement TCastleOnScreeMenu.
    Make sure that one of the menu items, like "Back",
    sets GameMenuClosed := true when clicked. }
 
... // use this when you want to actually show menu:
  SceneManager.Paused := true;
  GameMenuClosed := false;
  Window.Controls.Add(GameMenu);
  repeat
    Application.ProcessMessage(true, true);
  until GameMenuClosed;
  Window.Controls.Remove(GameMenu);
  SceneManager.Paused := false;

As the scene manager handles a lot of stuff automatically, processing events and calling Update methods of all 3D objects periodically, pausing it effectively pauses your whole 3D world, while still allowing it to be displayed as a background under the on-screen menu. Alternatively you could also hide the 3D world entirely, by changing SceneManager.Exists property to false — the SceneManager with Exists=false is not only paused, it's also invisible.

Background under on-screen menu

In addition to previous ideas, you may want to change the menu TCastleOnScreenMenu.FullSize to true. Otherwise, menu receives input only when mouse hovers over it. When FullSize = true, the menu obscures completely controls under it as far as key processing is concerned (although controls behind are still visible as a background).

  • So if you want your menu to be displayed and used orthogonally to the "live" 3D world underneath, leave FullSize = false.

  • For initial game menu with items like "New Game", you probably want to disable the camera input for this scene. This allows you to display interactive 3D scene in the background, but block user from interacting with it (after all, the user should only interact with your menu on the initial screen). To do this simply set FullSize = true.

If you want to place a static 2D image under menu, you can use TCastleImageControl underneath, instead of a 3D scene.