Game from Scratch C++ Edition Part 2



Alright, code time! In this part we are going to setup the general framework for our application. Pang! is going to be state driven, which means at any given time the game is in exactly one state. It is by changing states that we control program flow. It also makes certain tasks like pausing the game trivially simple. On the bright side, it’s all actually pretty easy to understand.



First off we want to open pang.cpp that Visual C++ automatically generated for us and make a few very small changes.



// pang.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "Game.h" int _tmain(int argc, _TCHAR* argv[]) { Game::Start(); return 0; }

Click here to download Pang.cpp



Add lines 6 and 10 to your copy of pang.cpp. Line 6 simply says to include the “Game.h” include file, we will be creating in just a few seconds. Line 10 calls the static method Start() which we are also about to create in a few minutes.



Optional information


But lets look at a couple Microsoftisms before we continue.


First, notice that your project already includes a stdafx.cpp file and on line 5 includes stdafx.h? This is Microsoft’s way of supporting precompiled headers.



What are precompiled headers?


Some of the header files you include in your project such as Windows.h or the various SFML .hpp files can become rather large but rarely ever change. To speed up the compilation process, you put all of these header files in stdafx.h and then include stdafx.h as the very first item in every cpp file. It actually makes header management a bit easier and makes the compilation process a whole bunch faster. If you don’t want to use stdafx, you can turn it off as a project setting ( or don’t select “Precompiled Headers” when you create your project ). For the record, with a file extension change, GNU C++ supports stdafx, so portability shouldn’t be an issue.


Second, you may notice that main looks different than what you expect. _tmain is a Microsoft provided macro that enables you to support UNICODE if possible or falls back to traditional main if you don’t. If you wish, you can replace _tmain with the more traditional


int main(int argc, char** argv)


if you prefer. If you aren’t compiling using a compiler other than Visual C++, there really is no point. One last quick note, if you ever see a function prefixed with an underscore such as _tmain, that indicates a vendor specific or non-standard extension that may not be portable cross compilers.



Now that you’ve made those changes, we need to create Game.cpp and Game.h.



In Solution Explorer right click Source Files and select Add->New Item… in the dialog that popped up select C++ File (.cpp) , fill in the name as Game.cpp and click Add.



Now we create the header file using almost exactly the same process. Right click Header Files and select Add-New Item.. , this time selecting Header File (.h) , fill in the name as Game.h and click Add.



Optional information


Why didn't we use Add Class?

We could have but the truth is, it is pretty buggy. First and foremost, it reserves all of the MFC datatype names even if you aren't using MFC. Want to see it for yourself, Right click Source Files and choose Add Class and try to add one named MenuItem. Annoying.




Alright, now that we have our two new source files, open up Game.h and enter the following code:


#pragma once #include "SFML/Window.hpp" #include "SFML/Graphics.hpp" class Game { public: static void Start(); private: static bool IsExiting(); static void GameLoop(); enum GameState { Uninitialized, ShowingSplash, Paused, ShowingMenu, Playing, Exiting }; static GameState _gameState; static sf::RenderWindow _mainWindow; };
First thing you might notice is that the class is entirely static.  You may be asking yourself, doesn’t that make it global?  Well frankly, yes it does.  Every public member of Game is globally available and this is actually what I want.
The reasons I made it static are:
  • I needed a global interface of some sort
  • I had no need to defer initialization so a Singleton is pretty much a waste of time
  • If Game fails to initialize, there’s no recovering anyways
  • There is going to be exactly one “instance” of this object
  • I find it cleaner




If you wanted, you could make Game a traditional class, instantiated one, passed it around and destroyed it. Personally, I simply find this solution cleaner.  You can think of static functions and member variables like the Highlander of C++ programming, there can be only one and they’ve been there forever.




The rest of the code is fairly straight forward, on line 10 we declare the single public method Start() that we called on line 10 of pang.cpp.  On line 16 we defined an enum type called GameState which represent the various states that our game can be in.  We will cover this a little bit later.




Line 19 and 20 we declare our class’s member variables.  One is an instance of the GameState enum we just defined.  The other is a (poorly named) RenderWindow, perhaps the single most important class in SFML.  We will cover this too in more detail later.




The one other thing you may have noticed is the #pragma once line.  By their nature, you should be wary of pragma’s, as they are quite often not portable across compilers.  #pragma once guarantees that the code is included only once and takes the place of a traditional include guards. #pragma once is pretty well supported by major compilers, so it should be safe to use.


Optional information

The Rules of using static in classes

When dealing with static inside your classes there are a few things you need to be aware of.

First off, static methods can only access static member variables.

Second, there is no this pointer in a static method.

Third, you cannot set a static variable in a class constructor.

Fourth, you need to initialize your static variables outside of your class.

Finally, your variables are initialized at runtime.



That’s about it. They appear more complicated than they really are.

Optional information

What's an enum?

