Games
Development
My Blog
About Me
Forums
Subscribe

Game Development

Pixie Tutorial 1 - Hello World

So, it's time to start writing up some tutorials for Pixie, so that others may find a use for it too :-)

First out is (of course) the classic Hello World example. The actual code for writing "hello world" on the screen is just a few lines, but there's a few details involved in getting the project created and compiling, so I'll take the opportunity to explain a few basic concepts of Pixie as part of this tutorial, as it will be handy to be familiar with them going forward.

The completed "Hello World" tutorial.

Getting Started with EmptyProject

First of all, you need to download the Pixie library. Unzip to anywhere you like. You should see three folders: EmptyProject, Example and PixieLib.

When you unzip Pixie.zip, you will have three folders: EmptyProject, Example and PixieLib.

The folder Example contains a small example application in Pixie, demonstrating a few basic concepts. We'll leave that one alone for now. PixieLib is the actual Pixie game engine in the form of a static library, and EmptyProject is a solution and project file set up with the bare minimum stuff needed to get an application up and running using Pixie. It won't actually do anything but display an empty window, but the basic framework and compiler settings are all there.

I find that the quickest way for me when I want to set up a new project for Visual Studio is to just copy an existing one which already works, so we'll use that method to get started now. Make a copy of the EmptyProject folder, and change its name to HelloWorld.

Copy the folder "EmptyProject", and rename the copy to "HelloWorld".

 Inside that folder there's EmptyProject.sln and EmptyProject.vcproj. Rename them to HelloWorld.sln and HelloWorld.vcproj, respectively.

Open the "HelloWorld" folder, and rename "EmptyProject.sln" to "HelloWorld.sln", then rename "EmptyProject.vcproj" to "HelloWorld.vcproj".

Open the two files, one at a time, in Notepad and do a search and replace, replacing all occurances of EmptyProject with HelloWorld.

Open the file in Notepad.

Do a search and replace, replacing all occurances of "EmptyProject" with "HelloWorld".

Remember to do the same thing to HelloWorld.vcproj as well.

Once this is done, you can open HelloWorld.sln in Visual Studio as you normally would, and we now have our project all set up and ready to go. Use this method whenever you want to start a new project, as it's the quickest way to get up and running.

Running in Fullscreen and Windowed modes

When you look at your new HelloWorld solution in the Solution Explorer in Visual Studio, you will see that it's got two projects: HelloWorld and PixieLib. These are set up so that HelloWorld is the startup project, and links with PixieLib to allow it to use the engines functionality.

The HelloWorld project has two projects: HelloWorld and PixieLib.

