Your First Game

A complete, working game built on the engine framework. Three files: a game unit, a screen unit, and the program itself.

The framework (TBaseGame) owns the game loop, so none of this touches VGA setup, timers or interrupt teardown directly.

1. Describe your resources

Assets load by name from an XML file. Put this in DATA\RES.XML:

<?xml version="1.0" encoding="US-ASCII"?>
<resources>
  <image name="player" path="PLAYER.PCX" palette="default" />
  <font name="small" path="SMALL.XML" />
  <music name="theme" path="THEME.HSC" />
</resources>

See the Resource Manager for the full format, and Creating Assets for how to produce the files.

2. The game unit

This holds everything the whole game needs. Screens reach it through the global Game.

unit MyGame;

interface

uses
  VGA, VGAFont, BaseGame, Screen, Config;

type
  TMyGame = object(TBaseGame)
    PlayerImage: PImage;
    Font: PFont;
    Palette: PPalette;

    constructor Init(AConfig: PConfig; const ResXmlPath: String);
    procedure Start; virtual;
  end;

var
  Game: TMyGame;
  GameConfig: TConfig;

implementation

constructor TMyGame.Init(AConfig: PConfig; const ResXmlPath: String);
begin
  inherited Init(AConfig, ResXmlPath);

  { Resources are not available yet - only plain state here }
  PlayerImage := nil;
  Font := nil;
  Palette := nil;
end;

procedure TMyGame.Start;
begin
  inherited Start;  { Brings up resources, input, sound, timing }

  { Now the resource manager is live }
  PlayerImage := ResMan.GetImage('player');
  Font := ResMan.GetFont('small');
  Palette := ResMan.GetPalette('default');
end;

end.

3. The game screen

A screen is one state of the game. This one moves a sprite with the arrow keys.

unit GameScr;

interface

uses
  VGA, VGAFont, Screen, Keyboard, MyGame;

type
  PGameScreen = ^TGameScreen;
  TGameScreen = object(TScreen)
    X, Y: Real;
    constructor Init;
    procedure PostInit; virtual;
    procedure Show; virtual;
    procedure Update(DeltaTime: Real); virtual;
  end;

implementation

const
  Speed = 60.0;  { Pixels per second }

constructor TGameScreen.Init;
begin
  inherited Init;
  X := 160.0;
  Y := 100.0;
end;

procedure TGameScreen.PostInit;
begin
  inherited PostInit;

  { VGA is up by now - safe to set the palette }
  SetPalette(Game.Palette^);
end;

procedure TGameScreen.Show;
begin
  { Build the static background once }
  ClearFrameBuffer(Game.BackgroundBuffer);
  PrintFontText(4, 4, 'ARROWS TO MOVE, ESC TO QUIT',
                Align_Left, Game.Font^, Game.BackgroundBuffer);

  Game.PlayMusic('theme');
end;

procedure TGameScreen.Update(DeltaTime: Real);
begin
  { Frame-rate independent movement }
  if IsKeyDown(Key_Left)  then X := X - Speed * DeltaTime;
  if IsKeyDown(Key_Right) then X := X + Speed * DeltaTime;
  if IsKeyDown(Key_Up)    then Y := Y - Speed * DeltaTime;
  if IsKeyDown(Key_Down)  then Y := Y + Speed * DeltaTime;

  if IsKeyPressed(Key_Escape) then
    Game.Stop;

  { Restore the background, draw the player, show the frame }
  CopyFrameBuffer(Game.BackgroundBuffer, Game.BackBuffer);
  PutImage(Game.PlayerImage^, Round(X), Round(Y), True, Game.BackBuffer);
  RenderFrameBuffer(Game.BackBuffer);
end;

end.

Note what is not here: no delta-time arithmetic, no VSync wait, no ClearKeyPressed at the end of the loop. The framework does all of that around your Update.

4. The program

program First;

uses
  MyGame, GameScr;

var
  GameScreen: PGameScreen;

begin
  GameConfig.Init('CONFIG.INI');

  Game.Init(@GameConfig, 'DATA\RES.XML');
  Game.Start;

  New(GameScreen, Init);
  Game.AddScreen('game', GameScreen);

  Game.SetNextScreen('game');
  Game.Run;

  Game.Done;
  GameConfig.Done;
end.

Compile it the way Building describes, and you have a running game with music, input, double buffering and clean shutdown — including on Ctrl+C.

Adding a second screen

Screens are how you get a title screen, a menu or a pause state. Register as many as you like and switch by name:

New(TitleScreen, Init);
Game.AddScreen('title', TitleScreen);

New(GameScreen, Init);
Game.AddScreen('game', GameScreen);

Game.SetNextScreen('title');
Game.Run;

From inside any screen’s Update:

if IsKeyPressed(Key_Enter) then
  Game.SetNextScreen('game');

The switch is deferred to the start of the next frame, so it is safe to call mid-update. The old screen’s Hide and the new screen’s Show run automatically.

Where to go next

If you need to drop below the framework — writing a demo, or driving VGA directly — every subsystem has its own page under Graphics & Input and Audio, and they all work standalone.