An enum is a user defined type consisting of a set of constants. Put in plainer ( and less accurate English ), it's a way for you to assign a range of labels to a sequence of data. By default, the first value of an enum is assigned 0, but this can be overridden. Before the enum existed, it would generally have been implemented as a series of consts or worse, using #defines.
If this all seems rather confusing, see here for a much better and much more detailed explanation of enums.  Generally when you have a set list of values that never change in value or composition, you may want to consider using an enum. If you find yourself using a lot of consts and #defines in your code, you should probably look into the enum with more detail.  They are handy and make your code much cleaner to read and maintain.


Now open up game.cpp and add the following:


#include "stdafx.h" #include "Game.h" void Game::Start(void) { if(_gameState != Uninitialized) return; _mainWindow.Create(sf::VideoMode(1024,768,32),"Pang!"); _gameState = Game::Playing; while(!IsExiting()) { GameLoop(); } _mainWindow.Close(); } bool Game::IsExiting() { if(_gameState == Game::Exiting) return true; else return false; } void Game::GameLoop() { sf::Event currentEvent; while(_mainWindow.GetEvent(currentEvent)) { switch(_gameState) { case Game::Playing: { _mainWindow.Clear(sf::Color(255,0,0)); _mainWindow.Display(); if(currentEvent.Type == sf::Event::Closed) { _gameState = Game::Exiting; } break; } } } } Game::GameState Game::_gameState = Uninitialized; sf::RenderWindow Game::_mainWindow;

Click here to download Game.cpp





First and most confusing, take a look at lines 52 and 53.  These are Game’s two member variables.  As a quirk of being static, they need to be instanced manually. That said, since we don’t have a constructor available, this is a convenient time to set a default value Unitialized to _gameState.  This means when Game is created, its _gameState value will start as unitialized, which is useful in a minute.





Now take a look at Game::Start().  This currently is the only publically exposed method and is what is called in pang.cpp to get the game started.  Since Game::Start() is available globally we need to be careful that it isn’t called more than once, which would cause all kinds of bad things to happen.  This is why we check to see if the _gameState is currently Unitialized, which it will only be when starting.  If it is unitialized, simply return for now, although throwing some kind of error would be the right behavior.  Considering that when you return from Game::Start() the program exits, returning an error would be pretty redundant.





Next up, Game::Start() tells SFML to create the main window at a resolution of 1024x768 at 32bpp colour with the title "Pang!". It then switches the game to the  Playing state.  Next it starts looping over and over until the game is set to an exiting state, calling the critical function Game::GameLoop() each loop through.  When the game is moved to an exiting state, IsExiting() will return true, ending the loop ( and thus exiting the game ).





Game::GameLoop() is easily the most important function in the program as this is where everything happens and it will get much more complex as we add features.  Remember, GameLoop is called over and over millions of times, depending on the speed of your computer.  SFML uses a polling based system for events which is why we call GetEvent.  If there is an event, it returns true and assigns it to currentEvent, if there are no new events it returns false.  It is called in a while loop because there can be multiple events in the queue at a time, so it will loop over all available events until there are none left to process.




Next up is a switch statement, which determines what code runs depending on what our current gameState is.  Right now we only handle a single gameState ( Running ) but that will change shortly.  The only thing you need to remember for now is that the game is only ever in a single state at a time.  For now, the running state simply clears the screen to a bright red colour then displays it on screen.  Then it checks to see if there was an event, and if that event was the Closed event ( someone closed the window ), it switches the game state to Exiting which causes isExiting() to return true on the next pass through the game loop, causing our game loop to end.





So here it is, our program in action!








Granted not that exciting yet, but it has the framework of almost everything a game needs.  Graphics, input and an event loop.  In the next part, we will add a bit more functionality, including a menu and splashscreen.




You can download the complete project source with everything you need to this point right here. 




EDIT 02/27/2012  Massive omission!


All this time later, I realized I made a massive omission in this chapter, that if you aren’t following along using the included sample project that it would cause major confusion.  I mentioned earlier about pre-compiled headers and the stdafx files, but I forgot to include them!  Essentially it allows you to include all the various header files you are going to need and precompile them to speed things up.  In a nutshell, you put any #includes in stdafx where the code is large and not going to change.  This means headers like Windows.h or SFML headers are ideal to be located in stdafx.h.


This was such a glaring omission because this is what actually causes SFML to be included!   Oops.  The file included below is actually the stdafx.h from a much later chapter, so there are some includes that aren’t needed yet, but shortly will be.  If you are downloading the sample project,  you will notice that your copy is slightly different.


So, lets take a look at those two missing files.  First up is stdafx.cpp

// stdafx.cpp : source file that includes just the standard includes // pang.pch will be the pre-compiled header // stdafx.obj will contain the pre-compiled type information #include "stdafx.h" // TODO: reference any additional headers you need in STDAFX.H // and not in this file

Click here to download stdafx.cpp




This file simply exists to make sure stdafx.h is called.  Now lets take a look at stdafx.h



// stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, but // are changed infrequently // #pragma once #include <stdio.h> // TODO: reference additional headers your program requires here #include <SFML/System.hpp> #include <SFML/Graphics.hpp> #include <SFML/Window.hpp> #include <SFML/Audio.hpp> #include <map> #include <iostream> #include <cassert>

Click here to download stdafx.h



This file simply consists of includes for all the various header files that we are going to need through-out the project.  This prevents you from having to include them in all your various files ( you get them included when you added #include “stdafx.h” at the top of your file ).  More importantly, it prevents the compiler from having to process the contents over and over, which can speed compilation up a fair bit.



Sorry for the omission…



Back to Part One Forward to Part Three

blog comments powered by Disqus

Exporting from Blender to PS Mobile: Blender to Vita in under 5 minutes.

Exporting from Blender to PS Mobile: Blender to Vita in under 5 minutes.

30. April 2012



We are now going to look at creating a fully textured model in Blender and exporting to a PlayStation Studio SDK project, a simple model viewer.  We create a very simple model, UV map, texture then export it.  At this point, we process it using the PS Studio ModelConverter tool, import it into PS Studio then finally run our code in the simulator.


The example model is as I said, brutally simple ( it’s simply a dice, er… die, also known as a textured cube ). However the process for exporting more complex models is exactly the same.  The process isn’t really all that difficult, especially if you know your way around Blender, but if you don’t know Blender all that well don’t worry, I’ve actually captured the entire process in video form.  This video covers exactly the same thing that the rest of this tutorial does, so if you have problems following one, refer to the other and vice versa.



Modeling, texturing and exporting from Blender to Sony PlayStation Suite SDK in under 5 minutes



The above video is actually encoded at 1080p and is probably almost illegible at anything less than 720p.  You can watch it on YouTube or Vimeo in full definition.  Again, the video demonstrates exactly what I am going to show below, except the source code.  So if you are the type that prefers to learn by watching, or the type that learns by reading, you get the best of both worlds here!  Alright, let’s get started.



If you already know how to model, texture and export in COLLADA format using Blender, I suggest you skip ahead to part 2, as the remainder of this section will be quite boring for you… it goes into detail, a lot of detail. Smile



If you haven’t already, fire up Blender.  We are going to work with the default cube, if you don’t have a default cube select the Add menu –> Mesh –>Cube.  The first thing we want to do is create a new image to texture our model.


In the properties panel to your right ( assuming you are using the default layout, which I will for the remainder of this tutorial ), locate the Textures tab and click it.  Like such:





Now change “Type” to Image:



Scroll down slightly to the Image section ( expand it if required ) and click New:



In the resulting dialog, we specify the texture details.  I am going to name it the ultra imaginative name “Texture” but fell free to call it whatever you want.  1024x1024 is massive overkill for what we are doing, but hey… I like overkill.  When done, click OK.




Now we want to apply some simple texture mapping to our 3D cube.  In the 3D view, make sure you are in “Edit Mode”, either by hitting Tab until selected, or via this menu:



Now that you are in edit mode, select all the faces of your mesh by hitting “a”.  Now in the Mesh menu, you want to select Mesh->UV Unwrap…->Follow Active Quads.



Simply click OK at the resulting dialog menu.  Now we want to switch to UV editing mode.  Locate the icon to the bottom left of your view, click it and select UV/Image Editor.



We now want to make our texture the active image.  Locate the “Browse image to be linked” button, click it and select texture from the list.




Now press A to select all of our faces, then using hit S to scale ( then mouse move, left click when done), followed by G to translate, until our UV coordinates are all over the black image, like so:



Now we want to quickly paint our dice faces on the texture within the UV coordinates.  From the menubar select Image->Image Painting.



Now left click to mouse paint the texture like the face of a die.  You can use the left hand panel ( press “N” if not visible ) to change the paint settings.  When done you should have something like this:



Now, back in the Image menu, unclick Image Painting.  Now we want to save our completed image.  In the same menu as before, select “Save as Image” or hit F3.  In the resulting dialog, locate the directory you want to save the texture to ( make it the same place you are going to save the model ), name it, then click Save as Image:



Now with that complete, we assign our image to our texture.  Back in the Texture panel ( to the right ), locate the Image section, click the Browse Image to be Linked button and select your texture.



Now scroll down lower in the Texture panel, locate Mapping, then select the Coordinates: dropdown and select UV.



Right below that, select the Map: dropdown and select UVMap.





All right, now we have a fully texture mapped model ready for exporting.  From the File Menu ( top right ), select File->Export->COLLADA(.dae).




In the resulting dialog, navigate to the same location where you saved your texture, name it ( I used box.dae ), optionally select “Export only selected” then click Export COLLADA.





And we are now done with Blender.



This section has already gotten quite long so I am going to break this post into two parts.



Continue on to Part 2.

Programming, Art , , , ,

blog comments powered by Disqus