If you build and run the solution, you should get an empty fullscreen window which you can exit by pressing Alt+F4. Now, if you right click on the HelloWorld project, and select Properties, and then locate Configuration Properties->Debugging and on the line where it says Command Arguments you put in -window (note there's a minus sign at the start), and then run the solution again.

Right-click on the HelloWorld project, and select "Properties".

Select "Debugging", then type "-window" where it says "Command Arguments".

Now it will be displaying an empty window with a titlebar which you can exit as you would any window, by pressing the close button in the top right corner. There's other Command Arguments available for Pixie as well, which we'll see later on, but -window is the most important one for now, as it makes things a bit nicer when debugging.

Game.h and Game.cpp

If you expand the HelloWorld project, you'll note there's a Game.h and a Game.cpp file in there. These are the starting point of our application, and we'll examine them quite closely, as they will highlight some of the central concepts of the Pixie game engine. This section will be quite technical, but stay with me; these are important concepts.

The HelloWorld project comes with two files, Game.cpp and Game.h.

Start by looking at the top of Game.cpp, at the function named SetupSystems. This function is called by the engine at the very start of the execution, before anything else is done. The engine expects this function to be present, and if it is not, you will get a linker error. The SetupSystems function is responsible for telling the engine which Systems (and in some cases which specific implementation of a system) to use for this application. A System is a basic component of Pixie, and it is a type of class which there will only ever be one instance of, and it will be created at the start of the execution, and destroyed when the user exit the application (classes with this behaviour are often called singleton classes. Pixie systems are a bit more formalized than plain singletons, as we will see later).

The "SetupSystems" function initializes the different components of the game engine.

A system can be responsible for pretty much anything, and you can create your own systems specific to your game, and if you do, those will also be specified here. As you can see, there's already a bunch of systems specified in here: five platform specific systems (these abstract some of the platform specific functionality, and they are specified to use the Windows implementation), five standard systems (which are generally useful stuff which comes with Pixie). Last, there's one custom system, which differs by being registered before it is added. That's generally what you'll do with any of your own custom systems as wel. The "Application" system is a required system - you always have to provide your own implementation for it, which willl be your main game class, and that's what is being done here.

A system always has four methods: Initialize, Start, Stop and Terminate, but they have default (empty) implementations, so they are optional for you to override. When the engine starts, right after SetupSystems have been called, it will loop through all the systems, in the order they were added, and call Initialize on each of them. Then it will loop through a second time, calling Start on each system. Upon exiting the application, the engine will loop through all the systems and call Stop on each one, and then loop through the systems a last time, calling Terminate on each system.

Using this two-pass initialization and shutdown, makes it a bit easier to manage startup dependencies, but in most cases, it is enough to just use the Start and Stop methods, and leave Initialize and Terminate with their default implementations (empty). The Game.h and Game.cpp from EmptyProject only implements Start and Stop, but doesn't do anything in them (they're there for us to fill in).

The Game class inherits from the Application base class (which is defined in the engine), and the Application base class defines a method named Update. This is a very important method; it is called repeatedly by the engine while the game is running. Initialization and shutdown of the game is done on Initialize/Start and Stop/Terminate, but everything else, the actual game stuff, is done on Update. Generally, one frame of the game will be processed on each call to Update.

The Bitmap Class

The Pixie engine provides a lot of functionality for working with art, and the most basic one is the Bitmap class. A bitmap in Pixie is just a collection of pixels, with a given width and height. You can set and get the color of individual pixels of a bitmap, and you can copy a bitmap (or a part of one) onto another bitmap. There's also functionality to draw sprites and text onto bitmaps.

When making a game, you want to create a bitmap to represent the screen, with the width and height you want, and then draw all your stuff to it. When you have done all the drawing for a frame, you display the bitmap by sending it to the screen.

HelloWorld - The Code

So, enough theory for now... let's get to the code...

First, we need to create the bitmap to represent the screen. Add the following line to the class declaration of Game.h:

Bitmap* screenBitmap_;

Add it on the line after the "protected:" keyword, as we don't need this to be a public variable. Throughout Pixie, and in all my own games, I use the notation of a trailing underscore to mark a variable as being a member of a class, rather than a local variable (a more common notation is to add a "m_" prefix to everything, but that just looks messy).

Then, at the top of Game.h, just after the line with the comment that says "// External Classes", you need to add:

 class Bitmap;

This is something I do a lot in the engine: forward declaring of a class. It simply means we're telling the compiler that there is something called Bitmap, and that it is a class, so that the declaration we added to the protected section will be valid. We could of course have included "Bitmap.h" here instead, and that would have worked just as well, but I try to avoid including files from header files as much as possible, as doing so can seriously increase compile times, as things are included all over the place, over and over again. Better to forward declare in the .h file, and include in the .cpp file. Being able to do a full rebuild of the whole engine in just a couple of minutes makes it worth the slight inconvenience.

At the top of Game.cpp there's a line which includes Game.h. Add the following directly below it:

#include "Bitmap.h"
#include "Platform_Screen.h"

