IwGame Engine Tutorial – Actors

CIwGameActor Object – Sprites With Brains

Introduction

Whilst our title comparison suggests that actors are simply sprites with brains they have the potential to be much more.

Going back to comparison in the scene introduction section, actors play a pivotal role in our scenes, each actor having its own unique role and visual appearance. Actors are the building block of the game, they provide the actual unique functionality and visuals that make up the game as a whole. They can provide any type of functionality from a simple bullet fleeting across the screen to something as complex as a dynamic machine that modifies its behaviour and appearance based upon data streamed from a web server.

A CIwGameActor is a very generic object that provides quite a lot of functionality out of the box. The idea is for developers to create their own actor types from the base CIwGameActor class then implement their own custom functionality within its Update() method. The basic functionality provided by CIwGameActor includes:

  • Support for actor pooling to help reduce memory fragmentation
  • Unique names so they can be searched
  • Actor types
  • Position, Depth, Origin, velocity and velocity damping
  • Angle, angular velocity and angular velocity damping
  • Scale and Colour
  • Layers
  • Active and visible states
  • A visual that represents it on screen
  • Animation timeline that can be attached to the visual
  • Collision size / rectangle
  • Wrapping at scenes extents
  • Instantiation itself from XOML
  • Animation timline update
  • Other actor linkage (used to connect actors in a child / parent style system)
  • A Box2D physical body consisting of a material and shape
  • Box2D collision category, mask and group

Note that any changes made to the actor will automatically be applied to the actors visual.

As IwGame progresses more actor types with additional functionality will be created to create more out of the box style game objects (plug-in actors if you will). For the moment the following actors have been created for you:

CIwGameActorImage – This object represents a basic image based actor which has an associated image and animation.
CIwGameActorText – This object represents a basic text based actor which has an associated font and animation.
CIwGameActorParticles – This object represents a complex particle based actor system consists of manypatricles that move independently and have varying life spans.

A word of warning, do not forget to call the based classes Init(), Reset(), Update(), UpdateVisual() methods from your own derived classes or the underlying functionality will not be provided.

Creating Actors

Creating an actor is very simple as the following code shows:

	// Create player actor
	MyActor* actor = new MyActor();
	if (actor == NULL)
		return NULL;

	actor->Init();
	actor->setName("Player1");
	actor->setPosition(x, y);

	// Add player actor to the scene
	scene->addActor(actor);

In the above code we create a a basic MyActor object, which is a class that I created derived from CIwGameActor giving us the base CIwGameActor functionality. However, adding this code into a game wouldn’t actually see anything as we have not assigned a visual element to the actor. CIwGameActor does not handle the creation of a visual for you, instead it handles the rendering and update of a visual and its animations.

To get developers started with actors we included the CIwGameActorImage that will create a basic image based actor that supports animation.

If you require your actor to support Box2D physics then you should either define the Box2DMaterial and Shape in XOML or if creating manually then call:

	InitBody(Scene, shape, material, &Position, Angle, com.x, com.y);

This can be called before or after CIwGameActor::Init()

Creating a CIwGameActorImage

Creating an image based actor is a little more complicated, lets take a look at some example code:

	// Create a new instance
	ActorPlayer* actor = new ActorPlayer();
	if (actor == NULL)
		return NULL;

	// Create player actor
	actor->setScene(scene);
	actor->Init(image, 36, 40);
	actor->setPosition(x, y);

	// Add player actor to the scene
	scene->addActor(actor);

Creation is very similar to creating a basic CIwGameActor with the additional complication of having to pass an image to the actors Init() method.

Looking at the above code we create an ActorPlayer, which is a class that I created derived from CIwGameActorImage as we want some basic image functionality.

We then call the actors its Init() method to set up actor internals. We give the actor a name so that we can find it later then set its world position to the centre of the scene. Finally we add the actor to the scene.

You will notice that ActorPlayer’s Init() method has quite a a few parameters. When we call Init(….) we are actually calling CIwGameActorImage::Init(….) and passing along all the details shown in the code above which includes an image that will represent our actor (or more usually an image atlas), and the width and height of the visual on screen (in virtual canvas coordinates). Internally CIwGameActorImage will create a sprite to display our actor.

The end product of the above code is an actor that can be seen, moved around, scaled, rotated etc..

Now lets take a look at a slightly more complicated ezmple that creates an image based actor that uses an animation time line (more details on time line’s later):

	// Create a new instance
	ActorPlayer* actor = new ActorPlayer();
	if (actor == NULL)
		return NULL;

	// Create an animation timeline to hold our image animation
	CIwGameAnimTimeline* timeline = new CIwGameAnimTimeline();

	// Create and set up our face animation
	CIwGameAnimInstance* face_anim = new CIwGameAnimInstance();
	face_anim->setAnimation(anim);
	face_anim->setTarget(actor, "SrcRect");
	timeline->addAnimation(face_anim);
	timeline->play();

	// Create player actor
	actor->setScene(scene);
	actor->Init(image, 36, 40);
	actor->setTimeline(timeline);
	actor->setPosition(x, y);

	// Add player actor to the scene
	scene->addActor(actor);

I have marked the changes from the previous example.

The first set of changes deals with creating a time line object then creating the instance of an animation and adding that to the time line. This process allows the actor to track and update its own animations

In the last change we simply assign the time line to the actor, the actor will now take care of playing the animation and updating the actors visual with animation changes.

Text Based Actors

Text based actors enable you to instantiate text into the scene with very little effort from code or more easily from XOML. Thse text objects can be use very much in the same way as image based actors in that they can be moved around, scaled, rotated, hit tested or even have physics and collision applied to them.

Lets firstly take a look at creating a text based actor in code:

	// Find our preloaded font
	CIwGameFont* font = (CIwGameFont*)IW_GAME_GLOBAL_RESOURCES->getResourceManager()->findResource("font1", CIwGameXomlNames::Font_Hash);

	// Create a text actor
	CIwGameActorText* text_actor = new CIwGameActorText();
	text_actor->Init(font);
	text_actor->setText("Hello World!");
	text_actor->setRect(CIwRect(-100, -100, 200, 200));
	text_actor->setColour(0, 0, 0, 255);
	text_actor->setPosition(0, 0);
	text_actor->setAngle(45);

	// Add to the scene
	CIwGameScene* scene = findScene("Scene1");
	scene->addActor(text_actor);

In the above code we firstly locate our font (which we have already preloaded into the resource system) then we create a text based actor from CIwGameActorText initialising it with the font. We then set the text, rect and some other parameters.

We now search for the scene we want to place the actor in and it to the scene.

Now lets take a look at how to instantiate a text based actor in XOML:

<ActorText Position=”0, 0″ Rect=”-100, -100, 200, 200″ Angle=”45″ Font=”font1″ Text=”Hello World!” Colour=”0, 0, 0, 255″ />

As you can see the XOML definition is much more compact and readable.

Particle System Actors

From v.030 of IwGame the new particle system based actor is available. This actor is special in that it is optimised for creating, displaying and updating a complete system of sprites (kind of like its own sprite manager). The advantage of this actor is that it does not have to deal with each particle as a separate actor object. The CIwGameActorParticles actor supports both manual and auto generation of particles. Auto generation can be controlled using a number of a control parameters.

