SDL:Tutorials:Simple Engine Framework with Delphi

From GDWiki

Jump to: navigation, search

[edit] Description

This is a Pascal/Delphi port of engine provided by Anders "Sion" Nissen here. Porting by Jani Mikkonen

There are some bugs still left in the code. The delphi code uses JEDI SDL and has been tested and compiled.


[edit] IGameEngine Unit

File: IGameEngine.pas

unit IGameEngine;
 
interface
 
uses
  SDL;
 
 
type IIGameEngine = class
private
 
  m_lLastTick   : integer;          (* Last iteration's tick value *)
  m_iWidth      : integer;          (* Window width *)
  m_iHeight     : integer;          (* Window height *)
  m_bQuit       : boolean;          (* Has quit been called? *)
  m_czTitle     : string;           (* The title of the window *)
  m_pScreen     : PSDL_Surface;
  m_bMinimized  : boolean;          (* start the window minimized? *)
  m_iFPSTickCounter
                : integer;          (* TICK/FPS Counter *)
  m_iFPSCounter : integer;          (* Frame rate counter. *)
  m_iCurrentFPS : integer;          (* Stores the last calculated frame rate. *)
 
  OldExitHandler:Pointer;
 
 
public
  Constructor Create;
  Destructor DestroyIt;
 
  Procedure Init();
  Procedure Start();
 
  Procedure AdditionalInit(); virtual;
  Procedure Think  (const iElapsedTime:integer);   virtual;
  Procedure Render( pDestSurface: PSDL_Surface  ); virtual;
  Procedure EndAll(); virtual;
  Procedure WindowActive (); virtual;
  Procedure WindowInactive (); virtual;
  Procedure KeyUp    (const iKeyEnum:integer);   virtual;
  Procedure KeyDown    (const iKeyEnum:integer);   virtual;
  Procedure MouseMoved    (const  iButton:integer;
           const iX:integer;
           const iY:integer;
           const iRelX:integer;
           const iRelY:integer);   virtual;
  Procedure MouseButtonUp  (const iButton:integer;
           const iX:integer;
           const iY:integer;
           const iRelX:integer;
           const iRelY:integer); virtual;
  Procedure MouseButtonDown  (const iButton:integer;
           const iX:integer;
           const iY:integer;
           const iRelX:integer;
           const iRelY:integer); virtual;
 
  Procedure  SetTitle  (const czTitle:String);
 
 
 
  Property Title: string read m_czTitle write SetTitle;
  Property Surface:PSDL_Surface read m_pScreen;
  property FPS:integer read m_iCurrentFPS;
 
protected
  Procedure DoThink();
  Procedure DoRender();
  Procedure SetSize(const iWidth:integer; const iHeight:integer);
  Procedure HandleInput();
 
end; (* IGameEngine Class *)
 
implementation
 
 
constructor IIGameEngine.Create;
begin
  m_lLastTick    := 0;
  m_iWidth    := 800;
  m_iHeight    := 600;
  m_czTitle    := '';
 
  m_pScreen    := nil;
 
  m_iFPSTickCounter  := 0;
  m_iFPSCounter    := 0;
  m_iCurrentFPS    := 0;
 
  m_bMinimized    := false;
 
end;
 
Destructor IIGameEngine.DestroyIt;
begin
  SDL_Quit();
  ExitProc := OldExitHandler;
end;
 
procedure IIGameEngine.SetSize(const iWidth:integer; const iHeight:integer);
begin
  m_iWidth  := iWidth;
  m_iHeight := iHeight;
  m_pScreen := SDL_SetVideoMode( iWidth, iHeight, 0, SDL_SWSURFACE );
end;
 
 
Procedure IIGameEngine.Init();
begin
  // Register SDL_Quit to be called at exit; makes sure things are cleaned up when we quit.
 
  ExitProc := @SDL_Quit;
 
  // Initialize SDL's subsystems - in this case, only video.
  if ( SDL_Init( SDL_INIT_VIDEO ) < 0 ) then
  begin
    WriteLn('Unable to init SDL: '+SDL_GetError()+'\n' );
    Halt( 1 );
  end;
 
  // Attempt to create a window with the specified height and width.
  Self.SetSize( Self.m_iWidth, Self.m_iHeight );
 
  // If we fail, return error.
  if ( m_pScreen = nil ) then
  begin
    WriteLn('Unable to set up video: '+SDL_GetError()+'\n');
    Halt( 1 );
  end;
  AdditionalInit();
end;
 
 
 
procedure IIGameEngine.Start();
begin
  m_lLastTick := SDL_GetTicks();
  m_bQuit := false;
 
  // Main loop: loop forever.
  while ( not m_bQuit ) do
  begin
    // Handle mouse and keyboard input
    HandleInput();
 
    if ( m_bMinimized ) then
    begin
      // Release some system resources if the app. is minimized.
      // WaitMessage(); 
    end else
    begin
      // Do some thinking
      DoThink();
 
      // Render stuff
      DoRender();
    end;
  end;
  EndAll();
end;
 
Procedure IIGameEngine.HandleInput();
var
  event : TSDL_Event;
begin
  // Poll for events, and handle the ones we care about.
 
  while ( SDL_PollEvent( @event )>0 ) do
  begin
    case event.type_ of
      SDL_KEYDOWN:
        begin
          // If escape is pressed set the Quit-flag
          if (event.key.keysym.sym = SDLK_ESCAPE) then
          begin
            m_bQuit := true;
            break;
          end;
           KeyDown( event.key.keysym.sym );
        end;
 
      SDL_KEYUP:
        begin
          KeyUp( event.key.keysym.sym );
        end;
      SDL_QUITEV:
        begin
          m_bQuit := true;
        end;
      SDL_MOUSEMOTION:
          MouseMoved(event.button.button,event.motion.x,event.motion.y,event.motion.xrel,event.motion.yrel);
      SDL_MOUSEBUTTONUP:
          MouseButtonUp(event.button.button,event.motion.x,event.motion.y,event.motion.xrel,event.motion.yrel);
      SDL_MOUSEBUTTONDOWN:
          MouseButtonDown(event.button.button,event.motion.x,event.motion.y,event.motion.xrel,event.motion.yrel);
      SDL_ACTIVEEVENT:
          begin
            if ( (event.active.state and SDL_APPACTIVE)=SDL_APPACTIVE ) then
            begin
              if ( event.active.gain = 1 ) then
              begin
                m_bMinimized := false;
                WindowActive();
              end else
              begin
                m_bMinimized := true;
                WindowInactive();
              end;
            end;
          end;
    end;
  end;
end;
 
 
Procedure IIGameEngine.DoThink();
var
  iElapsedTicks : integer;
begin
  iElapsedTicks := SDL_GetTicks() - m_lLastTick;
  m_lLastTick := SDL_GetTicks();
  Think( iElapsedTicks );
  m_iFPSTickCounter := m_iFPSTickCounter + iElapsedTicks;
end;
 
 
Procedure IIGameEngine.DoRender();
begin
  m_iFPSCounter:=m_iFPSCounter+1;
 
  if ( m_iFPSTickCounter >= 1000 ) then
  begin
    m_iCurrentFPS := m_iFPSCounter;
    m_iFPSCounter := 0;
    m_iFPSTickCounter := 0;
  end;
 
  SDL_FillRect( m_pScreen, nil, SDL_MapRGB( m_pScreen.format, 0, 0, 0 ) );
 
  // Lock surface if needed
  if ( SDL_MUSTLOCK( m_pScreen ) ) then
    if ( SDL_LockSurface( m_pScreen ) < 0 ) then
      Exit;
 
  Render( Surface );
 
  // Unlock if needed
  if ( SDL_MUSTLOCK( m_pScreen ) ) then
  begin
    SDL_UnlockSurface( m_pScreen );
  end;
 
  // Tell SDL to update the whole gScreen
  SDL_Flip( m_pScreen );
end;
 
 
procedure IIGameEngine.SetTitle(const czTitle:String);
begin
  self.m_czTitle := czTitle;
  (* Todo: Convert String to PAnciChar *)
  (*   SDL_WM_SetCaption( czTitle, 0 ); *)
end;
 
 
 
 
(* Virtual method stubs *)
Procedure IIGameEngine.AdditionalInit();
begin
end;
 
Procedure IIGameEngine.Think    (const iElapsedTime:integer);
begin
end;
 
Procedure IIGameEngine.Render( pDestSurface: PSDL_Surface  );
begin
end;
 
Procedure IIGameEngine.EndAll();
begin
end;
 
Procedure IIGameEngine.WindowActive ();
begin
end;
 
Procedure IIGameEngine.WindowInactive ();
begin
end;
 
Procedure IIGameEngine.KeyUp    (const iKeyEnum:integer);
begin
end;
 
Procedure IIGameEngine.KeyDown    (const iKeyEnum:integer);
begin
end;
 
Procedure IIGameEngine.MouseMoved    (const  iButton:integer;
           const iX:integer;
           const iY:integer;
           const iRelX:integer;
           const iRelY:integer);
begin
end;
 
Procedure IIGameEngine.MouseButtonUp  (const iButton:integer;
           const iX:integer;
           const iY:integer;
           const iRelX:integer;
           const iRelY:integer);
begin
end;
 
Procedure IIGameEngine.MouseButtonDown  (const iButton:integer;
           const iX:integer;
           const iY:integer;
           const iRelX:integer;
           const iRelY:integer);
begin
end;
 
 
end.

[edit] Game Application

File: game.pas

unit game;
interface
Uses IGameEngine,SDL;
 
type IEngine = class(IIGameEngine)
public
  Procedure AdditionalInit(); override;
  Procedure Think(const iElapsedTime:integer ); override;
  Procedure Render(pDestSurface:PSDL_Surface); override;
  Procedure KeyUp(const iKeyEnum:Integer); override;
  Procedure KeyDown(const iKeyEnum:Integer); override;
 
  Procedure MouseMoved     (const iButton:integer; 
           const iX:integer;
           const iY:integer;
           const iRelX:integer;
           const iRelY:integer); override;
 
  Procedure MouseButtonUp  (const iButton:integer;
           const iX:integer;
           const iY:integer;
           const iRelX:integer;
           const iRelY:integer); override;
 
  Procedure MouseButtonDown(const iButton:integer;
           const iX:integer;
           const iY:integer;
           const iRelX:integer;
           const iRelY:integer); override;
 
  Procedure WindowInactive(); override;
  Procedure WindowActive(); override;
 
  Procedure EndAll(); override;
end;
 
 
implementation
 
 
Procedure IEngine.AdditionalInit();
begin
  // Load up additional data
end;
 
Procedure IEngine.Think( const iElapsedTime:integer);
begin
  // Do time-based calculations
end;
 
Procedure IEngine.Render(pDestSurface:PSDL_Surface);
begin
  // Display slick graphics on screen
end;
 
Procedure IEngine.KeyDown(const iKeyEnum:integer);
begin
  case iKeyEnum of
    SDLK_LEFT:
      begin
        // Left arrow pressed
      end;
    SDLK_RIGHT:
      begin
        // Right arrow pressed
      end;
    SDLK_UP:
      begin
        // Up arrow pressed
      end;
    SDLK_DOWN:
      begin
        // Down arrow pressed
      end;
  end; (* Ends case *)
end;
 
 
Procedure IEngine.KeyUp(const iKeyEnum:Integer);
begin
  case iKeyEnum of
    SDLK_LEFT:
      begin
        // Left arrow released
      end;
    SDLK_RIGHT:
      begin
        // Right arrow released
      end;
    SDLK_UP:
      begin
        // Up arrow released
      end;
    SDLK_DOWN:
      begin
        // Down arrow released
      end;
  end;
end;
 
Procedure IEngine.MouseMoved(const iButton:integer;
           const iX:integer;
           const iY:integer;
           const iRelX:integer;
           const iRelY:integer);
 
begin
  // Handle mouse movement
 
  // iX and iY are absolute screen positions
  // iRelX and iRelY are screen position relative to last detected mouse movement
end;
 
 
Procedure IEngine.MouseButtonUp  (const iButton:integer;
           const iX:integer;
           const iY:integer;
           const iRelX:integer;
           const iRelY:integer);
begin
  // Handle Events
end;
 
Procedure IEngine.MouseButtonDown(const iButton:integer;
           const iX:integer;
           const iY:integer;
           const iRelX:integer;
           const iRelY:integer);
begin
  // Handle Events
end;
 
Procedure IEngine.WindowInactive();
begin
  // Pause game
end;
 
Procedure IEngine.WindowActive();
begin
  // Un-pause game
end;
 
 
Procedure IEngine.EndAll();
begin
  // Clean up
end;
 
 
 
 
Var  Engine:IEngine;
begin
  Engine := IEngine.Create;
 
  Engine.Title := 'Loading...';
  Engine.Init();
 
  Engine.Title := 'SDL Testing!';
  Engine.Start();
 
  Engine.Title := 'Quitting...';
 
  Halt(0);
 
end.
Categories