Use engine inside Lazarus form using TCastleControl

1. Introduction

TCastleControl is a component you can drop on a Lazarus (LCL) form. It allows to do all CGE rendering and processing as part of a form, and TCastleControl can be surrounded on your form by any other components.

TCastleControl designed in Lazarus 3D model viewer using TCastleControl

Note
This feature is right now available only for Lazarus (LCL) users. We plan to add it for Delphi, maybe both VCL and FMX, soon.

2. Comparison between TCastleWindow and TCastleControl

Most of this manual describes the process of using the engine with the TCastleWindow instead of TCastleControl. All new projects created from editor templates use TCastleWindow. We generally advise TCastleWindow, as:

  • TCastleWindow works on all CGE platforms (desktop, mobile - Android, iOS, consoles - Nintendo Switch, upcoming web target). It allows us to have true cross-platform projects, that can be recompiled to any supported platform out-of-the-box.

  • TCastleWindow allows us to handle events and message loop on the CGE side, and make it efficient. For example, it makes mouse look work perfectly smooth.

On the other hand, using the TCastleControl has one big benefit: as you place the control inside a form, you can surround it with all the standard LCL GUI controls. So you can use numerous LCL GUI controls, with native look on all desktop systems, together with Castle Game Engine.

We are committed to supporting both approaches (TCastleControl and TCastleWindow) in the foreseeable future.

2.1. It is not allowed to use both TCastleWindow and TCastleControl in a single application

You have to make a choice:

You cannot mix these approaches, as neither LCL (in which our TCastleControl works) nor TCastleWindow are prepared to handle the situation that they only handle some application forms, and other library handles the other forms.

That is also why we have separate packages

You should not use both of these packages at the same time.

Note that, if you use TCastleControl, it is OK to have multiple TCastleControl instances visible at the same time. Similarly, if you use TCastleWindow, you can have several TCastleWindow instances visible at the same time (but only on desktop platforms), see examples/window/multi_window.

3. Making TCastleControl available on Lazarus component palette

  1. Install in Lazarus the package castle_components.lpk. In the package dialog, use the option to "Install" (under the "Use" button).

    Note: Installing the castle_components package will also automatically install the package castle_base, as a dependency. That’s good.

    castle_components: Choose the file castle_components: Install castle_components: Confirm Lazarus rebuild

    Once castle_components.lpk is successfully installed, Lazarus restarts, and you should see the "Castle" tab with our components.

  2. Create a normal new LCL project (using Lazarus "New Project" menu item). Choose "Application".

  3. Pick TCastleControl from the component palette (tab "Castle") and drop it on a regular Lazarus form.

  4. Done. Press "Run" :)

See the engine examples in examples/lazarus/ subdirectory for various demos of this approach.

4. Opening LCL project in CGE editor

You can place a simple CastleEngineManifest.xml within your project, like this:

<?xml version="1.0" encoding="utf-8"?>
<project name="my_project"
  lazarus_project="my_project.lpi"
  build_using_lazbuild="true"
>
</project>

This way you can open this project in the CGE editor, and compile/run it. The line build_using_lazbuild="true" means that CGE editor and CGE build tool will invoke lazbuild under the hood.

5. Initializing the resources

Essentially, you can initialize your game resources at any time. Even before any TCastleControl exists. For example in the OnCreate event of a main form.

Initializing resources inside the TCastleControl.OnOpen is also a good choice, in typical cases. Since OpenGL context is available at this point, the engine will be able to initialize also GPU resources for things you instantiate at this point.

But note that TCastleControl.OnOpen runs each time a form with TCastleControl is opened. Depending on your LCL application organization, if you allow to open this form many times — then TCastleControl.OnOpen will happen many times. In this case, to execute something really once, just folow the usual LCL approaches (e.g. initialize from a main form, or even from the main program).

6. Loading user interface and state

In the simple case, you can load a design (file xxx.castle-user-interface) using TCastleControl.DesignUrl, and access loaded components using TCastleControl.DesignedComponent.

Using TCastleControl.DesignUrl to load design in TCastleControl, visible at Lazarus design-time

In more involved cases, you should use multiple states within your control. In this case, use TUIState within the TCastleControl, and load a design (file xxx.castle-user-interface) inside the state using TUIState.DesignUrl. This is analogous to our recommended workflow with TCastleWindow. You can add new state using CGE editor, or just define a new unit like this:

unit GameStateMain;

interface

uses CastleUIState;

type
  TStateMain = class(TUIState)
  private
    { Components designed using CGE editor, loaded from gamestatemain.castle-user-interface. }
    // ...
  public
    constructor Create(AOwner: TComponent); override;
    procedure Start; override;
  end;

var
  StateMain: TStateMain;

implementation

constructor TStateMain.Create(AOwner: TComponent);
begin
  inherited;
  DesignUrl := 'castle-data:/gamestatemain.castle-user-interface';
end;

procedure TStateMain.Start;
begin
  inherited;
end;

end.

That’s enough to load gamestatemain.castle-user-interface design from your data subdirectory.

To initialize this state in your application, you can use this code in TCastleControl.OnOpen :

TCastleControl.MainControl := MyCastleControl;

StateMain := TStateMain.Create(Application);
TUIState.Current := StateMain;
Note
We have an improvement to this approach coming, in branch uistate-to-view. Setting the singleton TCastleControl.MainControl will not be necessary soon. The example how it will work is in examples/lazarus/model_3d_with_2d_controls in that branch. It is not yet merged to master branch.

7. Focus (receiving key input)

Note
This section is important if your TCastleControl is placed on a form together with other LCL controls that may receive focus (that is, key input), like buttons (TButton) or edit boxes (TEdit).

Like every LCL control, our TCastleControl receives the keys only when it has focus. The control does not capture all the keys pressed over the form (this would be bad, as other controls, maybe even other TCastleControl on the same form, may want to handle them). To make sure that controlling the camera by keys (like AWSD or arrows) works, make sure that your control has a focus.

  • You can call MyCastleControl.SetFocus; at any time to explicitly make the MyCastleControl focused. You can assign this to some menu item, or key shortcut handled by form, to allow user to easily switch focus to the TCastleControl instance.

  • You can also use TCastleControl.AutoFocus to make the control automatically focused when user presses mouse over it.


To improve this documentation just edit the source of this page in AsciiDoctor (simple wiki-like syntax) and create a pull request to Castle Game Engine WWW (cge-www) repository.