about
history
syntax
visu
arcade
goodies
wrappers
efficiency
examples
experiments
tabageos
tutorials
actiontad logo white
Simple Platform Game Basics Part 1
Understanding the SimplePlatformEngine and BasicGameObjects.


All basicGameObjects extend a class called LoopEventSubscriber.
LoopEventSubscriber does what its name implies. It subscribes the object into a loop.
The loop is a global loop that is being dispatched from a global EventDispatcher.
The first key to efficiency for basic game objects is that they all loop off of this singular global loop.

Although they are looping from the same source, each basicGameObject has a looperDelay property which acts kind of like a frame rate allowing each bgo to loop at different speeds.

The end of this tutorial goes over LoopEventSubscribers more.

All that needs to be known now is that all basic game objects will automatically be ready to tap into this global loop simply by adding them to an engine.

For this intro to basicGameObjects, we'll go over making a basic platform game.
We'll start with just a square moving around, and then in parts 2 and 3 we'll add a body to the square and the ability to fire bullets. In part 4 we'll go over the basics of a more complete platform game.


The SimplePlatformEngine -

The job of any engine is to cause the main LoopEvent to happen and to calculate which basic game objects are hitting each other and then dispatch HitEvents on them.

Therefore engines contain an ILoopDispatchController and have methods for advanced hit testing.

There are a few different engines in the basicGameObject package.
The main ones are the basicEngine, the pixelPerfectEngine, the tinkBasedEngine, and the SimplePlatformEngine.

- BasicGameObject naming convetions: If a class starts with a lowercase letter it means it is a basic class.
Since SimplePlatformEngine starts upper case, it should alert you that it is more complex.


The SimplePlatformEngine expects to have a mainChar, and has a container.
The mainChar must be at least a basicUserControlledObject.
In the example below, a SimplePlatformWalker is used,
which is a subclass of basicUserControlledObject (amongst others).
SimplePlatformWalkers have methods that determine if they are on the ground or not, and if they should be falling or not, and the basic functionality that a platform walker needs.
The way a SimplePlatformWalkers behavior is determined is based on HitEvents.

HitEvents only happen in the container of a SimplePlatformEngine.
Other engines do not have a container, and simply hit test amongst all their children.
For a SimplePlatformEngine, you must add bgos you want to cause hits inside of the container.
The container also follows the mainChar. Anything you add to the container will be viewed based on the location of the mainChar.

In all engines only BasicGameObjects can cause hits.
If you add just a plain LoopEventSubscriber to an engine, it will loop, but not cause HitEvents to happen.
We will make use of this functionality in parts 2 and 3 to create a body for our player that can be any shape or size.
And to create more graphically rich land graphics that do not need to be hit tested.

In the code below what you'll see is just the basic outline of a platform game.
The idea is that an invisible rectangle causes HitEvents, and a visible body that does not cause HitEvents follows the invisible rectangle.
The code below just creates the mainChar rectangle and a basic enemy rectangle that interact with their surroundings.

We can think of the mainChar rectangle as a 'ghost' and the body would follow, and be on top of, the 'ghost'.
In this example, the ghost is the red rectangle that we can see.
In parts 2, 3 and 4 you'll see different ways to add a body to the invisible rectangle.

