Page 1 of 1

2D Engine Template

Posted: Sun May 22, 2011 11:39 am
by timfrombriz
Ok, so, Ive spent all day researching and obsorbing as much information as I can on PureBasic. This is my first attempt at PureBasic :o , and I think Ive done alright, but found there to be a lack of clear/correct examples out there. :cry:

Ive aimed to develop a basic 32bpp 2D engine template that aims to do the following;

1. Expose some basic Engine Attributes:
- Height, Width
- FPS (Frames Per Second)
- Fullscreen or Window mode
- Ticker System (Provides exactly # executions per second)

2. Respond to common behaviours
- Initialize
- Clean up (on exit)
- Respond to Windows Messages
- Restore itself upon loosing focus
- Pause itself when focus is lost

3. Capture some performance information
- FPS
- Wasted CPU Cycles
- Ticker Position

I would love feedback from any Guru's out there on how this may be further optimized or anything else that I have missed. :mrgreen:

Some things Im not understanding;
- DrawText causes Slowness ; I presume avoiding this by using a Sprite Font will be the fastest solution.
- Flipmode #PB_Screen_SmartSynchronization vs WaitSynch : Whats the difference, I cant seem to find any real explanation on what the technical differences are. Whats the advantages? I was thinking WaitSynch suspended program execution, whilst SmartSynch returned execution immediately, and background waited for the VSync to be ready. This doesnt appear to be the case in my observation.

Heres the code, note that you need to save the file names accordingly...
"mMain.pb"

Code: Select all

EnableExplicit
IncludeFile "mEngine.pb"

Enumeration 
  #sprite 
EndEnumeration 

Structure Sprite
  x.i
  y.i
  pas_x.i
  pas_y.i
EndStructure 
Dim Sprite.Sprite(0)

;# Game Engine Settings
Engine\FullScreen = 1
Engine\Width = 800 ; 1024
Engine\Height= 600
Engine\FPS=60
;# Init DirectX & Engine Elements
InitGameEngine
;# Init Window
InitScreen
;# Init Basic Box
sprite(0)\x=Engine\Width / 2
sprite(0)\y=Engine\Height / 2 
sprite(0)\pas_x=2 
sprite(0)\pas_y=2 

QueryPerformanceCounter_(@cTimerNow)
cTimerPerSecondTriggerTime = cTimerNow
cTimerEventTickNextTriggerTime = cTimerNow
Engine\State = #GAMESTATE_RUNNING
Repeat 
  QueryPerformanceCounter_(@cTimerNow)
  While cTimerNow >= cTimerEventTickNextTriggerTime
    cTimerEventTickNextTriggerTime = cTimerEventTickNextTriggerTime + IntervalTicker
    Engine\Tick = Engine\Tick + 1
    ;# Process Tick
    QueryPerformanceCounter_ (@cTimerNow)
  Wend
  ;# Is Next Frame due?
  If cTimerNow >= cTimerFrameNext
    QueryPerformanceCounter_(@cTimerFrameNext)
    cTimerFrameNext = cTimerFrameNext + Interval
    lFrames = lFrames + 1
    ;#Process Once Per Second
    If cTimerNow >= cTimerPerSecondTriggerTime
      cTimerPerSecondTriggerTime = cTimerPerSecondTriggerTime + IntervalSecond
      lFrameCount = lFrames
      lFrameDisposedCount = (lFrameDisposed / Engine\FPS)
      lFrameDisposed = 0
      lFrames = 0
      lTickCountPerSecond = Engine\Tick - lTickCountAtLastSecond
      lTickCountAtLastSecond = Engine\Tick
    EndIf
    
    ;# Do Rendering/Processing
    ClearScreen (0)
    ExamineKeyboard ()
    Sprite(0)\x=Sprite(0)\x+Sprite(0)\pas_x
    Sprite(0)\y=Sprite(0)\y+Sprite(0)\pas_y
    If Sprite(0)\x+64>=Engine\Width
      sprite(0)\pas_x=-sprite(0)\pas_x
    EndIf 
    If Sprite(0)\y+64>=Engine\Height
      Sprite(0)\pas_y=-Sprite(0)\pas_y
    EndIf 
    If Sprite(0)\x<=0
      Sprite(0)\pas_x=-Sprite(0)\pas_x
    EndIf 
    If Sprite(0)\y<=0
      Sprite(0)\pas_y=-Sprite(0)\pas_y
    EndIf     
    DisplayTransparentSprite (#sprite , Sprite(0)\x, Sprite(0)\y)
    StartDrawing(ScreenOutput()) 
    FrontColor(RGBA(255,255,255,255)) 
    BackColor(RGBA(255,0,0,0)) 
    DrawText(10,10,"FPS " + Str(lFrameCount) + "  [" + Str(lFrameDisposedCount) + " Redundant CPU Cycles]")
    DrawText(10,25,"Ticks p/s: " + Str(lTickCountPerSecond) + " , now @ #" +Str(Engine\Tick))
    StopDrawing()
    
    ;# If Windowed, Check for events
    If Engine\FullScreen = 0
      CheckWindowedEvents
    EndIf
    FlipBuffers ()
    ;# Check if Window has lost focus
    If IsScreenActive () = 0
      ShowCursor_(1)
      If Engine\FullScreen
        ReleaseMouse (1)
        CloseScreen ()
        OpenWindow (1,1,1,1,1, "IsoMatrix" , #PB_Window_Minimize )
        Repeat
          Event= WaitWindowEvent (2)
        Until Event= #PB_Event_ActivateWindow
        CloseWindow (1)
        InitScreen
      Else
        Repeat
          CheckWindowedEvents
        Until IsScreenActive() <> 0
        ShowCursor_(0)
      EndIf
    EndIf 
  Else
    ; Not due to render a frame, count this as a wasted CPU Cycle
    lFrameDisposed = lFrameDisposed + 1
  EndIf
Until KeyboardPushed ( #PB_Key_Escape )
Endprogram()
Save this file as "mEngine.pb" in the same folder as the previous mMain.pb file...

Code: Select all

EnableExplicit
;# Engine
Structure EngineStruct
  FPS.l
  Tick.l
  State.l
  Width.l
  Height.l
  FullScreen.l
EndStructure

#GAMESTATE_NOTRUNNING = 0
#GAMESTATE_RUNNING =1

Global Engine.EngineStruct

;# Game Objects
Global Event.l

Global Freq.q
Global Interval.q
Global IntervalSecond.q
Global IntervalTicker.q
Global cTimerNow.q
Global cTimerFrame.q
Global cTimerFrameNext.q
Global cTimerPerSecondTriggerTime.q
Global cTimerEventTickNextTriggerTime.q

Global lFrames.l
Global lFrameCount.l
Global lFrameDisposedCount.l
Global lFrameDisposed.l
Global lTickCountAtLastSecond.l
Global lTickCountPerSecond.l

Macro InitGameEngine
  If InitSprite() = 0
    ShowMsg("DirectX can not be initialized.")
    EndProgram()
  EndIf
  ;If InitSprite3D() = 0
  ;  ShowMsg("Direct3D can not be initialized.")
  ;  EndProgram()
  ;EndIf
  If InitKeyboard() = 0
    ShowMsg("DirectInput Keyboard can not be initialized.")
    EndProgram()
  EndIf
  If InitMouse() = 0
    ShowMsg("DirectInput Mouse can not be initialized.")
    EndProgram()
  EndIf
  ExamineDesktops ()
  QueryPerformanceFrequency_(@Freq)
  Interval = Freq / Engine\FPS
  IntervalSecond = Freq
  IntervalTicker = Freq / 15
EndMacro

Macro InitScreen
  If Engine\FullScreen = 0
    If OpenWindow(0, 0, 0, Engine\Width, Engine\Height, "IsoMatrix", #PB_Window_SystemMenu | #PB_Window_ScreenCentered) = 0 
      ShowMsg("Can't initialize GDI Interface!")
      EndProgram()
    EndIf
    If OpenWindowedScreen(WindowID(0), 0, 0, Engine\Width, Engine\Height, 0, 0, 0,#PB_Screen_SmartSynchronization) = 0
      ShowMsg("Can't initialize DirectDraw Windowed mode!")
      EndProgram()
    EndIf  
  Else
    If OpenScreen (engine\Width, engine\Height, 32, "IsoMatrix" ,#PB_Screen_SmartSynchronization) = 0
      ShowMsg("Can't initialize DirectDraw Fullscreen mode!")
      EndProgram()  
    EndIf
  EndIf
  ReleaseMouse (0)
  CreateSprite ( #sprite , 64, 64)
  StartDrawing ( SpriteOutput ( #sprite ))
  Box (0,0,64,64, RGB (0,255,40))
  StopDrawing ()
  ShowCursor_(0)
EndMacro

Macro CheckWindowedEvents
  Repeat
    Event = WindowEvent()
    Select Event
      Case #PB_Event_CloseWindow
        EndProgram()
      EndSelect
  Until Event = 0
EndMacro

Procedure ShowMsg(sMsg.s,sTitle.s = "IsoMatrix Message")
  MessageRequester(sTitle, sMsg, 0)
EndProcedure

Procedure EndProgram()
  If Engine\State = #GAMESTATE_RUNNING
    ; Close Window
    CloseScreen() 
  EndIf
  End
EndProcedure