Submit to StumbleUpon Share

Hello and welcome back to my blog!

This time I'm going to try something new. There are many tutorials for various techniques and tricks on the web, but what you don't often see is one that takes you through the development of a game, step by step from start to finish.

This is what I'm going to attempt here. I hope that it will provide some missing insight into the components that actually make up a working game and how to go about developing them.

I'm going to assume the reader is familiar with the basics of game development and I'm going to concentrate on the art and programming. At the end of this tutorial you will be able create a demo similar to this:

Angry Birds

So, the game I'm going to be making is to be based on the extremely popular Angry Birds by Rovio, a AAA title which cost some $140k USD to make.

Angry Birds

Obviously, since its just me making this I will have to take a few short-cuts and will be concentrating on the core part of the game.

Cloning

Before I start I should mention that I do not condone the cloning of games in any way; what I'm doing in these tutorials is purely for educational purposes and I have no plans to release the game at the end of this.

Analysis

Ok, lets have a look at the requirements for a bare-bones version of the game:

Graphics

  • Background, mid-ground and foreground layers
  • Bird characters,
  • Pig characters,
  • Slingshot,
  • Rigid body pieces - materials: wood, stone, glass in rectangle, square and triangle shapes

Code

Camera

  • Panning
  • Parallax
  • Zoom
  • Object tracking

Collision detection

  • Possible requirement for advanced broad-phase
  • Various static and dynamic shapes - rectangles, triangles, circles
  • Object colliding call-back system

Physics

  • Very stable physics engine
  • Integrated object sleeping system

Editor

  • Some kind of editor to allow layout of levels and creation of graphics. I'm using Flash CS4 for all this.

The Beginning

The first thing I'm going to tackle from that list is to get the world set up so that there is a nice looking environment and to root the project in something solid looking.

I started with the podium that the birds gets launched from; its going to be a static object in the physics system and to keep things simple I'm going to compose all objects from primitive parts, so collision wise at least the podium will be made from some squares and triangles.

Podium

As you can see, it's covered with a repeating earth texture, so the first thing is to make this. Its actually quite easy to do:

Process

The first part is easy, just pick two vaguely earthy colours and paint one half of the texture in each colour, with a nice rough overlap in the midddle.