Particles have a number of properties that can be adjusted:

  • Visual
  • Position
  • Velocity
  • Velocity Damping
  • Gravity
  • Scale
  • Scale Velocity
  • Scale Velocity Damping
  • Angle
  • Angle Velocity
  • Angle Velocity Damping
  • Colour
  • Colour Velocity
  • Colour Velocity Damping
  • Depth
  • Depth Velocity
  • Depth Velocity Damping
  • Active state
  • Visible state
  • Lifespan – Duration of particle in seconds
  • SpawnDelay – The amount of time to wait before spawning for the first time
  • Lives – Number of times the particle will re-spawn (-1 for infinite)

The CIwGameActorParticles class contains two methods for generating random particles:

void GenerateRandomParticles(int count, CIwRect& src_rect, CIwFVec4& colour, CIwFVec4& colour_velocity, float duration, int repeat_count, float spawn_delay_change, float gravity)
void GenerateRandomParticles(int count, CIwGameActorParticle* particle, CIwRect& src_rect, float duration, int repeat_count, float spawn_delay_change)

Both of these methods will generate a number of particles based on a set of limits.

To determine which particle parameters are generated randomly the CIwGameActorParticles class supports the following methods:

void	setPositionMode(eParticleMode mode)
void	setAngleMode(eParticleMode mode)
void	setScaleMode(eParticleMode mode)
void	setVelocityMode(eParticleMode mode)
void	setAngVelocityMode(eParticleMode mode)
void	setScaleVelocityMode(eParticleMode mode)
void	setDepthMode(eParticleMode mode)
void	setDepthVelocityMode(eParticleMode mode)

By setting the mode to PAM_Random the specified parameters will be generated randomly.

When parameters are generated the following methods specify limits to the random formulas used to generate the parameters:

void	setPositionRange(CIwFVec2& range)
void	setAngleRange(CIwFVec2& range)
void	setScaleRange(CIwFVec2& range)
void	setDepthRange(CIwFVec2& range)
void	setVelocityRange(CIwFVec4& range)
void	setAngVelocityRange(CIwFVec2& range)
void	setScaleVelocityRange(CIwFVec2& range)
void	setDepthVelocityRange(CIwFVec2& range)

Lets take a look at some code that generates an explosion type particle system:

CIwGameActorParticles* GameScene::AddExplosion(int num_particles, float x, float y, float scale, float depth, int layer, float gravity)
{
	// Create explosion particle actor
	CIwGameActorParticles* actor = new CIwGameActorParticles();
	addActor(actor);
	actor->Init(num_particles);
	actor->setImage((CIwGameImage*)ResourceManager->findResource("sprites1", CIwGameXomlNames::Image_Hash));
	actor->setPosition(x, y);

	// Set random parameters
	actor->setScaleMode(CIwGameActorParticles::PAM_Random);
	actor->setAngVelocityMode(CIwGameActorParticles::PAM_Random);
	actor->setVelocityMode(CIwGameActorParticles::PAM_Random);

	// Set paramater limits
	CIwFVec2 scale_range(scale, scale + scale / 2);
	actor->setScaleRange(scale_range);
	CIwFVec2 angle_range(-5, 5);
	actor->setAngleRange(angle_range);
	CIwFVec4 vel_range(-5, 5, -5, 5);
	actor->setVelocityRange(vel_range);
	CIwRect src_rect(908, 440, 100, 100);
	CIwFVec4 colour(255, 255, 255, 255);
	CIwFVec4 colour_vel(0, 0, 0, -5);

	// Generate the particles
	actor->GenerateRandomParticles(num_particles, src_rect, colour, colour_vel, 2, 1, 0, gravity);

	return actor;
}

Here we create a particle actor, set up the which parameters should be randomised then set the random limits. Finally we tell the actor to generate random particles

Now lets take a quick look at generating particles manually in code:

CIwGameActorParticles* GameScene::AddStream(int num_particles, float x, float y, float scale, float depth, int layer, float gravity)
{
	// Create stream particle actor
	CIwGameActorParticles* actor = new CIwGameActorParticles();
	addActor(actor);
	actor->Init(num_particles);
	actor->setImage((CIwGameImage*)ResourceManager->findResource("sprites1", CIwGameXomlNames::Image_Hash));
	actor->setPosition(x, y);
	CIwRect src_rect(800, 291, 68, 65);
	CIwFVec4 colour(255, 255, 255, 128);
	CIwFVec4 colour_vel(0, 0, 0, -3);

	// Create and add particles
	float spawn_delay = 0;
	for (int t = 0; t < num_particles; t++)
	{
		CIwGameActorParticle* p = new CIwGameActorParticle();
		p->LifeSpan = 1;
		p->Lives = -1;
		p->SpawnDelay = spawn_delay;
		p->Gravity = gravity;
		p->Colour = colour;
		p->ColourVelocity = colour_vel;
		p->DepthVelocity = -0.01f;

		actor->addParticle(p, src_rect);
		spawn_delay += (float)1.0f / num_particles;
	}

	return actor;
}

This method creates a particle actor then manually creates a stream of particles that spawn at slightly different times to create a stream type particle system.

Particle actors can also be created in XOML. Lets take a quick look at an example:

    <ActorParticles Name="StreamParticles" Image="sprites1" Position="0, 0" Scale="1.0" Depth="1.0" Layer="1" VelAngMode="random" VelMode="random" AngMode="random" ScaleMode="random" PositionRange="100, 100" AngleRange="0, 360" AngVelRange="-5, 5" ScaleRange="0.25, 0.5" DepthRange="0.5, 1.0" VelRange="-2, 2, -2, 2" ScaleVelRange="0, -0.1" DepthVelRange="0, 0">
        <Particle Count="10" Position="0, 0" VelocityDamping="0.95, 0.95"
		SrcRect="908, 440, 100, 100" ColourVelocity="0, 0, 0, -4" Duration="2"
		Repeat="-1" SpawnDelay="0" />
        <Particle Position="0, 0" VelocityDamping="0.95, 0.95" SrcRect="908, 440, 100, 100"
		ColourVelocity="0, 0, 0, -4" Duration="2" Repeat="-1" SpawnDelay="0" />
        <Particle Position="0, 0" VelocityDamping="0.95, 0.95" SrcRect="908, 440, 100, 100"
		ColourVelocity="0, 0, 0, -4" Duration="2" Repeat="-1" SpawnDelay="0.4" />
        <Particle Position="0, 0" VelocityDamping="0.95, 0.95" SrcRect="908, 440, 100, 100"
		ColourVelocity="0, 0, 0, -4" Duration="2" Repeat="-1" SpawnDelay="0.8" />
        <Particle Position="0, 0" VelocityDamping="0.95, 0.95" SrcRect="908, 440, 100, 100"
		ColourVelocity="0, 0, 0, -4" Duration="2" Repeat="-1" SpawnDelay="1.2" />
        <Particle Position="0, 0" VelocityDamping="0.95, 0.95" SrcRect="908, 440, 100, 100"
		ColourVelocity="0, 0, 0, -4" Duration="2" Repeat="-1" SpawnDelay="1.6" />
    </ActorParticles>