On to the code!
package Tutorials
{
	import com.actiontad.basicGameObjects.SimplePlatformEngine;
	import com.actiontad.basicGameObjects.basicGameObjectExtension;
	import com.actiontad.basicGameObjects.SimplePlatformWalker;
	import com.actiontad.basicGameObjects.ComputerPlatformWalker;
	import com.actiontad.basicGameEvents.GameObjectEvents;
	import com.actiontad.basicGameEvents.HitEvent;
	import com.actiontad.gameUtils.GameMath;
	
	import flash.events.Event;


	/**
	*  You'll notice that there are no methods in this class for things like loops, or key events.
	*  All those things are already set up in the other classes used.
	*/
	[SWF(frameRate = '30', backgroundColor = '0xc8c8c8', width = '650', height = '350')]
	public class simplePlatformGameSetup extends SimplePlatformEngine
	{

		private var theMainChar:SimplePlatformWalker;
		private var theLand:basicGameObjectExtension;
		private var simpleEnemy:ComputerPlatformWalker;

		private var enemyDirection:int = 1;
		private var deathColor:int = 0;//enemys deathColor

		public function simplePlatformGameSetup() {
			super();

			//a basicGameObjectExtension is what to use if you just need a blank BasicGameObject
			theLand = new basicGameObjectExtension();
			//remember that only basic game objects cause HitEvents, 
			//so the land must be a basic game object so that it can be landed on.
			
			//draw area to be around theMainChar
			with (theLand.graphics) {
				lineStyle(25, 0x0000FF, 1, false, "normal", null, null, 1);
				drawRect(0, 0, 1500, 350);
			}
			//draw a hump in area
			with (theLand.graphics) {
				moveTo(700, 350);
				lineTo(700, 250);
				lineTo(600, 250);
				lineTo(600, 350);
			}
			
			//add things that need to scroll to the container, including the mainChar and enemies
			container.addChild(theLand);
			
			//create the mainChar, it must derive from basicUserControlledObject
			theMainChar = new SimplePlatformWalker();

			//In this example we will show the rectangle that is used for the mainChar.
			//In a more complete game, this rectangle will be invisible and 
			//the body we want for the main character will follow on top of it.
			with (theMainChar.graphics) {
				beginFill(0xFF0000);
				drawRect(0, 0, 20, 55);//the height should be greator than the width.
			}

			theMainChar.x = 55;
			theMainChar.y = 25;
			theMainChar.gravity = 1.5;
			//too much speed or friction and the char may go through walls if going fast enough.
			theMainChar.friction = .85;
			theMainChar.speed = 1.5;//3 is fast

			container.addChild(theMainChar);

			simpleEnemy = new ComputerPlatformWalker();

			with (simpleEnemy.graphics) {
				beginFill(0x000000);
				drawRect(0, 0, 10, 25);
			}
			simpleEnemy.x = 680;
			simpleEnemy.y = 25;
			simpleEnemy.speed = 1;
			//HitEvents happen because we are in an engine
			simpleEnemy.addEventListener(HitEvent.HIT, enemyHitReaction);
			simpleEnemy.addEventListener(GameObjectEvents.COMPUTER_THINKING, walkBackAndForth);
			//ComputerPlatformWalkers extends CCOUnderGravity and they dispatch this event if you add a listener for it,
			//it happens on a separate loop controlled by a thoughtProcess LoopEventDispatcher.

			container.addChild(simpleEnemy);
			
		}
		/* //Example of repositioning and altering the view of the game
		override public function subscribe(e:Event = null):void {
			super.subscribe(e);
			this.x = stageRect.x = (650/2) - (300/2);
			stageRect.width = 300;
			stageRect = stageRect;
		} */
		
		/**
		 *  The enemy will slowly turn red then die, after the mainChar hits it.
		 * 
		 * @param	e A HitEvent, it contains a hitSpecs Object which has 2 properties.
		 *  		hitRect is the Rectangle of the hit, 
		 * 			and theHitter is the BasicGameObject that caused the hit.
		 */
		private function enemyHitReaction(e:HitEvent):void {
			if (e.hitSpecs.theHitter == theMainChar) {
				simpleEnemy.removeEventListener(HitEvent.HIT, enemyHitReaction);
				simpleEnemy.addEventListener(GameObjectEvents.LOOP, slowlyDie);
			}
		}
		
		private function slowlyDie(e:Event):void {
			deathColor++;
			var color:uint = GameMath.fadeHexColors(0x000000, 0xFF0000, deathColor/100);
			with (simpleEnemy.graphics) { clear(); beginFill(color); drawRect(0,0,10,25); }
			if (color == 0xFF0000) {
				simpleEnemy.removeEventListener(GameObjectEvents.COMPUTER_THINKING, walkBackAndForth);
				simpleEnemy.removeEventListener(GameObjectEvents.LOOP, slowlyDie);
				simpleEnemy.die(true, 1);				
			}
		}

		private function walkBackAndForth(e:Event):void {
			if (simpleEnemy.x >= 660) {
				enemyDirection = 1;
			} else if (simpleEnemy.x <= 620) {
				enemyDirection = -1;
			}
			if (enemyDirection == 1) {
				simpleEnemy.moveLeft();
			} else {
				simpleEnemy.moveRight();
			}
		}
	}
}

The Result

You can compile the above code in different ways.
It can be used as a Document Class, or you can use the command line compiler to compile just the .as file itself.
The com folder needs to be in the source directory.
You can just call the package whatever you like, or take out "Tutorials" from the declaration.

This is how it should look, click on it first for key control:


See the example in its own window here.

Next: Part 2

In part 2 of this tutorial we will use Samus (a make shift re do version of the character from Super Metroid)
as the body for a smaller rectangle ghost.

We will see the most basic of ways to construct animations for that body.
And we will include methodology that will allow Samus to fire bullets from her gun.

part 3 part 4a part 4b part 4c











actiontad twitter.com/actiontad terms