The secrets of making 2D RPG enemy AI divulged? Yep. Right here.
In this post we will talk about how the Troll Boss AI was developed for our free iPhone game. The game is an RPG co-developed with our community and will be released on the App Store in March, 2011.
Creating Artificial Intelligence, Something Cocos2D Doesn’t Do
The iPhone Game Kit has always provided the basic framework for creating artificial intelligence. Since day one, we’ve been showing you how to make skeletons seek, attack, and even dance. Now we will show you a few more advanced tricks, like how an enemy can perform ranged attacks, special attacks, flee, teleport, and yes, burp out crazy windstorms which send the player spinning.
The book included in the Kit talks about how AI works in the chapter “Artificial Intelligence Agency.” This post will expand on it a little.
Our take on developing game AI has always been to create different possible moods for the enemy and then run them with a timer. When the timer reaches zero it’s time to think about a different mood.
For example, the default mood for all enemies is to sleep. The enemy sleeps, sleeps, sleeps and when the timer reaches zero the enemy opens one eye and looks for the player. If the player is close enough then the enemy switches into the seek mood. Simple enough, right?

The original skeleton AI from QuexlorLite
The beauty here is that each mood method is self contained. If the timer is in a state of reset, then the mood is initialized for the first time. If the timer is running, then the mood continues. If the timer reaches zero, then think about choosing a different mood. All these neatly fit into one method. For example, everything related to an enemy fleeing happens in the updateFlee
method.
The first step to creating a more advanced AI was to stuff the enemy’s quiver full of different possible moods. A few were added in our latest development release:
// enemy moods
enum
{
kMoodSleep,
kMoodSeek,
kMoodRandom,
kMoodDance,
kMoodFlee,
kMoodTeleport,
kMoodRangedAttack,
kMoodSpecialAttack,
};
Now, how to develop the moods so that certain basic things (like fleeing and teleporting) can be re-used by any enemy, without cluttering the codebase with silly booleans like canTeleport
?
Easy.
Just override the chooseMood
method. The default mood-choosing method looks like this:
-(void) chooseMood
{
// this is the default chooseMood method
// override it to implement different enemy personalities
[self setMood:([self canWakeup] ? kMoodSeek : kMoodSleep)];
}
The Troll Boss AI’s mood choosing looks like this:
-(void) chooseMood
{
if( [self canWakeup] )
{
// ranged attack?
if( mood != kMoodRangedAttack
&& playerDistanceSQ > 16000.0f // about 2 tiles
&& CCRANDOM_0_1() < 0.3f
&& [[self getLevel] countObjects:@"Windstorm"] < 3 )
[self setMood:kMoodRangedAttack];
// seek
else
[self setMood:kMoodSeek];
}
else
{
[self setMood:kMoodSleep];
}
}
With me so far? This method simply causes the Troll Boss to perform his ranged attack if the player is on the screen, the player is at least two tiles away, a certain randomness is fulfilled, and there are not already too many ranged attacks (Windstorms) on the screen.
Special AI Components Require Special Objects
Most of the Troll Boss functionality is created with simply one line of code:
-(id) init
{
return [super initWith:@"greytroll" life:100.0f strength:12.0f dexterity:1.0f scale:2.0f];
}

Quexlor fights the great Troll Boss, who burps out random Windstorms
This creates a very strong, slow, big creature with lots of life force. The iPhone Game Kit's existing Enemy
class handles the creation of this creature and even its basic moods, like sleeping, seeking, and fleeing.
The real difference between the Troll Boss and every other enemy is his ranged attack, the Windstorm, an entirely separate object. You can download the entire source code to the Windstorm in our latest development release.
The Windstorm gets created by a huge belch from the Troll Boss. It storms directly toward the player on its initial vector, then chooses to move randomly around the screen after a few seconds. Every time it chooses to move in a new direction it also chooses a new speed, mimicking the true nature of wind. When it gets close enough, it sucks the player in and disables all control for a time, depositing the player somewhere else completely. The player cannot move or attack while in a Windstorm. However, the player can be attacked, which creates an interesting dynamic while fighting the big, heavy-hitting Troll Boss.
This code shows how the Windstorm creates its initial movement vector toward the player:
// initially head directly toward player
vectorTimer = 2.0f;
vector = ccpNormalize(ccpSub([self getPlayer].position, self.position));
The rest of the Windstorm code is too lengthy and complex to throw into this humble blog post.
AI Refinements, More Improvements Above the Cocos2D Layer

A sample Charater Profile .plist file
With a big, strong, slow boss like the Big Troll belching out challenging Windstorms in place, it was time to refine the AI and make it more fun. What is one of the simplest ways to make a game more fun? Sound effects.
A few troll-esque sounds were gleaned from Freesound.org, pitched a little different, cropped, converted to .caf, and thrown into the troll's character profile. (Note that we keep a list of Freesound.org users to thank. Thank you in advance, Freesound users.)
Here's another bit of beauty built into the iPhone Game Kit that you don't get with Cocos2D: character profiles.
Character profiles are simply a property list (.plist file) which outlines all the sprites, animations, sounds, and spritesheets necessary for a character. The CharacterProfile class (part of the Kit) automagically loads all these resources and makes them available to the AI code at run time. With one simple entry of "troll-belch.caf" added to the sounds
dictionary of greytroll profile.plist
, the troll can belch with one line of code (note that no extra code was necessary to manually load the sound effect):
[self playSound:@"belch"];
The Kit's CharacterProfile class does a lot more than that. It also loads a plethora of sprite frames into handily-accessible animations, all magically via one dictionary entry in the property list.
The belch animation loads 7 frames per direction (a total of 7 * 8 == 56 different sprite frames!) all without a single extra line of code. The Troll Boss AI simply uses the animation like this:
[sprite runAction:[CCAnimate actionWithAnimation:[profile getAnimation:@"belch" index:currentDir]]];
After these sound effects and animations were added to the Troll Boss AI, he was a lot more fun to fight! Hooray for fun making in such an easy and quick way.
Thanks
This blog post and the latest development release were not possible without the incredible inspiration and code submitted by our rad, rad community. Thanks especially to leathalChicken, stahlmanDesign, and juancasanueva.
Progress with our community Cocos2D RPG game continues with the latest development release (iPhone Game Kit version 3.3.5). Join us in the forums or download the Kit today to participate in beta testing. We need all the help we can get to release our game on time in March.
Parting Thought
With all these enhancements to the iPhone Game Kit, we should probably be charging a lot more. Maybe we will. Get it now while it's on sale.
That's all for now.