The above XOML firstly generates 10 random particles at time 0, followed by 4 additional particles at times 0.4, 0.8, 1.2 and 1.6 seconds.

If you would like finer grained control over particle actors then you can simply derive your own version from CIwGameActorParticles

Actor Lifetimes

Actors will persist within the scene until a) the scene is deleted b) you explicitly remove them or the recommended method c) they remove themselves. An actor can easily remove and delete itself from the scene by returning false from its Update() method. Here’s an example:

bool ActorPlayer::Update(float dt)
{
	// If fade timer has timed out then delete this actor
	if (FadeTimer.HasTimedOut())
	{
		return false;	// returning false tells the scene that we no need to be removed
	}

	// Calculate our opacity from time left on fade timer
	int opacity = FadeTimer.GetTimeLeft() / 2;
	if (opacity > 255) opacity = 255;
	Colour.a = opacity;

	return CIwGameActorImage::Update(dt);
}

Actor Naming and Finding Actors

As mention previously for scenes, actors also named objects, each instance of an object that you wish to query should have its own unique name (per scene) so that it can be located and modified at a later date.

You can find an actor in a particular scene using:

	CIwGameActor* actor = scene->findActor(“Player1”);
	if (actor != NULL)
	{
		// Do somethinig with the actor
	}

There are three ways to locate actors within a scene:

	CIwGameActor*		findActor(const char* name);
	CIwGameActor*		findActor(unsigned int name_hash);
	CIwGameActor*		findActor(int type);

These allow you to search for actor by string, hash or type. Note that searching by type will return the first and only the first instance of that particular actor type. This is very useful if you want to find a unique actor type, for example the player.

Actor Types

When developing games I find it incredibly useful to assign different types of actors different type ID’s, this allows me to optimise many area of my code such as collision checks. Carrying a type ID for each actor also comes in handy when you want to know the types of actor that you are interacting with.

You can set and get the actors type ID using:

	void		setType(int type)
	int		getType() const

Moving, Rotating and Spinning Actors

Actors come with a very basic physics system that allows movement via velocity and angular velocity, actors can also be scaled. CIwGameActor provides the following basic functionality to handle these features:

	void				setPosition(float x, float y)
	CIwFVec2			getPosition()
	void				setAngle(float angle)
	float				getAngle()
	void				setVelocity(float x, float y)
	CIwFVec2			getVelocity()
 	void				setVelocityDamping(float x, float y)
	void				setAngularVelocity(float velocity)
	float				getAngularVelocity() const
	void				setAngularVelocityDamping(float damping)
	void				setScale(float scale)
	float				getScale() const

Note that velocity and angular velocity damping is a reduction factor that is applied each game frame to slow down objects linear and angular velocities. Their default values are 1.0f which provides no damping, setting this value to less than 1.0f will dampen velocity whilst setting it to a value greater than 1.0f will enhance velocity.

Also note that changing position or angle will not effect velocity.

If the actor was created with Box2D physics enabled then you can also use the supplied force application methods.

Attaching a Visual and an Animation Timeline

For our actor to become visible on screen we need to assign it a visual component. If you are rolling your own actor and don’t go the CIwGameActorImage route then you will need to create and assign your own visual component to the actor.

To assign a visual to an actor you would call:

	void		setVisual(CIwGameSprite* visual)

Now when the scene renders the actor it will attenot to render the visual. I want to mention at this pont that as far as IwGame is concerned a visual is an object type that derived from a CIwGameSprite (we will cover this later), but for now we will just say that a sprite as far as IwGame is concerned is anything that can be displayed, be it a simple image or a complex piece of SVG.

And where you find visuals you will usually find some kind of animation. The actor class supports attachment of CIwGameAnimTimeline which is basically a collection of animations (we will cover this in more depth later). To assign a time line we call:

	void		setTimeline(CIwGameAnimTimeline* timeline) { Timeline = timeline; }

Changing an Actors Colour

Each actor has its own independent colour (including opacity). All actors are set to a default colour of white and full opacity. To change the colour of an actor you can call:

	void		setColour(CIwColour& colour)

Note that an actors colour will be combined with its parents base colour.

Obeying Scene Extents

