09 October, 2006

Game Engine Progress...

I've been working on my (2D) game engine now in Dolphin for a little over a month, and I must say that I'm quite impressed with how quickly it is progressing. The iteration time on code is extremely fast, and I've been able to create some very fast demos. The most frustrating work was just typing in the COM wrappers for Direct3D, DirectInput, and XACT. However, once that work was done, the rest has been pretty smooth sailing.

(e := GameEngine current)
createGameView: GameView fullscreen: false;
run.

That's about as simple as it gets right now. The GameEngine object is a singleton, which wraps up Direct3D and has two other objects inside it that make up the majority of the engine: GameControllers and GameAudioEngine (DirectInput and XACT respectively).

The GameEngine has a stack of GameScene objects, each of which can respond to varying messages:

#advance:
#render
#enter
#exit

Those are the basics. The #enter and #exit messages are sent to the scene when it is pushed onto and popped off of the scene stack (used for loading/creating objects and releasing them). The #advance: message is sent once per main loop iteration with the delta time (in seconds) since the last advance. This is where various controller inputs and game logic would progress. And, whenever needed, the #render message is sent.

It is extremely easy to test out new scenes and try out different code. What's even better, all of this is doable while the game is running! I can't stress this enough. A great example of this was when I wanted to try out a simple pause screen. I had the main menu scene setup, and every time the spacebar was pressed I wanted to enter the pause scene. So, while the game was running, I created a PauseScene object:

PauseScene>>enter

font := GameFont new.
font load: 'Arial' height: 60.

PauseScene>>render
font
draw: 'PAUSED'
center: (GameEngine current view viewportExtent) / 2.


PauseScene>>advance: deltaTime
(GameEngine current keyboard keyPressed: DIK_SPACE)
ifTrue: [
GameEngine current exitScene].

Once this was in place, all that was needed was to modify the #advance: message in the main menu object so that it was possible to get to the paused scene:

MainMenuScene>>advance: deltaTime
(GameEngine current keyboard keyDown: DIK_SPACE)
ifTrue:
[
GameEngine current enterScene: PauseScene new].

Right-click/accept, and switch back to the game and hit the spacebar. And now we're at the save game screen. I can't stress enough just how much of a time saver this will be once I actually start working on the game itself.

Also, for those that might be interested, framerate has not been an issue at all. This was something that I was worried about at the beginning of this little endeavour, too. Right now, I can blit massive numbers of quads to the screen and keep a consistent framerate well over 300 on my Thinkpad laptop. The same code on my workstation at work runs in excess of 3000 FPS. Dolphin might be interpreted bytecode, but I have to hand it to Andy and Blair, it's fast!

Something else that I've found to be an absolute dream in Smalltalk is resumeable exceptions. I'm hardly a "code it right the first time" programmer - especially when working in a new language. I don't know how many times I've had a bug, an exception is thrown, and I've fixed the code right then and there, and/or modified a variable's value and continued running the game. And I also need to thank Andy and Blair for all those little touches in Dolphin that can make all the difference (for example, if a COM object returns an HRESULT error code, the debugger will give you the text error in addition to the cryptic error code).

I can't impress enough on programmers (outside the Smalltalk community) just how wonderful it is to be able to inspect anything at runtime. Make the GameEngine a singleton object was easily the best decision made so far. While running, I can just open up a worksheet and type:

GameEngine current

And then hit Ctrl+I to inspect it. Instantly seeing what scene is running, what state everything is in. This isn't remotely the same as variable watch in C++ (which was really all I thought it was initially).

Hopefully in the next post I'll be able to throw together some screenshots and perhaps a downloadable demo. Stay tuned!

No comments: