Creating a First-person Shooter 3D Game with the Castle Game Engine

These instructions are based on part of the PDF of the slide show presented by Michalis Kamburelis.

  1. Get the engine and install the package castle_components in Lazarus.
  2. Start a new form-based application in Lazarus and drop onto the form a TCastleControl from the newly created Castle tab.
  3. Extract the ZIP file and copy the data folder from the fps_game subdirectory to your application folder.
    The file bridge_final.x3dv is written by hand and inlines bridge.x3d, which was exported from Blender. (The file level.xml is already present in the data folder so you do not need to create it).
  4. Add a uses CastleLevels; clause to the implementation section:
    implementation
    
    {$R *.lfm}
    
    uses
      CastleLevels;    
    
  5. Double click in the onOpen event of CastleControl1 in the Object Inspector and add this code to the CastleControl1Open procedure:
    Levels.LoadFromFiles;
    CastleControl1.SceneManager.LoadLevel('bridge');   
    
  6. Add CastleResources to the implementation uses clause and insert the line Resources.LoadFromFiles; at the start of the CastleControl1Open procedure.
    This lets the engine see the resource.xml file (which is already present in the item_medkit folder) describing the item named MedKit.
  7. Instead of changing the Blender file to add a MedKit, you can insert this code after the Background DEF tag near the beginning of bridge.x3d.
        <Transform DEF="CasMedKit_TRANSFORM"
                   translation="-10.035152 0.0 1.422754"
                   scale="1.000000 1.000000 1.000000"
                   rotation="0.000000 0.707107 0.707107 3.141593"
                   >
          <Transform DEF="CasResMedKit_ifs_TRANSFORM"
                     translation="0.000000 0.000000 0.000000"
                     scale="1.000000 1.000000 1.000000"
                     rotation="1.000000 0.000000 0.000000 0.000000"
                     >
            <Group DEF="group_ME_Cube">
              <Shape>
                <Appearance>
                </Appearance>
                <IndexedFaceSet solid="false"
                                coordIndex="1 0 4 5 -1 5 6 2 1 -1 6 7 3 2 -1 0 3 7 4 -1 0 1 2 3 -1 7 6 5 4 -1 "
                                >
                  <Coordinate DEF="coords_ME_Cube"
                              point="-1.000000 -1.000000 -1.000000 -1.000000 1.000000 -1.000000 1.000000 1.000000
                                     -1.000000 1.000000 -1.000000 -1.000000 -1.000000 -1.000000 1.000000 -1.000000
                                      1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 -1.000000 1.000000"
                              />
                </IndexedFaceSet>
              </Shape>
            </Group>
          </Transform>
        </Transform>
    
    
    
    Alternatively, for the inclusion of two knights and many MedKits, you could move bridge_with_resources.x3d from the unused_extras folder to data and change the inline url code in bridge_final.x3dv to:
    Inline {
      url "bridge_with_resources.x3d"
    }
    
  8. Add CastlePlayer to the interface uses clause (not to the implementation uses clause) and declare in the class private section Player: TPlayer;. Insert at the beginning of the CastleControl1Open procedure the code:
    Player := TPlayer.Create(CastleControl1.SceneManager);
    CastleControl1.SceneManager.Items.Add(Player);
    CastleControl1.SceneManager.Player := Player;
        
    

    Items are pickable now; they are added to player’s inventory but the inventory is not visualized in any way. Next add a simple 2D HUD as described in the following two steps.

  9. Add CastleUIControls to the implementation uses clause. Add to the interface section:
    type
      TGame2DControls = class(TUIControl)
      public
        procedure Render; override;
    end;
    
    procedure TGame2DControls.Render;
    var
      Player : TPlayer;
      I, J, X: Integer;
    begin
      Player := Form1.Player;
      X := 0;
      for I :=0 to Player.Inventory.Count - 1 do
        for J := 0 to Player.Inventory[I].Quantity - 1 do
          begin
            Player.Inventory[I].Resource.GLImage.Draw(X, 0) ;
            X += 100;
          end;
    end;                 
    
  10. Insert in the CastleControl1Open procedure the code:
    var
      Game2DControls: TGame2DControls;
    begin
      . . . 
      Game2DControls := TGame2DControls.Create(Application);
      CastleControl1.Controls.InsertFront(Game2DControls); 
    end;  
    
  11. Add a button on a form to spawn creatures. It is best to use a TSpeedButton (from the Additional component tab) so as not to capture focus.
  12. Add to the implementation uses clause CastleVectors and CastleCreatures.
  13. Double click on the speed button and add code to the click procedure so that it looks like this:
    procedure TForm1.SpeedButton1Click(Sender: TObject);  //Note speedbutton
    var
      P: TVector3Single;
      Direction: TVector3Single;
      CreatureResource: TCreatureResource;
    begin
      P := Player.Position + Player.Direction * 10;
      Direction := Player.Direction;
      CreatureResource := Resources.FindName('Knight') as TCreatureResource;
      CreatureResource.CreateCreature(CastleControl1.SceneManager.Items, P, Direction);
    end;                 
    
  14. Add a speed button to kill a creature and add Castle3D to the implementation uses clause. The button click procedure is:
    procedure TForm1.SpeedButton2Click(Sender: TObject);
    var
      I: Integer;
      Hit: TRayCollision;
    begin
      Hit := Player.Ray(Player.Middle, Player.Direction);
      if Hit <> nil then
        begin
          for I := 0 to Hit.Count - 1 do
            if Hit[I].Item is T3DAlive then
              begin
                (Hit[I].Item as T3DAlive).Hurt(100, Player.Direction, 1, Player);
                Break;
              end;
            FreeAndNil(Hit);
         end;
    end;           
    
  15. Compile, execute and test the program.
    You should be able to pick up the MedKit. It should appear at the bottom left of the window and stop rotating. One knight looks fine on the bridge. It should die impressively when you shoot it from a suitable position.
Programming - a skill for life!

How to use and learn from the Castle Game Engine