This tutorial is part of the Marmalade SDK tutorials collection. To see the tutorials index click here
In our previous tutorial we created created an actor, scene and camera system to automate the handling of game objects for us as well as allow us to create different types of actors derived from a common actor base. This week we are going to take the previous IwGame code base and turn it into a proper game engine. If you just want the code to this tutorial then download it from here.
Cleaning up the Mess
If you take a look at last weeks Main.cpp, you may agree that it just looks static, messy and not very readable. This week we are going to clean up our act and turn IwGame into a real usable game engine. Lets take a quick look at our new Main.cpp to see how much tidying up has been done:
int main() { // Init Game Game::Create(); GAME->Init(); // Main Game Loop while (!s3eDeviceCheckQuitRequest()) { // Update the game if (!GAME->Update()) break; // Check for back button quit if (IW_GAME_INPUT->isKeyDown(s3eKeyAbsBSK)) return false; // Draw the scene GAME->Draw(); } GAME->Release(); Game::Destroy(); return 0; }
Wow, ok, where’s everything gone? our game has been whittled down to initialisation, update, draw and clean-up. Well not quite, the code has gone somewhere, some of it has been integrated into the IwGame engine whilst the game specific code has been moved into a proper game class. Remember last week, we created scenes to manage actors? Well this week we have created a CIwGame class to manage scenes.
IwGame Class for Managing our Game
if you check out the engine code you will notice some new files:
IwGame.cpp
IwGame.h
IwGameImage.cpp
IwGameImage.h
Lets take a quick look at the CIwGame class:
class CIwGame { public: // Public access for scene iteration typedef CIwList::iterator _Iterator; _Iterator begin() { return Scenes.begin(); } _Iterator end() { return Scenes.end(); } protected: //// Properties CIwGameScene* CurrentScene; // Scene that has current input focus CIwGameScene* NextScene; // Scene that we wish to switch focus to CIwList<CIwGameScene*> Scenes; // A collection of game scenes public: void addScene(CIwGameScene *scene); void removeScene(CIwGameScene* scene); void removeScene(unsigned int name_hash); CIwGameScene* findScene(unsigned int name_hash); CIwGameScene* findScene(const char* name); CIwGameScene* getScene(int index); void clearScenes(); void changeScene(CIwGameScene *new_scene); bool changeScene(unsigned int name_hash); //// Properties end protected: uint64 LastFrameTime; // The time at which the last frame ended public: virtual void Init(); virtual void Release(); virtual bool Update(); virtual void Draw(); virtual void Save() {} virtual void Load() {} private: public: void SetBackgroundColour(uint8 r, uint8 g, uint8 b, uint8 a); };
As you can see its basically a class that allows us to add / remove and change scenes. It also provides initialisation, release, update, drawing and save / load functionality.
These methods represent the most basic functionality that I believe any game would need. If you take a look at IwGame.cpp you will notice that there is quite a lot of code in there. Lets take a quick look at what CIwGame::Init() does:
CIwGame’s Init() method carries out some basic Marmalade SDK and game specific initialisation:
void IwGame::Init() { CurrentScene = NULL; NextScene = NULL; // Initialise Marmalade SDK graphics system and Iw2D module IwGxInit(); Iw2DInit(); // Initialise the input system CIwGameInput::Create(); IW_GAME_INPUT->Init(); // Init IwSound IwSoundInit(); // Initialise Marmalade SDK resource manager IwResManagerInit(); #ifdef IW_BUILD_RESOURCES // Tell resource system how to convert WAV files IwGetResManager()->AddHandler(new CIwResHandlerWAV); #endif // Set default background colour SetBackgroundColour(0, 0, 0, 0); // Get initial time stamp LastFrameTime = s3eTimerGetMs(); }
You will notice that much of this code was present in our previous tutorials Main.cpp initialisation code. We have moved it into our CIwGame base so that we have some basic initialisation code that allows us to get our games up and running quickly.
Similarly our CIwGame::Release() method:
void IwGame::Release() { // Clean up scenes for (_Iterator it = Scenes.begin(); it != Scenes.end(); ++it) delete *it; Scenes.clear(); // Shut down the input system IW_GAME_INPUT->Release(); CIwGameInput::Destroy(); // Shut down the resource manager IwResManagerTerminate(); // Shutdown IwSound IwSoundTerminate(); // Shut down Marmalade graphics system and the Iw2D module Iw2DTerminate(); IwGxTerminate(); }
The only thing new here is the recursive release of all of the attached scenes
The last method I am going to look at is our CIwGame::Update() method
bool IwGame::Update() { // Calculate how long the last game frame took (capped at 60 and 10 fps) - We use this to scale all of our transient variables that rely // upon time so that everything movess at the same rate regardless of our devices frame rate float dt = (float)(s3eTimerGetMs() - LastFrameTime) / 16.67f; if (dt < 1.0) dt = 1.0f; if (dt > 6.0f) dt = 6.0f; LastFrameTime = s3eTimerGetMs(); // Clear the screen IwGxClear(IW_GX_COLOUR_BUFFER_F | IW_GX_DEPTH_BUFFER_F); // Update pointer system IW_GAME_INPUT->Update(); // Update Iw Sound Manager IwGetSoundManager()->Update(); // Check for scene change if (NextScene != CurrentScene) { // Notify scenes that there is a change of circumstances if (CurrentScene != NextScene) { if (CurrentScene != NULL) { CurrentScene->NotifyLostFocus(NextScene); if (CurrentScene->getAllowSuspend()) CurrentScene->NotifySuspending(NextScene); } if (NextScene != NULL) { NextScene->NotifyGainedFocus(CurrentScene); if (NextScene->getAllowSuspend()) NextScene->NotifyResuming(CurrentScene); } CurrentScene = NextScene; } } // Update all scenes that are not suspended for (_Iterator it = Scenes.begin(); it != Scenes.end(); ++it) { if (CurrentScene == *it) { (*it)->Update(dt); } else { if (!(*it)->getAllowSuspend()) { (*it)->Update(dt); } } } return true; }
Update() basically does most of what we were doing in our previous tutorials main loop, except that we use the concept of a “current scene”. Because scenes can potentially be overlaid over one another and we do not want to always be processing all scenes within our game, we use the concept of a current scene which has the focus of the player.
Now that we have the ability to change scenes, we ideally need a mechanism for notifying the other scenes that they have been switched to or switched away from, allowing our scenes to carry out processing specific to handle such events.
Another important addition to our game update loop is the measurement of time. At the start of Update() we calculate how much time has passed since Update() was last called. We can use this value to scale all of our transient variables (animations, movement etc..) within our game to ensure that they move in a consistent manner regardless of variations in frame rate. The variable dt holds our scaling factor, which is how much we need scale our transient variables by to ensure that they remain in step with our frame rate. If we did not handle time within our game then animations and movement / spinning etc.. would slow down when our games frame rate drops and increase when our frame rate increases.
Building Our Own Game Class
The idea of the CIwGame class is to provide the most basic functionality required to initialise, clean-up and update / draw our game. In order to create a real game we should derive our own class from CIwGame and add our own game specific code, not forgetting to call the CIwGame base methods so we do not miss out on our already implemented functionality.
In this case we create a class called Game.cpp which is a singleton class that derives from CIwGame and implements Init(), Release(), Update() and Draw() methods. if you take a quick look at Game.cpp you will notice that the game specific code from our old Main.cpp has been moved here.
We also go a step further and remove our dependency on storing references to scenes, images and animations etc, instead opting for finding them within their respective managers as and when needed. For example:
In Game::Update() we have:
CIwGameScene* scene = findScene("GameScene"); if (scene != NULL) { // Get tapped position in our virtual screen space CIwFVec2 pos = scene->ScreenToVirtual(IW_GAME_INPUT->getTouch(0)->x, IW_GAME_INPUT->getTouch(0)->y); // Find our sprite atlas CIwGameImage* image = scene->getImageManager()->findImage("sprites"); // Find our manic face animation frames CIwGameAnimImageFrame* anim = scene->getAnimFrameManager()->getImageFrames(0); // Create 10 player actors for (int t = 0; t < 10; t++) { ActorPlayer::Create(pos.x, pos.y, scene, image, anim); } }
Notice how we now search for our game scene, instead of accessing it via the local game_scene variable. We also do the same with image and anim.
This brings me to another new addition to the CIwGame engine.
CIwGameImage and CIwGameImageManager – Managing Images
After I had finished factoring out most of the code from Main.cpp I was left with what to do about the sprite atlas images that my game is going to use. I didn’t really want to be storing pointers to images all over the place that may or may not still exist, this can lead to some nasty and hard to find bugs. instead I opted to create a class that will manage all of the images that my scene allocates. This way we have them all in one central location and the manager can manage their lifetimes for us.
In addition, I thought that I would extend the CIw2DImage class a little by wrapping it up inside the CIwGameImage class, allowing us to add other cool functionality such as on-demand load and eventually streaming from an external web server (future blog will cover this).
So you will find that all instances of CIw2DImage throughout the engine have been replaced with the more manageable CIwGameImage class.
.
An important thing to note about CIwGameImage based images is that they rely on a CIwResGroup resource group, this is to allow them to be loaded on-demand.
To create and add an image to the game engine its a simple case of:
CIwResGroup* Level1Group = IwGetResManager()->GetGroupNamed("Level1"); game_scene->getImageManager()->addImage("sprites", Level1Group);
This creates our image based on its resource name and group and adds it to our scenes image manager. Note that the underlying CIw2DImage is not yet created. This gets created when you first call CIwGameImage::getImage2D() (on-demand). You can however force the loading / creation of the underlying CIw2DImage by passing true to addImage():
game_scene->getImageManager()->addImage(“sprites”, Level1Group, true); or by calling the Load() method on the CIwGameImage.
Well that brings us to the end of this tutorial, I’m very rushed this week so I will probably not get the time to create additional tutorials until next week. I did want to create an actual game this week using the engine, but I thought that with the engine tidy up is probably best to do that next week so readers don’t get lost. I may be able to fit a short blog update in early next week covering the wrapping up of Marmalades sample audio engine into IwGame.
If you want the source code that accompanies this tutorial then you can download it from here.
Nice clean-up! The new update method makes dealing with time much simpler, very nice.
I will play with it this weekend, and report any issues that I may find. I am thinking about implementing a game with the framework… perhaps ill add a couple of things that are missing like collisions and layers just for my own needs… Ill have to read the code again to see what is missing, but for a basic 2D game i don’t think there is much missing. Looking forward for the next installments.
My next update will wrap the audio engine and file system up into a friendlier format. I will also add layers and clipping to scenes, as well as box collision and some other bits and bobs. Good luck with your game.
Unfortunately my schedule for this weekend is full as I’m helping another developer with one of their projects. I’m also stating a new project next week, but I should be able to sneak a little time into adding the updates and maybe a quick blog covering them.
Getting memory errors when I load new images… grrr why can’t marmalade just work out memory without annoying developers to change settings.
Anyway, it would be cool if you could add layers soon, I think we would have a pretty solid foundation to build on. With layers I could do parallax, transitions and a couple of other things, already have some posteffects working (well I wrap Iw2DSetPostTransformFn and a couple of transform functions into a class).
Another huge one would be fonts, an easy way to add fonts… anyway, if you could churn out the layers class (no need for a full tutorial).
With a couple more features I could probably use this instead of my modified cocos2d-x engine. Now that would be COOL!
I agree with you on the memory issue. I’m not keen on setting memory restraints up front.
Our own internal engine supports bitmap based fonts, but I will be replacing that with the new native UI updates once Marmalade 5.2 is released. Probably best to wait for that to surface.
Will do my best to add layers and what not this week.
Hi Mat,
I noticed you updated the IwGame Engine page… you mention you have timers implemented but i did not find the code for that. Is this timer events like every 10s run animation, etc… or am I missing something here?
Also you have a duplicated item I think:
Scene game management system
Game controller (IwGame – managed collections of scenes).
Hope you had a great weekend!
CIwGameTimer in IwGameUtil is a polled software timer used by the animation system. It will also be used in many other things.
Scene game management system – This refers to individual IwGameScene scenes
Game controller (IwGame – managed collections of scenes) – This refers to IwGame which manages a collection of scenes
I will tidy the list up eventually to make it a bit clearer.
Hi Matt,
Have you actually so far integrated Box2D into Marmalade, or is this a plan for the future?
I only ask in case you know of any useful pointers that are already out there for Box2D to Marmalade (or even Chipmunk / Bullet Physics to Maramalade)?
I have tried all three but cant find a reliable solution.
Box2D is integrated into Pocketeers’ internal engine which is kind of a super supped up version of IwGame. We have a couple of games in development that use it. I did have a couple of problems integrating it, one was something to do with the definition of NaN I think. I will be adding a new Actor type that has Box2D physics / collision integrated into it at some point. Box2D is nice and quick by the way, so I would recommend going with that, although I haven’t tested any of the other engines out yet.
Thanks, I think I’ll sit tight and wait for the guide!
Cheers for the info.
I will try to free up some time this week to put together a quick Box2D integration blog for you.
running mkb using app from /Developer/Marmalade/5.1/s3e/bin …
(, IOError(21, ‘Is a directory’), )
Error opening mkb file
Press ENTER to continue
I get that error when I duble click mkb file from your project attached here. What can be wrong?
Ok, the problem was the name of directory. It was “IwGame 2” and the space caused the problem.
BTW. Your examples are always impossible to test in simulator on Mac, because it renders to fast I guess. Is there any way to limit it to 60 fps?
I believe that the call to Iw2DSurfaceShow() should cap the frame rate by performing a vertical sync. Not tried the mac version but it caps it on Windows. As a fix you could try increasing the value passed to s3eDeviceYield(0); in CIwGame::Draw()
Thanks, your advice works perfectly:) It looks that Marmalade need improve MAc version.
Really?
You wouldnt mind?
That would be great, and I’d really appreciate it.
One big query i have:
I have a rather nice class I have written that will animate / scroll menu screen etc.
Im using an image as a surface that can be used in conjunction with DrawImageRegion to display the section i want – similar to a sprite sheet but with screens.
I can use this to play a full screen animation with a ‘sprite sheet’ of 1920×480. This works fine with no problems at all.
Doubling the frames count, by way of increasing to 3840×480 / increasing to 1920×960, and altering the code to suit… the resulting display is very blurry.
Im guessing this has to do with Marmalade optimising such a large surface?
Is there a flag that can be set to tell this not be the case?
Does this mean I would have to settle for loading each frame individually?
So a sort of virtual canvas that you scroll around? That’s a mighty big image by the way, I have never drawn anything that large, I tend to use a collection of multiple images optimised as power of two textures. Are you using IwGame to display it? I’m, not sure how clever Iw2D is, but it may split the image into chunks of texture that are 1024×512 pixels wide. Don’t sew why that would make it blurry though. Maybe bilnear filtering is switched on? You can disable it by calling image2D->GetMaterial()->SetFiltering(false);
Martin, I am going to be a little delayed on producing the Box2D integration tutorial. I am currently working on an API that will hopefully allow developers to integrate Inner-active’s ad platform into any Marmalade app regardless of platform because we need it internally at Pocketeers quite urgently. The good news is that I will also be integrating it directly into IwGame along with a queued Http manager and string class etc..
Hi there,
Dont worry, thats no problem at all.
Tbh I have no idea how you manage to fit in all the things you manage in a week… You got some kind of special machine that gives you more hours in a day? Can we buy it?
Hum… I have an app that used the ad-based free app model, but the numbers have been disappointing. How is everybody else experience on getting advertisement based revenue from free apps?
Martin: I wish I could fit more hours into one day that’s for sure 🙂
Dan: We have had disappointing figures from Funky Cam 3D earning less than $400 over the last few months, but that’s because Admobs eCPM is pretty bad at $0.67 (It was less but it has recently made a large jump). We have had 529,428 impressions with a CTR of around 2%. We are hoping that when we switch to inner-actives API we will fair much better on eCPM and CTR.
What’s the best way of implementing my above requirement would you think?
My animation frames are full screen images.
I’d like to add to the basic class already mentioned.
I’ve never got my head around using arrays within classes in c++, seems far too unnecessarily complicated to me. I’ve today tried using vector arrays – sound exactly fit for my requirements – but with no luck: I get an error message about Ciw2dimage being abstract.
Is there a simple way of having an array, or vector array within a class that could hold all relevant images required for animation of that object?
Without seeing your animation its difficult to say, but personally i wouldn’t go with large full screen animations, instead I would create a class that contains a group of actors that make up the animating screen, mixing in some on screen effects.
If you do need to go for full screen animations than the best bet is to create a sprite class that can handle multiple atlases. When you draw the background sprite you can determine from your animation frame number which atlas to use. I will put this one on my list of engine todo’s because it could be quite useful.
I like to use a CIwList of pointers to objects when dealing with collections
Yep it’s hard to get a decent CTR. I think the ad based model is good only for google and other ad vendors, because as a sustainable revenue model it does not seem to work that well. Ill have to retink my android strategy that is for certain. People just don’t buy games as much on Android as they do on iOS devices…
I was sent an ad guidelines document from inner-active a few days ago and since I read that my whole view of in-app ads has changed. Prior to reading that doc my ideas of an ad was a banner at the top of the screen for everything. To me this has always seemed a bit out of place for games in particular. In future products we plan on a much closer integration of ads into the game.
Hi Matt,
I ended up having some success with using CIwArrays to hold the images within the class, and using .push_back(img) to append frames.
This works fine, my problem being now that I dont understand the format for deleting the images from the array at cleanup.
Using something along the lines of:
MenuItem::~MenuItem()
{
int i2=AnimImages.size();
for (int i=0; i<i2;i++)
{
delete AnimImages[i];
}
}
wont work.
I have tried a few things though all with no success.
Could you point me in the direction of deleting the images when used with an array as in this case?
Try this:
CIwList<CIwGameImage*> AnimImages;
void MenuItem::addImage(CIwGameImage* image)
{
AnimImages.push_back(image);
}
MenuItem::~MenuItem()
{
for (CIwList<CIwGameImage*>::iterator it = Images.begin(); it != AnimImages.end(); ++it)
{
delete *it;
}
AnimImages.clear();
}
You can access the images just like an array using:
AnimImages.element_at(index);
Perfect!
Much appreciated Matt – once again, thanks for the swift help.
First allow me to tell you that you are doing a great job putting this engine togetherSecond, I have been nailed to the screen for the past two weeks reading your code/tutorials to understand it. Its grat. keep up the good job and I will be looking for the rest of the tutorials.
I have a problem:
In order to add one actor of my own to the scene, I derived the class CIwGameActorImage. My actor has nothing in it, just a skeleton and some calls to its parent methods. In the Game::Init() I added this code :
————————————————————-
/////////////////////////
// Create My Animation
/////////////////////////
// Create an image frame manager (manages allocation and auto clean up of our animation frames)
CIwGameAnimFrameManager* anim_frame_manager = new CIwGameAnimFrameManager();
// Auto create a 8 frames of animation 36×40 pixels in size
CIwGameAnimImageFrame* anim_frames = anim_frame_manager->allocImageFrames(8, 36, 40, 0, 0, 512, 40, 512);
// Find our sprite atlas
CIwGameImage* image = game_scene->getImageManager()->findImage(“sprites”);
// Create and set up our face animation
CIwGameAnimImage* face_anim = new CIwGameAnimImage();
face_anim->setFrameData(anim_frames, 8);
face_anim->setLooped(-1);
face_anim->setPlaybackSpeed(0.2f);
face_anim->Start();
// Create the Eye Actor
MyActor *myActor = new MyActor();
myActor ->Init(game_scene, image, face_anim, 36, 40);
myActor ->setPosition(0, 0);
// Add it to the Game Scene
game_scene->addActor(myActor);
————————————————————
I ran the app on the simulator, the results were exactly as I expected them. The face is displayed in the center of the scree. However, when I run it on some real devices, they all gave some other results => nothing is diplayed on the screen, its all black, I feel that I m missing something. Any ideas?
Thanks
Criss
Hi Criss,
I’m afraid there were a few bugs in the previous version of eth engine that could prevent sprites from appearing (my apologies). Please try the new v0.230 and let me know if it fixes it. if not, you can send me your code and I will check to see what’s going wrong.
Hi Matt,
I downloaded v0.230 and I upgraded to Marmalade SDK 5.1.10 and it fixed the problem.
Thanks a lot and have a great day
Criss
PS: looking forward to read more of your tutorials
Nice one, glad to hear that its sorted.
Hoping to fit some more tutorials in this week.
Matt,
Sorry about this and please believe me I don’t mean to be a nitpicker but I am finding myself modifying the following changes with every release of your awesome engine so I was wondering if you can add them to your main branch so that everyone would benefit from your great work that you are doing. Of course that’s if only you find the changes adequate if they weren’t please ignore my email and please accept my appologies.
1-In CIwGameActor I hope you don’t mind chaging the name of the following method :
void getVisual(CIwGameSprite* visual)
to:
void setVisual(CIwGameSprite* visual)
2-Can you override: CIwGameActor::findActor() to the following :
CIwGameActor* CIwGameScene::findActor(const char* name)
{
return findActor(IwHashString(name));
}
I find it much easier to find my actors using their names instead of their references.
Finally, I have a question:
I implemented CheckIfTapped() using your provided method HitTest(), Where is the best place to call CheckIfTapped()? is it within the update method of any of my Actors?
Thanks a lot for taking the time to reply.
Best Regards,
Criss
Hi Criss,
Thanks loads for pointing our my silly copy and paste error with setVisual().
I’ve added the new findActor() method as well.
CheckIfTapped() isn’t currently implemented. I plan on adding a system that will allow you to mark actors to be checked if the user has tapped them. When a actoris tapped on a tapped flag will be marked and / or a tapped event handler will be called.
You are probably better off calling HitTest() after Draw() because the visual transform doesn’t get rebuilt until after Draw() is called.
When I implement the functionality, I will create a PostUpdate() method of the scene, which goes through the list of all actors that are marked for tap testing and determine which one is tapped based upon layer.
Will try and get this in for the weekend update.
Mat
Hi Matt,
I created few scenes and added them to the Game using the method addScene() and apparently the last added scene is displayed on top of the CurrentScene. I fiddled a bit in your code and this seems to have fixed the problem:
void CIwGame::Draw()
{
// draw all scenes that are not suspended
for (_Iterator it = Scenes.begin(); it != Scenes.end(); ++it)
{
if (CurrentScene == *it)
{
(*it)->Draw();
}
else
{
if (!(*it)->getAllowSuspend())
{
(*it)->Draw();
}
}
}
// Show the surface
Iw2DSurfaceShow();
// Yield to the operating system
s3eDeviceYield(0);
}
Hope I got it right.
While we are at it, can you please suggest a way to perform transitions between scenes? I am basically looking at a page flip back and forth between my scenes. Any guidance will be much appreciated.
have a great day and again thanks for the awesome work that you did on the engine.
Cheers
Criss
Hi Criss,
I’ve just added a proper fix for this problem, but it wont appear until 0.25 next week. I have added a new method to IwGame:
When the user adds a new scene it will bring the current scene back to the front:
I’m looking at putting together an XML driven CIwGameTimeline class that allows multiple animations to be put together (a bit like a Flash timeline), I will also integrate this into the scene system to allow scene transitions with multiple effects.
For the moment you could add TransitionUpdate() and transition states to IwGameScene then call TransitionUpdate() on any scene that’s in transition.
Mat,
Thanks a lot for the fix.
Not so sure about how would the TransitionUpdate() might work but you know what they say, “you don’t know what you can do unless you try” so I will give it a shot.
Cheers,
Criss
No problem. Im just in the process of documenting IwGame so it should become much easier to use soon. I’m up to 43 pages so far though and not even half done, so it may prove a long read. Believe it or not I’m trying to make the documentation brief 🙂