This Is A Custom Widget

This Sliding Bar can be switched on or off in theme options, and can take any widget you throw at it or even fill it with your custom HTML Code. Its perfect for grabbing the attention of your viewers. Choose between 1, 2, 3 or 4 columns, set the background color, widget divider color, activate transparency, a top border or fully disable it on desktop and mobile.

This Is A Custom Widget

This Sliding Bar can be switched on or off in theme options, and can take any widget you throw at it or even fill it with your custom HTML Code. Its perfect for grabbing the attention of your viewers. Choose between 1, 2, 3 or 4 columns, set the background color, widget divider color, activate transparency, a top border or fully disable it on desktop and mobile.

In this particular episode I'm going to talk about the horror of Ladders and the joy of AI.

Ladders

Ok, so ladders are a complete pain in the ass on all fronts! The reason being is that they represent a discontinuity in control mechanism and physics response.

The player must transition from walking along in an environment where they experience gravity and can jump to one where they do not and cannot jump. On top of that, you have several different methods of starting the climb:

Walking along the floor and joining the bottom of a ladder

Walking along the top of a platform and joining the top of a ladder

Landing on the middle of the ladder after falling or doing a jump

The Horror

So why have these distinctions?

Walking along the floor and joining the bottom of a ladder

You don't want to walk past the bottom of a ladder and automatically start climbing it, so that means you must have a special case for being at the bottom of the ladder and pressing up (which would normally have jumped you).

Walking along the top of a platform and joining the top of a ladder

Again, you don't want to start climbing down a ladder just by walking over the very top of one, or indeed by landing on the top, so you need a special case for pressing down while being on the top of the ladder. Additionally, this is made even more of a pain because this is the only instance in the game where the player can pass down through a platform (in order to join the ladder).

Landing on the middle of the ladder after falling or doing a jump

I did wrestle with this idea and for a while I had it set so you couldn't land on the middle of a ladder but after a while of playing I realised this was going to be too fiddly for the player and might lead to a bad play experience. Landing on the middle of the ladder is tricky because it has to act exactly like a platform, but a platform which exists at whatever pixel you land. Also, it represents yet another transition from normal falling to ladder climbing.

Ladder tile types

I have two different tiles which represent a ladder:

The middle

The middle section and:

The top

The top part. The top part is special since it acts like a jump through platform and a ladder, depending on where the player is in relation to it.

Solving the problem

In order to address each of these issues I found I needed to track a fair amount of state and transitions between state.