Game.cpp will make use of both the Bitmap class and the Platform_Screen system (which we'll learn about later), so both of those classes needs to be included here.

Next, locate the Start method in Game.cpp, and add this line to it:

screenBitmap_=new Bitmap(640,480);

This creates a new Bitmap which is 640 pixels wide and 480 pixels high. Each pixel will be a 16 bit RGB value, with 5 bits for the red component, 6 bits for the green, and 5 bits for the blue component (there's no other available bitmap types in Pixie - everything is 16 bit, just to make things nice, simple and fast. And 16 bits is really all that's needed).

If we allocate something, we need to destroy it as well, so add this to the Stop method:

delete screenBitmap_;

Now we have the Bitmap all set up, so it is time to draw something on it and display it, so locate the Update method in Game.cpp, and add the following two lines:

screenBitmap_->Fill(RGB16(255,0,0));
siPlatform_Screen->Present(screenBitmap_); 

The first line fills the entire bitmap with a solid color. Since colors are 16 bits, we use the function RGB16 to convert three individual 8 bit values (one each for the red, green and blue component) into the 16 bit RGB565 value expected by the Pixie library functions. There's also a Fill method for filling only a specified part of the bitmap, but for this, I thought it best to fill the whole thing.

The second line displays the bitmap. Platform_Screen is a system which has a custom implementation for each different platform. For the windows platform, the Platform_Screen will attempt to use DirectX9 to display the bitmap, and if that fails for some reason, it will fall back to DirectX3, which works on most windows computer. In the unlikely event that DirectX3 would also fail, Pixie will fall back to use standard Windows GDI functions, and they always work (but are a bit on the slow side...). This makes Pixie very stable, and your game should run on any Windows machine.

The "si" prefix before Platform_Screen in the Present call, is an acronym for "System Instance". For every system I create, I have a habit of creating a macro to access the instance of that system, to make it more convenient to use, and siPlatform_Screen is the macro to access the instance of Platform_Screen.

I will go into a bit of detail about Platform_Screen, as it differs a bit from how things usually works in engines. First of all, there's no way to set the screen resolution, not even in fullscreen mode. You can choose the size of the bitmap you send to Platform_Screen, and Platform_Screen will always do its best to make the bitmap cover as much of the screen as possible. But with todays range of different monitors (4:3, widescreen, portrait/landscape), there might well not be a 1:1 mapping between your standard 4:3 resolutions and the monitor. In that case, Pixie will automatically display borders above/below or on the sides of your bitmap. By not changing screen resolution, we also eliminate the risk of messing up the other windows the user might have open, which is always an annoying effect of running fullscreen apps.

Ok, so if we run the game now, we should get an all red window (though you might get some black borders, depending on what resolution you're running at), showing us that both the Fill call and the Present call works as it should.

So now let's wrap this up HelloWorld tutorial with something that actually prints some words on the screen. At the top of Game.cpp add another line to the list of #includes:

#include "DebugFont.h" 

Next, modify the Updatemethod by inserting two line, to be like this:

screenBitmap_->Fill(RGB16(255,0,0));
DebugFont font;
font.Blit(screenBitmap_,10,10,"Hello World");
siPlatform_Screen->Present(screenBitmap_); 

What we're doing here, is declaring an instance of the DebugFont class, locally on the stack (which is ok, because there's not any particular overhead to creating it), and then we use it to Blit (or draw) a text string onto our bitmap, at a specified location (10 pixels from the left edge and 10 pixels from the top edge).

The DebugFont is a predefined font which is primarily intended for debug purposes, as it is much more limited (but also more lightweight) than the standard Font class (which we'll go through in a later tutorial).

Wrapping Up

That's pretty much it for our HelloWorld exammple. We added ten lines of code to Game.h and Game.cpp, to include the necessary header files, define and create our screen bitmap, write "Hello World" onto it, and present it on the screen. We also looked at the basic concept of Systems, and the Application base class.

If you build the code, the executable you get will be fully stand-alone. For Pixie, there's no external dependencies: No runtime environments or dll's to include, no third-party software required to be installed (though it will take advantage of DirectX if it comes across it), and no files you need to include with the distribution. Just the executable on it's own is all that is needed.

Stay tuned for the next tutorial, in which we will display a picture on the screen, and learn how Pixie handles files...

The final Game.h

The final Game.h

The final Game.cpp

The final Game.cpp