By default an actor would merrily travel across the game scene and beyond its extents into oblivion and out of range coordinates, this can cause a bit of a mess for the underlying math and rendering routines. To prevent actors from going off into oblivion we can tell them to wrap around to the other side of the scene if they hit its extents boundary. To force actors to wrap around at the boundaries of the scene we call setWrapPosition(true):

	void		setWrapPosition(bool enable)
	bool		getWrapPosition() const					{

Actor Layering

We touched on layering earlier when we talking about layering in scenes. All actors within a scene exist (visually) on a layer. The layer determines the order in which the actors are rendered with lower layers appearing below higher layers. The maximum layer that an actor can exist on is determined by the scene that it lives in. To change the layer that an actor appears on and to retrieve its current layer we use:

	void		setLayer(int layer)
	int		getLayer() const

Scene Visibility and Active State

You can query an actors visibility state and set its visibility state using:

	void		setVisible(bool visible)
	bool		isVisible() const

You can query an actors active state and set its active state using:

	void		setActive(bool active)
	bool		isActive() const

Note that when an actor is made inactive it will also become invisible. However making an actor invisible will not make it inactive.

Resetting Actors

Because actors can be part of an object pooling system and may not get re-initialised when re-used, we provide the functionality to reset them to a default state. This allows developers to re-use objects and not worry about the previous state of the object. Just remember to call the underlying CIwGameActor::Reset() method from your own Reset() method to ensure that the actor is completely reset.

Collision Checking

Right now IwGame does not carry out collision checks for you, instead it calls back each actor in the scene after the scene has been updated to give each possible colliding object a chance to check and respond to collisions. To take advantage of this functionality you need to implement the following handler in your derived actor class:

	virtual void	ResolveCollisions() = 0;

A basic actor to actor collision method is included in CIwGameActor to allow actors to test for overlap based on the size set by setCollisionRect();

When a collision does take place, actors can notify each other by calling:

	virtual void	NotifyCollision(CIwGameActor* other) = 0;

Here’s a quick example showing how to use the system:

void ActorPlayer::ResolveCollisions()
{
	// Walk the scenes actors
	for (CIwGameScene::_Iterator it = Scene->begin(); it != Scene->end(); ++it)
	{
		// Only test collision against ball type actors
		if ((*it)->getType() == ActorType_Ball)
		{
			// Check for physical collision
			if (CheckCollision(*it))
			{
				// Notify ourselves that we collided with ball actor
				NotifyCollision(*it);
				// Notify ball actor that we collided with it
				(*it)->NotifyCollision(this);
			}
		}
	}
}
Note that if you are using integrated Box2D then you safely bypass this collision check system.

Creating an Actor from XOML

Actors can be created declaratively using XOML mark-up, making actor creation much easier and more intuitive. Below shows an example of an actor declared using XOML:

        <MyActor Name="Player1" Position="0, 0" Size="100, 100" Angle="45" SrcRect="0, 0, 36, 40" Image="Sprites" Timeline="Player1Intro2" />

The basic actor tag supports many different attributes that determine how an actor is created and how it behaves. A description of these tags are listed below:

  • Name – Name of the scene (string)
  • Style – Style that should be applied to this actor. If a properties that exists in the style is added to the definition then it replaces the property found in the style
  • Type – A numerical type that can be used to identify the type of this actor (integer)
  • Position– Position in the scene (x, y 2d vector)
  • Origin– Origin in the scene, moves the point around which the actor will rotate and scale (x, y 2d vector)
  • Depth – Depth of the actor in 3D (float – larger values move the sprite further away)
  • Velocity – Initial velocity of the actor (x, y 2d vector)
  • VelocityDamping – The amount to dampen velocity each frame (x, y 2d vector)
  • Angle – The orientation of the actor (float)
  • AngularVelocity – The rate at which the orientation of the actor changes (float)
  • AngularVelocityDamping – The amount of rotational velocity damping to apply each frame (float)
  • Scale, ScaleX, ScaleY – The scale of the actor (float)
  • Colour – The initial colour of the actor (r, g, b, a colour)
  • Layer – The scenes visible layer that the actor should appear on (integer)
  • Active – Initial actor active state (boolean)
  • Visible – Initial actor visible state (boolean)
  • HitTest – If true then this actor will receive touch events
  • Collidable – Collidable state of actor (boolean)
  • CollisionSize – The circular size of the actor (float)
  • CollisionRect – The rectangular collision area that the actor covers (x, y, w, h rect)
  • WrapPosition – If true then the actor will wrap at the edges of the canvas (boolean)
  • Timeline – The time line that should be used to animate the actor
  • Box2dMaterial – Sets the physical material type used by the Box2D actor
  • Shape – Box2D fixture shape for the Box2D actor
  • COM – Centre of mass of Box2D body (x, y 2d vector)
  • Sensor – Can be used to set the Box2D actor as a sensor (boolean)
  • CollisionFlags – The Box2D body collision flags (category, mask and group)
  • OnTapped – Tapped event handler
  • OnBeginTouch – Event handler that specifies an actions list to call when the user begins to touch the actor
  • OnEndTouch – Event handler that specifies an actions list to call when the user stops to touching the actor
  • OnTapped – Event handler that specifies an actions list to call when the user taps the actor
  • OnCreate – Event handler that specifies an actions list to call when this actor is created
  • OnDestroy – Event handler that specifies an actions list to call when this actor is destroyed
  • LinkedTo – Name of actor that this actor links to (string)

For actors that are derived from CIwGameActorImage we have the following additional properties:

  • Image – The image that is to be used as the actors visual (string)
  • Size – The on screen visible size of the actor (x, y 2d vector)
  • SrcRect – The position and source of the source rectangle in the image atlas (x, y, w, h rect). Used for panning the portion of a sprite atlas shown allowing frame based animation.
  • FlipX – Horizontal flipped state (boolean)
  • FlipY – Vertical flipped state (boolean)

For actors that are derived from CIwGameActorText we have the following additional properties:

  • Font – Name of font to use to draw the text (string)
  • Rect – The area thuat the text should be drawn inside of (x, y, w, h rect)
  • Text – String to display (string)
  • AlignH – Horizontal alignment (centre, left and right)
  • AlignV – Verticalalignment (middle, top and bottom)
  • Wrap – If true then text is wrapped onto next line if to long (boolean)

Note that unlike scenes you cannot create an Actor or ActorImage directly as their corresponding CIwGameActor and CIwGameActorImage classes are abstract, so you must derive your own actor class. More on this later.

In addition, actors must be declared inside a scene tag element as they must have a parent scene and cannot be declared as resources.

Animating Actor Components

Actors allow an animation time line to be attached to them that animates various properties of the actor. The following properties are currently supported:

  • Position – Actors current position
  • Depth – Actors 3D depth
  • Origin – Actors transform origin
  • Velocity – Actors current velocity
  • Angle – Actors current angle
  • AngularVelocity – Actors current angular velocity
  • Scale, ScaleX, ScaleY – Actors current scale
  • Colour / Color – Scenes current colour
  • Layer – Actors current visible layer
  • Visible – Actors current visible state
  • HitTest – Determines if the actor can be tapped
  • Timeline – The currently playing timeline

For actors that are derived from CIwGameActorImage we have the following additional properties:

  • SrcRect – Actors currebt bitmapped visual source rectangle
  • Size – Actors visible size on screen

Any of these properties can be set as an animation target

Creating a Custom Actor

Whilst CIwGameScene can be instantiated and used as-is, CIwGameActor and CIwGameActorImage are abstract and cannot. The actor system is designed this way as the developer is meant to create their own custom actor types that provide bespoke functionality that is specific to their game.

You begin the creation of a custom actor by deriving your own actor class from either CIwGameActor or CIwGameActorImage then overloading the following methods to provide implementation:

	virtual void		Init();
	virtual bool		Update(float dt);
	virtual bool		UpdateVisual();
	virtual void		ResolveCollisions() = 0;
	virtual void		NotifyCollision(CIwGameActor* other) = 0;

Here’s a quick example:

class MyActor : public CIwGameActor
{
public:
	MyActor() : CIwGameActor() {}
	~MyActor() {}

	void		Init()
	{
		CIwGameActor::Init();
	}

	bool		Update(float dt)
	{
		if (!CIwGameActor::Update(dt))
			return false;

		// Here we put our actor specific implementation

		return true;
	}

	bool		UpdateVisual()
	{
		if (!CIwGameActor::UpdateVisual())
			return false;

		// Here we put our actor specific rendering code (if any is needed)

		return true;
	}

	void		ResolveCollisions() {}
	void		NotifyCollision(CIwGameActor* other) {}
};

We have provided a very basic implementation of Init(), Update() and UpdateVisual() which call the base CIwGameActor class methods so we keep its functionality in-tact.

We also provide a none functional implementation of ResolveCollisions() and NotifyCollision() as these are required methods

You can take the implementation one step further by implementing both the IIwGameXomlResource and IIwGameAnimTarget interfaces to allow instantiation of your custom actor class from XOML and to allow your class to be a target for animation time lines.

Firstly lets take a look at XOML enabling your custom actor class. To get IwGame to recognise your class whilst parsing XOML files you need to do a few things:

  • Derive your class from IIwGameXomlResource and implement the LoadFromXoml method
  • Create a class creator that creates an instance of your class then add this to the XOML engine

Lets start by taking a look at step 1.

Because we have derived our class from CIwGameActor we already have the support for step 1. However we would like to insert our own custom attribute tags so we need to make a few changes.

Lets take a look at our new class with thiose changes:

class MyActor : public CIwGameActor
{
public:
	// Properties
protected:
	int		NumberOfEyes;
public:
	void		setNumberOfEyes(int num_eyes)	{ NumberOfEyes = num_eyes; }
	float		getNumberOfEyes() const		{ return NumberOfEyes; }
	// Properties End
public:
	MyActor() : CIwGameActor() {}
	~MyActor() {}

	void		Init()
	{
		CIwGameActor::Init();
	}

	bool		Update(float dt)
	{
		if (!CIwGameActor::Update(dt))
			return false;

		// Here we put our actor specific implementation

		return true;
	}

	bool		UpdateVisual()
	{
		if (!CIwGameActor::UpdateVisual())
			return false;

		// Here we put our actor specific rendering code (if any is needed)

		return true;
	}

	void		ResolveCollisions() {}
	void		NotifyCollision(CIwGameActor* other) {}

	// Implementation of IIwGameXomlResource interface
	bool		LoadFromXoml(IIwGameXomlResource* parent, bool load_children, CIwGameXmlNode* node)
	{
		if (!CIwGameActor::LoadFromXoml(parent, load_children, node))
			return false;

		// Add our own custom attribute parsing
		for (CIwGameXmlNode::_AttribIterator it = node->attribs_begin(); it != node->attribs_end(); it++)
		{
			unsigned int name_hash = (*it)->getName().getHash();

			if (name_hash == CIwGameString::CalculateHash("NumberOfEyes"))
			{
				setNumberOfEyes((*it)->GetValueAsInt());
			}
		}

		return true;
	}
};

Our new class now basically supports a new NumberOfEyes attribute that we will eventually be able to set in XOML using something like:

    <MyActor Name="AlienCritter" Position="100, 100" Size="100, 100" NumberOfYes="3" />

However, before we can do that we need to let the XOML system know about our new type of class (MyActor), so it can be instantiated when the XOML parser comes across it. To do this we need to create a XOML class creator:

class MyActorCreator : public IIwGameXomlClassCreator
{
public:
	MyActorCreator()
	{
		setClassName("MyActor");
	}
	IIwGameXomlResource* CreateInstance(IIwGameXomlResource* parent) { return new MyActor();	}
};

The creator basically defines the tag name “MyActor” and returns an instance of the MyActor class when CreateInstance() is called.

To get the XOML system to recognise our new creator we need to add it to the XOML parsing system using:

	// Add custom MyActor to XOML system
	IW_GAME_XOML->addClass(new MyActorCreator());

Now XOML integration is out of the way, lets take a quick look at enabling our class as an animation target.

To enable a class as an animation target we derive it from IIwGameAnimTarget and implement the UpdateFromAnimation() method. Luckily we derived our MyActor class from the CIwGameActor class which already provides this functionality.

Lets take a quick look at how we extend the animation update method to account for animating our NumberOfEyes variable.

	bool	UpdateFromAnimation(CIwGameAnimInstance *animation)
	{
		if (CIwGameActor::UpdateFromAnimation(animation))
			return true;

		// Add our own custom animating property
		unsigned int element_name = animation->getTargetPropertyHash();

		if (element_name == CIwGameString::CalculateHash("NumberOfEyes"))
		{
			CIwGameAnimFrameFloat* frame = (CIwGameAnimFrameFloat*)animation->getCurrentData();
			setNumberOfEyes((int)frame->data);
			return true;
		}

		return false;
	}

We added the above code to our MyActor class definition. We begin by calling the base UpdateFromAnimation() method so we can keep the existing animation properties of the actor. We then add our own custom check for the NumberOfEyes variable. If the animation property matches NumberOfEyes then we set the number of eyes to the provided interpolated value.

 

 

Marmalade SDK Tutorial – Actors, Scenes and Cameras Make the World Go Round

This tutorial is part of the Marmalade SDK tutorials collection. To see the tutorials index click here

In our previous tutorial we created an extensible animation system that allowed us to create discrete frame based image animation as well as other types of animations. This week we will continue on our quest to create an easy to use, extensible cross platform 2D game engine. In order to do this effectively we need to get organised and organised we will be. If you just want the code to this tutorial then download it from here.

Ok, so how do we organise things in a 2D engine? What sort of things do we want our 2D engine to do? Here’s a brief list:

  • We want sprites and animations obviously, these we already covered in previous tutorials
  • We want sprite creation / handling to be automated so that we do not have to worry about creating, updating and destroying sprites
  • We want to define specific types of game objects (players, bombs, aliens, pickups etc..) modelled on a single game object type
  • We want scenes that contain our game objects, managing their life times and updates
  • We want cameras that we can move around in our scene to view different parts of the scene.
  • We want our game objects to recognise when they collide with each other and react to each other

To manage all of the above we are going to create a Camera, Actor, Scene (CAS) system.

Cameras, Actors and Scenes

The camera, actor and scene system (CAS) constitutes the logical implementation of our game engine. The scene represents our finite gaming world, whilst actors represent our individual game objects that live within our gaming world. The scene manages all of our actors for us, taking care of updating them and deleting them when they are no longer needed. The camera represents a view into the gaming world that can be moved around, rotated and scaled to view different parts of the world.

CIwGameActor – Our game objects are really just actors on a stage

Ok, we will begin by taking a look at the CIwGameActor class:

class CIwGameActor { protected: // Properties CIwGameScene* Scene; // Scene that actor lives in bool Used; // Used is used when actors pooled to reduce memory fragmentation bool Managed; // Marks this actor as being managed by another object so we do not delete it unsigned int NameHash; // Name of Actor (stored as an hash for speed) int Type; // Type of Actor (use to distinguish beteeen different actor types) CIwFVec2 OriginalPosition; // Original position of actor in the scene (when actor was first spawned) CIwFVec2 Position; // Current position of actor in the scene CIwFVec2 Velocity; // Current velocity of actor CIwFVec2 VelocityDamping; // Dampens the velocity float OriginalAngle; // Original angle in scene (when first spawned) float Angle; // Orientation in scene (degrees) float AngularVelocity; // Angular velocity float AngularVelocityDamping; // Angular velocity damping float Scale; // Scale CIwColour Colour; // Colour bool IsActive; // Active state of actor bool IsVisible; // Visible state of actor bool IsCollidable; // Collidable state of actor CIwGameSprite* Visual; // Visual element that represents the actor CIwGameAnimManager* VisualAnimManager; // Visuals animation manager, used to control the actors visual componen animations int CollisionSize; // Size of collision area CIwRect CollisionRect; // Spherical collision size float PreviousAngle; // Previous updates angle CIwFVec2 PreviousPosition; // Previous updates position public: void setUsed(bool in_use) { Used = in_use; } bool isUsed() const { return Used; } void setManaged(bool managed) { Managed = managed; } bool isManaged() const { return Managed; } void setScene(CIwGameScene* scene) { Scene = scene; } CIwGameScene* getScene() { return Scene; } void setName(const char* name) { NameHash = IwHashString(name); } unsigned int getNameHash() { return NameHash; } void setType(int type) { Type = type; } int getType() const { return Type; } void setOriginalPosition(float x, float y) { OriginalPosition.x = x; OriginalPosition.y = y; } CIwFVec2 getOriginalPosition() { return OriginalPosition; } void setPosition(float x, float y) { Position.x = x; Position.y = y; } CIwFVec2 getPosition() { return Position; } void setOriginalAngle(float angle) { OriginalAngle = angle; } float getOriginalAngle() { return OriginalAngle; } void setAngle(float angle) { Angle = angle; } float getAngle() { return Angle; } void setVelocity(float x, float y) { Velocity.x = x; Velocity.y = y; } CIwFVec2 getVelocity() { return Velocity; } void setVelocityDamping(float x, float y) { VelocityDamping.x = x; VelocityDamping.y = y; } void setAngularVelocity(float velocity) { AngularVelocity = velocity; } float getAngularVelocity() const { return AngularVelocity; } void setAngularVelocityDamping(float damping) { AngularVelocityDamping = damping; } void setScale(float scale) { Scale = scale; } float getScale() const { return Scale; } void setColour(CIwColour& colour) { Colour = colour; } CIwColour getColour() const { return Colour; } void setActive(bool active) { IsActive = active; } bool isActive() const { return IsActive; } void setVisible(bool visible) { IsVisible = visible; } bool isVisible() const { return IsVisible; } void setCollidable(bool collidable) { IsCollidable = collidable; } bool isCollidable() const { return IsCollidable; } void getVisual(CIwGameSprite* visual) { Visual = visual; } CIwGameSprite* getVisual() { return Visual; } void setVisualAnimManager(CIwGameAnimManager* anim_manager) { VisualAnimManager = anim_manager; } CIwGameAnimManager* getVisualAnimManager() { return VisualAnimManager; } void setCollisionRect(CIwRect& rect); CIwRect getCollisionRect() const { return CollisionRect; } int getCollisionSize() const { return CollisionSize; } void setPreviousPosition(float x, float y) { PreviousPosition.x = x; PreviousPosition.y = y; } CIwFVec2 getPreviousPosition() const { return PreviousPosition; } void setPreviousAngle(float angle) { PreviousAngle = angle; } float getPreviousAngle() const { return PreviousAngle; } // Properties end CIwGameActor() : Used(false), Managed(false), Scene(NULL) { Reset(); } virtual ~CIwGameActor(); // Reset the actor (used by memory pooling systems to save actor re-allocation, usually called after re-allocation to reset the object to a default state) virtual void Reset(); // Update the actor (called by the scene every frame) virtual bool Update(float dt); // Update the visual that represents this actor on screen virtual bool UpdateVisual(); // Actors are responsible for carrying out there own collision checks. Called after all actor updates to check and resolve any collisions virtual void ResolveCollisions() = 0; // NotifyCollision is called by another actor when it collides with this actor virtual void NotifyCollision(CIwGameActor* other) = 0; // Call to see if this actor was tapped by the user virtual bool CheckIfTapped(); // Checks to see if another actor is colliding with this actor bool CheckCollision(CIwGameActor* other); };

Hmm, I agree, its a bit on the BIG side, but if you look carefully our CIwGameActor class provides a lot of functionality built in, which in the long run will save us lots of coding time. Again, you never create a direct instance of this class as it is abstract, you dervice your own different actor types from CIwGameActor.

We handle a lot of functionality with this base class including:

  • Position, orientation, scale, angular velocity, velocity damping and angular velocity damping – Physical attributes of our actor
  • Visibility state, active state, collide-able state – Used to hide, disable and mark as collide-able objects
  • Object type identifier, object name (used to search for specific types of objects or named objects). These are very useful as they allow you to create actors and forget about them, no need to store a pointer to them to reference them later, you simply ask the scene to find the object by name or type to access it again.
  • Visual, colour and animation manager – We will use these variables to give our object a visual representation in our world
  • Collision size and collision rectangle are used for collision detection

Note that eventually we will be replacing the physical components in this class with the Box2D physics engine.

I would like to point out a few important methods in this class:

  • Update() – When you implement your own actor types you override this method to provide the implementation of your game object specific behaviour. For example, if you create a player actor then you may check and move the player, maybe fire of different animations / sound effects. The default implementation of Update() will update the basic physics for the actor, any attached playing animations as well as add the actor to the collision check list if it is collision enabled. You should call CIwGameActor::Update() in your own Update() implementation, if you want to keep this functionality.
  • UpdateVisual() – You do not generally need to override and provide your own implementation of this method as this method will automatically update the actors associated visual for you.
  • ResolveCollisions() – When the scene has finished calling all of its actor updates it will then call ResolveCollisions() on each of the actors. You provide the implementation of this method to check for collisions with other actors. We implement collision checking this way as it allows us to optimise which collision checks to make. For example, lets say we are creating the old game Asteroids. The scene consists of 10 asteroids, 10 bullets and our ship. Our ship actor only needs to check for collisions with the asteroids and not the bullets or the ship.
  • NotifyCollision() – When an actor collides with another actor we need some mechanism for letting the other actor know that we have collided with them. When an actor collides with this actor it will call its NotifyCollision() method to let it know, this gives the actor a chance to respond to the collision event
  • CheckIfTapped() – Whilst this method is not currently implemented it will eventually allow us to check and see if the actor was tapped by the user. This is good for allowing the user to interact with our actors
  • CheckCollision() – Helper method for checking if two actors bounding circles overlap

One important note about Actors is there coordinate system. As far as our scene is concerned, the worlds centre is the the middle of the scene (0, 0), which corresponds to the centre of the screen for a scene that has not been moved.

CIwGameActorImage – An image based actor helper class

I suspect that many of you will want to simply get up and running quickly with your game and not have to worry about deriving your own actor class. With that in mind I created an image based actor that allows you to quickly set up a basic image based actor, complete with a sprite atlas, a base animation and a size. Lets take a quick look at the code in that class as I thin it will prove helpful when it comes to you designing your own actors:

bool CIwGameActorImage::Init(CIwGameScene* scene, CIw2DImage* image, CIwGameAnimImage* anim, int width, int height) { // Reset the actor CIwGameActor::Reset(); // Set the scene Scene = scene; // Create sprite if (image != NULL) { CIwGameBitmapSprite* sprite = new CIwGameBitmapSprite(); if (sprite == NULL) return false; // Set sprite image sprite->setImage(image); sprite->setDestSize(width, height); // Set sprite as visual Visual = sprite; // Add sprite to the sprite manager so it can be managed and drawn Scene->getSpriteManager()->addSprite(sprite); } // Create an animation manager and add the animation to it if (anim != NULL) { VisualAnimManager = new CIwGameAnimManager(); VisualAnimManager->setUpdateAll(false); if (VisualAnimManager == NULL) return false; VisualAnimManager->addAnimation(anim); // Set the first animation in the animation manager as the current animation VisualAnimManager->setCurrentAnimation(0); } return true; }

Our Init() method is pretty simple, it resets the actors internal data to default, creates a bitmapped sprite visual from the image and actor size then creates an animation manager and adds our default animation to it.

bool CIwGameActorImage::UpdateVisual() { if (CIwGameActor::UpdateVisual()) { // Update the sprite if (VisualAnimManager != NULL) { // Get the animations current image frame data and copy it to the bitmapped sprite CIwGameAnimImage* anim = (CIwGameAnimImage*)VisualAnimManager->getAnimation(0); if (Visual != NULL) ((CIwGameBitmapSprite*)Visual)->setSrcRect(anim->getCurrentFrameData()); } return true; } return false; }

Our UpdateVisual() method simply moves the image animation frame data to our sprite to update its image.

Unfortunately you will still need to derive your own actor from CIwGameActorImage() in order to create an actor object

CIwGameScene – A place for actors to play

The scene is the place where we put actors. When you add an actor to a scene the scene will take care of calling game logic update and visual update methods and building a list of potential colliding actors, as well as cleaning up the actors when the game is finished.
The scene also takes care of updating the camera and fitting our game across different sized screens with different aspect ratios. Look as the scene as the driving force that manages much of our game processes for us, so we can get on with coding up cool actors and other game logic.Lets take a quick look at the CIwGameScene class:

class CIwGameScene { public: // Public access to actor iteration typedef CIwList::iterator _Iterator; _Iterator begin() { return Actors.begin(); } _Iterator end() { return Actors.end(); } // Properties protected: CIwGameSpriteManager* SpriteManager; // Manages sprites for the whole scene CIwGameAnimFrameManager* AnimFrameManager; // Manages the allocation and clean up of animation frames unsigned int NameHash; // Hashed name of this scene CIwVec2 ScreenSize; // Native screen size CIwVec2 VirtualSize; // The virtual size is not the actual size of the scene. but a static pretend size that we can use to render to without having to cater for different sized displays CIwMat2D VirtualTransform; // Virtual transform is used to scale, translate and rotate scene to fit different display sizes and orientations CIwMat2D Transform; // Scene transform CIwList Actors; // Collection of scene actors CIwRect Extents; // Extents of scenes world CIwGameCamera* Camera; // Current camera CIwGameActor** Collidables; // List of collidable objects built this frame int MaxCollidables; // Maximum allowed collidables int NextFreeCollidable; // Points to next free slot in sollidables list pool public: CIwGameSpriteManager* getSpriteManager() { return SpriteManager; } // Manages sprites for the whole scene CIwGameAnimFrameManager* getAnimFrameManager() { return AnimFrameManager; } // Manages the creation and clean up of animation frames void setName(const char* name) { NameHash = IwHashString(name); } unsigned int getNameHash() { return NameHash; } CIwVec2 getScreenSize() const { return ScreenSize; } CIwVec2 getVirtualSize() const { return VirtualSize; } void setVirtualTransform(int required_width, int required_height, float angle, bool fix_aspect = false, bool lock_width = false); CIwMat2D& getVirtualTransform() { return VirtualTransform; } CIwMat2D& getTransform() { return Transform; } void addActor(CIwGameActor *actor); void removeActor(CIwGameActor* actor); void removeActor(unsigned int name_hash); CIwGameActor* findActor(unsigned int name_hash); CIwGameActor* findActor(int type); void clearActors(); void setExtents(int x, int y, int w, int h) { Extents.x = x; Extents.y = y; Extents.w = w; Extents.h = h; } CIwRect getExtents() const { return Extents; } void setCamera(CIwGameCamera* camera) { Camera = camera; } CIwGameCamera* getCamera() { return Camera; } bool addCollideable(CIwGameActor* actor); CIwGameActor** getCollidables() { return Collidables; } int getTotalCollidables() const { return NextFreeCollidable; } // Properties end private: public: CIwGameScene() : Collidables(NULL), SpriteManager(NULL), AnimFrameManager(NULL), NextFreeCollidable(0), Camera(NULL), MaxCollidables(0) {} virtual ~CIwGameScene(); // After creating the scene, call Init() to initialise it, passing the maximum number of actors that you expect can collide virtual int Init(int max_collidables = 128); // Update() will update the scene and all of its contained actors virtual void Update(float dt); // Draw() will draw all of the scenes actors virtual void Draw(); // Event handlers };

Yep, I know, its another biggy, but again it supports lots of cool functionality such as:

  • Handles all of our actors
  • Handles our camera
  • Fits our game to any sized screen / any aspect ratio using a virtual size display
  • Tracks potential colliders
  • Manages sprites and animation frames

You will be happy to know that you do not need to derive you own scene from CIwGameScene() and you will generally instantiate and work with a version of this class directly. Our scene class does not however need a little setting up initially. Heres asome basic code on how to set up a scene:

CIwGameScene* game_scene = new CIwGameScene(); game_scene->Init(); game_scene->setVirtualTransform(VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT, 0, true, false);

I think at this point I need to explain something about the virtual screen system that I use to ease the pain of cross platform development

Using a Virtual Screen Size to Target Any Sized Screen

There are quite a few different solutions knocking around that solve the problem of targeting our game at a variety of different screen resolutions, including:

  • Scale to fit – This is a very simple scaling of the scene to match the display resolution. This is quick and simple but your game can appear stretched or squashed on displays that have a different aspect ratio to your game aspect ratio
  • Bordered – Another simple system that displays your game at its native resolution but with a border around it to fill the space that your game doesn’t cover. I don’t like this method as you are giving up too much screen real-estate
  • Unlimited screen size – This method is quite complex and involves rendering enough stuff on screen at a 1:1 pixel resolution to cover the entire screen. This disadvantage (and advantage, depends how much time you have on your hands) of using this method is that you would need to display a much larger area of the gaming world on a higher resolution display than on a lower resolution display.
  • Virtual screen – This method uses a pretend screen size that best fits many resolutions (800 x 512 / 512 x 800 is a good choice). Your game renders everything as though it is rendering to the virtual size and not the actual phones / tablets native screen size. You later scale and translate the virtual canvas to fit onto the native phones screen resolution.

We could use any of these methods in our game, but I am going to use a virtual canvas because it is the most convenient. Our CIwGameScene class has a method called setVirtualTransform() which will set this up for us, so that all of our actors will render to the virtual screen size. Heres how to use the method:

void CIwGameScene::setVirtualTransform(int required_width, int required_height, float angle, bool fix_aspect, bool lock_width)

  • required_width, required_height – This is the width and height we would like to use for our virtual screen
  • fix_aspect – Tells the scene to fix the aspect ratio of the scene to match the native screen aspect ratio
  • lock_width – Tells the scene to fix eth aspect ratio based on the width of the display instead of the height

CIwGameCamera – Our View Into the Gaming World

Many games can get away with simply one visible screen of information (such as Asteroids, Space Invaders, Pac-man etc..), but other types of games such as platformers, sports games, strategy games etc.. require the ability to move around the gaming world in some fashion. This is usually accomplished by using a camera that can move our point of view within the world. CIwGameScene supports the attachment of a camera to allow us to move our view around a larger virtual world. Lets take a quick look at the CIwGameCamera class:

class CIwGameCamera { public: // Properties protected: unsigned int NameHash; // Hashed name of this camera CIwMat2D Transform; // The combined camera transform CIwFVec2 Position; // Position of view within scene float Scale; // Cameras scale float Angle; // Cameras angle bool TransformDirty; // Marks camera transform needs rebuilding public: void setName(const char* name) { NameHash = IwHashString(name); } unsigned int getNameHash() { return NameHash; } CIwMat2D& getTransform() { return Transform; } void setPosition(float x, float y) { Position.x = x; Position.y = y; TransformDirty = true; } CIwFVec2 getPosition() const { return Position; } void setScale(float scale) { Scale = scale; TransformDirty = true; } float getScale() const { return Scale; } void setAngle(float angle) { Angle = angle; TransformDirty = true; } float getAngle() const { return Angle; } void forceTransformDirty() { TransformDirty = true; } bool isTransformDirty() const { return TransformDirty; } // Properties end private: public: CIwGameCamera() : Position(0, 0), Scale(1.0f), Angle(0), TransformDirty(true) {} virtual ~CIwGameCamera() {} // Updates the camera virtual void Update(); // Event handlers };

Ah much better, nice and short. As you can see the camera class is quite simple, supporting position, rotation and scaling of the view. To use a camera we simply create one and attach it to the Scene, the scene will then follow the camera around. To see different areas of the game world we simply move the camera around and all of our actors will move along with it.

Cool, I’m now done with explaining the new classes. I’ve been battling with trying to keep this article short and to the point, but alas I don’t think its quite happening for me.

What’s changed in IwGame Code

Marmalade SDK - Actor, Scene Camera Example
Marmalade SDK - Actor, Scene Camera Example


I will be honest, A LOT has changed since the previous article and I will try my best to walk through most of it; this is the main problem with an engine that’s  in-development.

Firstly I have had to make quite a few changes to our previous classes including:

  • CIwGameAnim – CIwGameAnimFrameManager now handles the life time of animation frames and not the separate animation classes themselves
  • CIwGameAnimFrameManager – Added the ability to retrieve allocated animation frames
  • CIwGameAnimManager – setCurrentAnimation() never actually set the current animation (oops, fixed)
  • CIwGameBitmapSprite – Source rectangle can now be set from a CIwGameAnimImageFrame (helper method just saves some typing)
  • CInput – This class has been renamed to CIwGameInput to fit in with the frameworks naming conventions. CIwGameInput is now a singleton and not declared as a global variable (I’m not generally a fan of global variables and prefer to use singletons for systems that there will only ever be one instance of). If you do not know what a singleton is then think of it as a global instance of a class.

Now onto the changes to Main.cpp. Note that I won’t be going into minor details such as “Oh I included these header files”.

We now accesss input using a singleton, here we have to create the IwGameInput singleton and then initialise it:

// Initialise the input system CIwGameInput::Create(); IW_GAME_INPUT->Init();

Note that IW_GAME_INPUT is just a macro that calls CIwGameInput::getInstance() (I find it more readable to use the macro)

Next, we create and initialise a scene then create a camera and attach that to the scene

// Create a scene CIwGameScene* game_scene = new CIwGameScene(); game_scene->Init(); game_scene->setVirtualTransform(VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT, 0, true, false); // Create a camera and attach it to the scene CIwGameCamera* game_camera = new CIwGameCamera(); game_scene->setCamera(game_camera);

Next, we allocate a bunch of image animation frames for our manic face animation. Take note that we now allocate them through the game scenes animation manager. This ensures that the game scene later cleans them up for us.

// Allocate animation frames for our player CIwGameAnimImageFrame* anim_frames = game_scene->getAnimFrameManager()->allocImageFrames(8, 36, 40, 0, 0, 512, 40, 512);

Within our main loop we check for the player tapping the screen and if they do we explode 10 sprites into the scene at the tapped position:

if (IW_GAME_INPUT->getTouchCount() > 0) { if (!PrevTouched) { // Get tapped position in our virtual screen space CIwFVec2 pos = game_scene->ScreenToVirtual(IW_GAME_INPUT->getTouch(0)->x, IW_GAME_INPUT->getTouch(0)->y); // Create 10 player actors for (int t = 0; t < 10; t++) { // 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 player actor ActorPlayer* player = new ActorPlayer(); player->Init(game_scene, sprites_image, face_anim, 36, 40); player->setName("Player"); player->setPosition(pos.x, pos.y); // Add player actor to the scene game_scene->addActor(player); } } PrevTouched = true; } else PrevTouched = false;

Note that because we are now dealing with a virtual screen resolution and not the actual native screen resolution, our tap coordinates need converting to virtual screen coordinates. We achieve that using the ScreenToVirtual() method of the CIwGameScene class.

Also note that we still have to create our face_anim animation, but this time we pass it to the ActorPlayer Init() method, so that the actor / scene can take of its management.

Next we update and draw the scene:

// Update the scene game_scene->Update(1.0f); // Draw the scene game_scene->Draw();

Lastly we clean-up the camera, scene and input system:

// Safely clean up the camera if (game_camera != NULL) delete game_camera; // Safely cleanup game scene if (game_scene != NULL) delete game_scene; // Shut down the input system IW_GAME_INPUT->Release(); CIwGameInput::Destroy();

ActorPlayer our First Derived Actor

ActorPlayer is our very first user defined CIwGameActor based actor and because we derived it from CIwGameActorImage (and in turn CIwGameActor) we get all of the useful functionality defined in those classes.

The only parts of our ActorPlayer that’s worth drawing out are the Init() and Update() methods:

bool ActorPlayer::Init(CIwGameScene* scene, CIw2DImage* image, CIwGameAnimImage* anim, int width, int height) { CIwGameActorImage::Init(scene, image, anim, width, height); FadeTimer.setDuration(1000); Velocity.x = IwRandMinMax(-100, 100) / 20.0f; Velocity.y = IwRandMinMax(-100, 100) / 20.0f; AngularVelocity = IwRandMinMax(-100, 100) / 20.0f; return true; }

Our Init() method calls the base CIwGameActorImage::Init() method to initialise the base CIwGameActorImage part of the actor. We then set up a fade timer and random velocities for position and spin.

bool ActorPlayer::Update(float dt) { // If fade timer has timed out then delete this actor if (FadeTimer.HasTimedOut()) { return false; // Returning false tells the scene that we need to be removed from the scene } // Calculate our opacity from time left on fade timer int opacity = FadeTimer.GetTimeLeft() / 2; if (opacity > 255) opacity = 255; Colour.a = opacity; return CIwGameActorImage::Update(dt); }

Our Update() method is called every game loop by the scene system, so keep in mind that this code will be called every single game frame. Firstly we check to see if the fade timer has timed out and if it has then we return false to the the scene system (this causes the actor to be removed from the scene / game) after the scene has finished updating all actors.

We then calculate the opacity of our actor from the time left on the fade timer which causes the actor to fade out of existence.

And that’s about it for this article, hope that most of your managed to stay awake, long drawn out technical stuff can send the best of us to sleep, especially if read on a few hours sleep, which is usually the case for us programmer types. The source code that accompanies this article can be downloaded from here

I’m quite happy with this blog as it marks the start of a real usable cross platform game engine that we can now build upon. In our next tutorial we are going to cover upgrading the engine to include some very basic collision detection and response whilst building a small game using the engine to show working collision. We will also cover handling frame rate variations and putting some audio in there. Hopefully I will get the time to do that this weekend.

That’s it for now and don’t forget HTML 5 is evil! (Just kidding, I got a book on it the other day and it looks quite good, think that I may use it to spruce up my blog)