Then, offset the image by half the width and height (I used photoshop's offset filter) to get the image in the middle. Then you can rough up the middle edge as you did in the first image. Once you're done you'll have a nice infinitely repeatable texture.

In order to actually apply this texture to an arbitrary object in Flash, you import the image onto the stage, then right click and and choose 'Break apart'. Then you can use the eye-dropper tool to pick that texture as a fill texture which you can then apply to any shape with the paint bucket.

Infinite repeats

Because we're making all collision objects from primitive shapes, we need to decide what basic shapes we're going to be using. I chose these for the podium:

Podium shapes

These can be rotated, duplicated and placed to form the podium shape:

Podium composed

So, when actually creating the shape for the visible part of the podium, its important to follow the exact same shape that you could compose only using the primitive collision shapes in various different configurations.

This will start to become very useful later when we try to integrate the physics system with the graphics.

Foreground

The foreground consists of another tiling shape; the soil. This was made from a rectangle, and lots of ovals in Flash, just repeated and placed. Its important to make sure it tiles so pay attention at the edges and use grid snap.

Foreground

Mid-ground

Yet another tiling shape, this time rolling hills with a few far off plants; nothing out of the ordinary here, just make sure it tiles. You'll want to make this one wider than the screen - I made mine roughly twice as wide:

Mid-ground

Background

Yes, you guessed it, tiling shapes again - this one is roughly one screen in size:

Background

Exporting

All these layers have been set to export for action-script in the Flash IDE; I've appended every name with Fla so that I can tell in code which classes are from Flash and which are my own.

Everything should now be exported as a .swc which you can enable in File->Publish Settings->Flash->Export SWC.

Then you can import this SWC into your project in your favourite flash code compiler; I'm using Amythyst because I cannot live without Visual Studio.

Putting it together

Of course, the idea with all these separate layers is that the code will place and animate them separately to give a parallax effect as the camera pans/zooms around the world.

Dimensions

I picked a screen size of 640x360 (wide-screen), and a world size of 2560x720 (four screens wide by two screens high). And I've centred the world at 0,0.

The Camera

Having a good camera class is fundamental to any game, particularly this one which requires lots of smooth pans and zooms.

At the very least it should provide functions for converting coordinates between world space and screen space and visa versa.

Because there are three layers of parallax at work, the camera needs to be able to position each one as it pans around the scene, so its constructor takes them as parameters:

public function Camera(background:MovieClip, midground:MovieClip, foreground:MovieClip, bird:Bird)
{
...
}

The bird parameter is just a dummy at the moment which represents the focal point for the camera - it contains accessors for position so the camera can know where in the world it is.

In order to get the camera to centre on the bird no matter where the bird is on screen we need to do a little bit of maths; forming what is known as the world to screen matrix, so called because it transforms points in world space into screen space.

public function Update( dt:Number ) : void
{
	m_worldToScreen = new Matrix();
 
	m_worldToScreen.translate( -m_bird.m_Pos.x, -m_bird.m_Pos.m_y );
	m_worldToScreen.scale( m_scale.m_x, m_scale.m_y );
	m_worldToScreen.translate( Constants.kScreenDimensions.m_x/2, Constants.kScreenDimensions.m_y/2 );
 
	m_foreground.transform.matrix = m_worldToScreen;
 
	// for screen->world matrix
	m_screenToWorld = m_worldToScreen.clone();
	m_screenToWorld.invert();
}

What's going on in the above function is: first the camera is centred on the bird, then any zoom is applied and finally we add on the centre of the screen to make sure the bird is in the centre (remember, 0,0 is the top left of the screen, not the centre).

We then take this matrix and apply it as the transform for the foreground geometry, so in actual fact the geometry moves around the camera, not the other way around - which is a bit difficult to get your head around at first.

The first translate in the above snippet is easier to understand with this in mind - 0,0 is the destination for the world geometry, coming from the position of the bird and 0-m_bird.m_Pos is the vector which achieves that.

Then I take a copy of this matrix and invert it so that I can do the opposite transform whenever I need to convert a point in screen-space to world-space.

To get the parallax effect on the back and mid-ground layers, I do a similar piece of maths for each layer (each layer getting its own world->screen matrix), the only change is that I divide the x translation by the layer's z-depth, which causes them to move at different speeds.

All this will work fine, but it won't prevent the camera from leaving the bounds of the world. In order to do that we need to understand the camera's relationship with the world.

So, lets take a look at the world, the camera and their relationship:

Figure 1

Figure 1 shows the entire extents of the world and also one possible location for the camera. Note that the camera has the dimensions of the screen.

In this configuration, the camera is actually showing a view which is partially outside the world, which should not be allowed to happen. In order to fix this problem we need to know exactly how far outside the camera is.

Figure 2

Figure 2 shows the measurements we need to correct this problem (the red arrows) and also shows in green, the camera's position after it has been corrected.

public function Update( dt:Number ) : void
{
	//
	// clamp camera to only show map
	//
 
	var translate:Vector2 = m_bird.m_Pos.m_Neg;
 
	var screenHalfExtents:Vector2 = new Vector2(Constants.kScreenDimensions.m_x/2, Constants.kScreenDimensions.m_y/2).Div(new Vector2(m_scale.m_x, m_scale.m_y));
	var mapExtents:Vector2 = Constants.kWorldAabb.m_HalfExtents.MulScalar( 2 );
 
	var topLeft:Vector2 = m_bird.m_Pos.Sub(screenHalfExtents);
	var bottomRight:Vector2 = m_bird.m_Pos.Add(screenHalfExtents);
 
	var correctLeft:Number = Math.min(topLeft.m_x+Constants.kWorldAabb.m_HalfExtents.m_x, 0);
	var correctTop:Number = Math.min(topLeft.m_y+Constants.kWorldAabb.m_HalfExtents.m_y, 0);
 
	var correctRight:Number = Math.min(Constants.kWorldAabb.m_HalfExtents.m_x-bottomRight.m_x, 0);
	var correctBottom:Number = Math.min(Constants.kWorldAabb.m_HalfExtents.m_y-bottomRight.m_y, 0);
 
	translate.m_x += correctLeft - correctRight;
	translate.m_y += correctTop - correctBottom;
 
        ...
}

The above is the code which calculates the red arrowed regions shown in Figure 2, and applies any corrections needed to the initial translation of the camera.

There is one caveat to watch out for: the foreground layer is a child of the main MovieClip which makes up the game - this is essential because otherwise the camera would only be translating the foreground shape and not everything in the world, which would be all bad. However, because the camera is translating the main MovieClip, the mid and background layers cannot be children of it. Instead, they must be children of the stage; this allows them to be translated independently to give the correct effect.

Create a tile strip

This is relatively simple, you just divide the width of the world by the width of a tile to get the number of repetitions, set the starting point and then just instance the correct MovieClip:

private function CreateTileStrip( start:Vector2, type:Class ):MovieClip
{
	var obj:* = new type();
	var mc:MovieClip = MovieClip( obj );
	var tileRoot:MovieClip = new MovieClip( );
 
	var worldWidth:Number = Constants.kWorldAabb.m_HalfExtents.m_x*2;
	var numTiles:int = (worldWidth/mc.width) + 1;
 
	for ( var i:int = 0; i<numTiles; i++)
	{
		var x:Number = i*(mc.width-1);
 
		mc.x = x + start.m_x;
		mc.y = start.m_y-mc.height;
 
		tileRoot.addChild( mc );
 
		obj = new type();
		mc = MovieClip( obj );
	}
 
	return tileRoot;
}

And you call it like this:

m_backgroundLayer = CreateTileStrip( bottomLeft, BackgroundTileFla );
m_midgroundLayer = CreateTileStrip( bottomLeft, MidgroundTileFla );
m_foregroundLayer = CreateTileStrip( bottomLeft, ForegroundTileFla );

I was actually pleasantly surprised how easy it was to pass a class as a type in actionscript, much easier than in c# or c++ and it really makes it simple to tile any shape that you export from the Flash IDE.

The demo

Ok, so here is the demo so far:

It demonstrates a continuous loop of zooming and panning, three layers of parallax and will not allow the camera to move outside of the world.

The source

As ever, please if you like this article buy the source code (and in this case assets as well) so that I can produce more of this series; your contribution makes a real difference!

It will include all the code which produced the demo above and in addition, all the artwork as well, which you will need Flash CS4 to edit. You are free to use this however you like, even in commercial applications - the only thing I ask is that you don't give it wholesale to anyone else.

After purchasing, you will be redirected to a page where you can download the source immediately.

USD 4.99

Subscribers can access the source here

Continue reading part 2 here

Have fun,

Cheers, Paul.

Submit to StumbleUpon Share