The variable m_collideLadder is updated every frame and represents the ladder tile type that the player is currently colliding with (i.e. player's centre point is within the AABB of the tile). It can either be eTileTypes.kLadderMid, eTileTypes.kLadderTop, or eTileTypes.kInvalid when not colliding with a ladder tile.

In addition I have:

privatevar m_oldCollideLadder:uint;

private var m_oldCollideLadder:uint;

This tracks the last state of m_collideLadder, so whenvever m_collideLadder changes, the previous state is stored in m_oldCollideLadder. This allows me to detect transitions between colliding with the ladder and not.

In particular the case where the player lands on the middle of a ladder by falling or jumping looks like this:

You should be able to see that part where this transition is picked up, and the kLandAnim animation gets played.

privatevar m_climbLadder:Boolean;

private var m_climbLadder:Boolean;

This is set when the player is confirmed as climbing the ladder; in this event a special case control mechanism will take over from the normal gravity/friction behaviour which actually enables the climbing itself.

privatevar m_onLadderTop:Boolean;

private var m_onLadderTop:Boolean;

This is set by the collision system when the player is confirmed as being on the top of a ladder - this has a dual purpose; it lets me have a special case so I can apply the same collision logic as I would do when standing on a jump-through platform and it also allows me to detect the down arrow in the player input control mechanism as an indication that the player wants to join the ladder at the top.

privatevar m_disableLadderTopCollision:Boolean;

private var m_disableLadderTopCollision:Boolean;

This is set when joining the ladder at the top and allows the collision system to ignore the ladder top tile until the player is confirmed as having his centre point in the AABB of any ladder tile. This causes the player to fall briefly down until they start colliding with the middle of the ladder tile, where collision is then re-enabled.

Special ladder physics

I covered the regular collision response code in the last article (the part where friction is applied etc). When m_climbLadder is set, this code is ignored and some new code takes over.

The player has a special variable called m_velTarget which is 0,0 by default and only comes into use when we're confirmed as being on a ladder. This gets set to the maximum player speed in the X or Y axis inside the player input system when the player is moving left/right or up/down and is on a ladder.

The main body of the code tries to alter the player's actual velocity (which is Player.m_vel) towards this new target velocity, and to prevent the player from arriving at the target velocity too quickly there is a special Player.kLadderStayStrength constant. This prevents the ladder movement from contrasting with the player's movement in the rest of the game.

What's going on in the above code? Well, once we're inside the condition:

if( m_climbLadder ){

if ( m_climbLadder )
{

then we're actually doing the ladder physics. First of all we work out the difference between our current velocity and the desired velocity:

var delta:Vector2 = m_velTarget.Sub( m_vel );

var delta:Vector2 = m_velTarget.Sub( m_vel );

Then, if the length of this vector is longer than our ladder stay strength amount (i.e. the movement is too much to do in one frame), we have to clamp the movement so its just as much as we're allowed to move by the constant kLadderStayStrength:

Once this has been clamped we add this delta to our velocity vector and we're done:

m_vel.AddTo( change );

m_vel.AddTo( change );

Once the player stops colliding with a ladder tile, the normal collision resolution code takes over again and we're done with ladders completely!

The joy of AI

I really enjoyed working on the AI for this demo because it went so amazingly smoothly, nearly everything I wanted to try worked first time. It was refreshing after all the messing around with the ladder stuff; you learn to enjoy these small victories after you've been programming for a while because they're so rare 🙂

Lets have another look at the class hierarchy I showed in the last article:

AI Hierarchy

The complete AI hierarchy is shown as the yellow branch which inherits from Character.

Enemy, the base class

Ok, so lets have a look at the base class which all enemies derive from:

It requires any concrete implementations to define a couple of attributes: m_WalkSpeed and m_KillsOnTouch. m_WalkSpeed is used in the constructor and as the name suggests is the speed at which the enemy walks. m_KillsOnTouch is used in the collision detection system and defines whether this particular enemy will kill the player on touch.

All enemies are required to provide a couple of animations with specific names which are referenced in this base class (or in Character from which this derives):

Star walk anim

'walkAnim' - the walk animation this enemy has

'hitAnim' - the animation to play when this enemy is punched by the player

The individual enemy types may well provide additional animations, but the above is the bare minimum.

The other function worth mentioning is Hurt() which is actually defined in Character. This is overridden here with a custom implementation which counts down a few frames, then triggers the 'hitAnim' and attaches a callback which will happen when the animation is done playing; this marks the enemy for deletion and also spawns a pick-up where the enemy was at the time.

Simple enemy

This is another shared, non-concrete class which encompasses the behaviour of two of the enemy types.

Their basic behaviour is to head in a constant direction until they encounter a special marker type which indicates they should reverse direction. This marker type is always mapped in the middle collision layer in the map along with all other AI stuff.

Change direction marker

The marker itself gets a special tile (shown above) so the mapper (i.e me) can see where I've placed it in the map. At runtime, these tiles are turned invisible, or rather they don't actually have any visual data exported with them.

...which forms an AABB for the extents of the character and then calls out to the collision system to return all tiles which intersect with this AABB. The collision system then calls back to my callback function InnerCollide:

...this checks the tile type being collided with is of type eTileTypes.kReverseDirection, if so and if the enemy is heading towards this tile then we toggle the enemy's direction and also have him face the opposite way. That last condition (which checks the enemy's direction) is very important, otherwise it could be that the enemy is still going to be colliding with the same change direction marker next frame; this would lead to a very confused enemy who would toggle direction constantly.

For reference, here is the utility function to work out if an object is heading towards a point (in the X axis):

So, what's going in here? Firstly we do some simple maths to determine whether the player is within some radius of the enemy. We also work out if the enemy is facing the player (after all, no point trying to attack the player if he's behind you). If both of these conditions are true we decrement a counter, once this counter gets to 0, we trigger a sequence of animations via callbacks:

The first simply waits until the end of the current anim - this is to make sure the animation frames match up; since I designed the anims to follow on from each other.

When the above callback is called, we trigger the actual animation for spawning the spikes. This is done so the player knows to prepare themselves to get out of the way. I also pause the motion of the creature at this point, taking a note of what its original motion was so I can restore it afterwards.

Once the spawn spikes animation has finished, I actually spawn a new object, which is another very simple class which obeys gravity and does full collision but also kills on touch. I make sure it heads in the same direction that the original enemy is facing, make sure it appears behind this enemy and lastly play the fired animation which sets up the synchronisation to match with the frame the normal animation ended on.

Once the previous animation has finished, the firing action is complete, so we resume playing the regular walk animation, reset the counter and restore the original motion of the enemy.

The brain

This enemy type has a pretty simple behaviour as well: it kills on touch and it will move for a while, then pause for a while and then carry on. When it starts to move it heads towards wherever the player was at the time.

All the magic is within the Update function. As you can see there are a couple of simple timers which tick down depending on what mode the enemy is currently in (either m_think==true or not). When the think timer becomes less than 0, the enemy toggles modes and picks a direction to move.

This new direction takes the form of a velocity. This velocity is based on the vector from the enemy to the player, which is then made unit length and scaled by the kWalkSpeed of the enemy. This will cause the enemy to move in the direction of the player at a constant speed. Once he gets there he kills on touch.

The skeleton

This is the final AI character and has the most complex behaviour of them all.

Skeleton

Its all driven via the animations though, which makes it rather flexible.

This was done to stop multiple skeletons from landing in the exact same spot too much, which looked unnatural. Then, this target position is turned into a unit length direction vector, which points from the skeleton towards the player:

The reason it was -2 and not -1 or some other number is that this forces the final vector to be mostly pointing upwards with just a little side to side motion. If I'd chosen -1, it would have been a more even spread.

You can use normalisation like this to create different spread patterns, whereby a random unit vector is added to a known fixed direction vector, then renormalised. I've used this technique in the past to create an emission spread for a particle system which needed to emit particles in a cone formation; it was random point in a unit sphere plus some predefined forward direction (scaled as appropriate) and then renormalised.

Then I actually perform the jump by setting the skeleton's velocity and clear the animation callback to reset the sequence:

And that's it!

The motley crew

That concludes my series on how to make a 2d platformer, I hope you've enjoyed reading it!

As ever, if you want, you can buy the source-code for the entire game (or even try a version for free), including all the assets and levels you see above. It will require Adobe Flash CS4+, the Adobe Flex Compiler 4.0+ and either Amethyst, or Flash Develop to get it to build. And you'll want Mappy or some alternative in order to create your own levels!

Following on from feedback from the Angry Birds article, I've included a Flash Develop project as well as an Amethyst project inside the .zip file, to help you get started more quickly, no matter which development environment you have.

You are free to use it for whatever purposes you see fit, even for commercial games or in multiple projects, the only thing I ask is that you don't spread/redistribute the source-code around. Please note that you will need some programming and design skills in order to make the best use of this!

Share This Story, Choose Your Platform!

A games industry veteran of ten years, seven of which spent at Sony Computer Entertainment Europe, he has had key technical roles on triple-A titles like the Bafta Award Winning Little Big Planet (PSP), 24: The Game (PS2), special effects work on Heavenly Sword (PS3), some in-show graphics on the BBC’s version of Robot Wars, the TV show, as well as a few more obscure projects.
Now joint CEO of Wildbunny, he is able to give himself hiccups simply by coughing.
1NobNQ88UoYePFi5QbibuRJP3TtLhh65Jp

hi paul,
i kinda completed my game, but i couldn’t manage to understand how the 2 background images work (furthest one is drawn with respect to the map, and its fixed) but the mid-ground image is definitely not the same.

I’m so lost. I don’t get where these codes are supposed to go when pasted into layers or objects. Which codes get the AI scripts? Where exactly does all that hit detection script go? A particular layer, perhaps? If so, which one? I keep reading and re-reading, but I’m just not seeing it. I understand the dynamics of what the codes are meant to accomplish, even when reading it from the script, but I don’t get where they are placed. Please, help…

Excellent article thanks! If anybody’s interested in developing flash games I would also recommend reading http://www.scratchpad-cloud.com/blogs.htm as it talks through how Flash games enemy artificial intelligence works. Really helpful like this article.

About the author

A games industry veteran of ten years, seven of which spent at Sony Computer Entertainment Europe, he has had key technical roles on triple-A titles like the Bafta Award Winning Little Big Planet (PSP), 24: The Game (PS2), special effects work on Heavenly Sword (PS3), some in-show graphics on the BBC’s version of Robot Wars, the TV show, as well as a few more obscure projects.
Now joint CEO of Wildbunny, he is able to give himself hiccups simply by coughing.
1NobNQ88UoYePFi5QbibuRJP3TtLhh65Jp