1. Intro

A practical overview of using Castle Game Engine to create 3D and 2D games, and building them for various platforms. The talk is directed at both FPC/Lazarus and Delphi users.

Lost? Contact me:

cge course screen

2. Monday: Overview - objects, classes, designing 3D levels and 2D UI

What we have covered:

  • Overview of CGE editor,

  • Overview of initial skeleton - code, views, data, ui

  • Views: Example: main menu, play, options - options on top of play, paused or not

  • How to react to event - button click

  • Generate log from code

  • Change objects from code, change any property of published: Add box, change box Translation from code

Basic reaction to button press:

procedure TViewPlay.Start;
begin
  ....

  MyButton.OnClick := {$ifdef FPC}@{$endif} MyButtonClick;
end;
procedure TViewPlay.MyButtonClick(Sender: TObject);
begin
  WritelnLog('Button clicked');

  // translation is 3D vector, TVector3

  Box1.Translation := Box1.Translation + Vector3(0, 1 , 0);
end;

Homework if you want:

3. Tuesday: Designing a level, playing with physics

  • Using Castle Game Engine to design 3D environment from ready pieces

  • Add rigid body physics for environment interactions

Homework if you want:

4. Wednesday: Coding, behaviors

What we have covered:

Shooting bullets:

function TViewSomething.Press(const Event: TInputPressRelease): Boolean;
var
  SphereWithStuff: TCastleTransform;
  Pos, Dir, Up: TVector3;
  Body: TCastleRigidBody;
begin
  Result := inherited;
  if Result then Exit;

  if Event.IsKey(keyEnter) then
  begin
    { Executed when user presses space key. }
    WritelnLog('Space key pressed');

    //Sphere := TCastleSphere.Create(Self);
    //Sphere.Radius := 0.1;

    SphereWithStuff := TransformLoad('castle-data:/bullet.castle-transform', Self);

    MichalisViewport.Items.Add(SphereWithStuff);

    MichalisViewport.Camera.GetWorldView(Pos, Dir, Up);

    SphereWithStuff.Translation := Pos + Dir * 10.0;

    Body := SphereWithStuff.FindBehavior(TCastleRigidBody) as TCastleRigidBody;
    Body.LinearVelocity := Dir * 100.0;
    Body.OnCollisionEnter := @CollisionEnter;

    Result := true;
  end;
end;

Make enemies disappear when bullet hits them:

procedure TViewSomething.CollisionEnter(const CollisionDetails: TPhysicsCollisionDetails);
begin
  { TODO: put enemies in a list (e.g. TCastleTransformList)
    to make this code less error-prone. }
  if CollisionDetails.OtherTransform = DesignMouseWithHat1 then
    DesignMouseWithHat1.Exists := false;
  if CollisionDetails.OtherTransform = DesignMouseWithHat2 then
    DesignMouseWithHat2.Exists := false;
  if CollisionDetails.OtherTransform = DesignMouseWithHat3 then
    DesignMouseWithHat3.Exists := false;
end;

Homework if you want:

  • Learn about CGE behaviors, https://castle-engine.io/behaviors , to attach information (like life points) and actions to enemies, and to detect enemies better.

  • See also "3D FPS Game" template for example of using behaviors with enemies.

  • Rework above code to use 1 behavior for each enemy.

5. Thursday: Sound, savegames, using GitHub actions

  • Using spatial and non-spatial sound effects

    • music - OggVorbis (.ogg) or wav

    • sound - emitted by some object in 3d

    • sound - emitted by some object in 3d, from time to time, when user pressed key "keyP"

      if Event.IsKey(keyP) then
      begin
        SoundSourceManual.Play(SoundAlien);
      end;
  • Saving persistent data (savegames)

    procedure TViewSomething.SaveGame(Sender: TObject);
    var
      Pos, Dir, Up: TVector3;
    begin
      MichalisViewport.Camera.GetWorldView(Pos, Dir, Up);
    
      UserConfig.SetVector3('player_position', Pos);
      UserConfig.SetVector3('player_direction', Dir);
      UserConfig.SetVector3('player_up', Up);
      UserConfig.SetValue('player_saved', true);
    
      UserConfig.Save;
    end;
    
    procedure TViewSomething.LoadGame(Sender: TObject);
    var
      Pos, Dir, Up: TVector3;
    begin
      UserConfig.Load;
    
      if UserConfig.GetValue('player_saved', false) then
      begin
        Pos := UserConfig.GetVector3('player_position', Vector3(0, 0, 0));
        Dir := UserConfig.GetVector3('player_direction', Vector3(0, 0, -1));
        Up := UserConfig.GetVector3('player_up', Vector3(0, 1, 0));
    
        MichalisViewport.Camera.SetWorldView(Pos, Dir, Up);
      end;
    end;
  • Using GitHub Actions ( https://castle-engine.io/github_actions )

Homework if you want:

  • Deploying the game to Android, integration with Google Play Games

  • Pickup items - inventory UI if you want, at least log

  • Shoot instant (see "3D FPS Game template")