Introduction to AI Programming for Games

Update: We have an updated version of this tutorial for iOS 9 and GameplayKit here. When you make a game, you often have enemies for the player to combat. You want these enemies to seem intelligent and present a challenge to the player to keep the game fun and engaging. You can do this through […] By Ray Wenderlich.

Leave a rating/review
Save for later
Share

Update: We have an updated version of this tutorial for iOS 9 and GameplayKit here.

When you make a game, you often have enemies for the player to combat. You want these enemies to seem intelligent and present a challenge to the player to keep the game fun and engaging.

You can do this through a variety of techniques collectively known as artificial intelligence (AI) programming. This is a huge field – entire books have been written on the subject! There are many subtopics, from pafhfinding to steering to planning to state machines to behavior trees and more.

Obviously I can’t cover everything in this introductory tutorial, but I can show you how to get started with a hands-on example of adding AI into a simple game!

In the process of adding AI into this game, you’ll learn about two common AI techniques along the way: steering and finite state machines.

You’ll see that when adding AI into a game, the techniques you use don’t necessarily have to be complicated to be good AI – as long as the game is fun, you win!

This tutorial covers making a game with Cocos2D on iOS. Most of the tutorial is not Cocos2D specific, so you should be able to follow along even if you aren’t familiar with it. However, it you would like to learn more about Cocos2D, you can check out some of our other Cocos2D tutorials on this site.

Introducing Monster Wars

To keep the focus on AI programming and not all the extra stuff, I have created a starter project for you, called Monster Wars!

Download the starter project, open it in Xcode, and build and run to check it out. You’ll see something like the following:

I didn’t have time to add instructions, so here’s how you play:

  • You get money every so often, which you can see in the upper left. You can use it to buy units, by tapping the buttons at the bottom.
  • There are three enemy types: Quirk (fast and cheap), Zap (ranged attackers), and Munch (slow but has AOE chomp).
  • You can switch your units from attacking to defending by tapping on your castle (the green one on the left).
  • If your units destroy the enemy’s castle, you win!

Of course, the game is not a challenge right now, because the enemy doesn’t have any smarts! He doesn’t choose units or choose when to attack or defend – that’s your job in this tutorial! :]

You’ll also notice the movement is kind of “jaggedy” when monsters arrive at their destination – this is another thing you will fix.

Feel free to take a look at the code in the project to get familiar with how things are put together. Then keep reading to get started with adding some AI!

Note: Don’t laugh too much at the poorly drawn art – I am a pretty bad artist! ;]

Note: Don’t laugh too much at the poorly drawn art – I am a pretty bad artist! ;]

The Simplest Solution

In this first section I want to show you how easy AI can be – it can just be “normal” coding if that’s all your game needs! You only need to add more complicated techniques if it would benefit your game.

So in this first section, you’ll get some basic AI in the simplest possible way – making decisions in an update loop with an if/else statement!

In this game, there is an object called AIPlayer that represents the enemy team/castle. It has an update method that gets called every frame, and that’s where you’ll put your code.

Open AIPlayer.m and at the end of the update method, add the following:

// Attack/Defend decision
if (self.layer.aiTotalValue > self.layer.humanTotalValue) {
    if (!self.attacking) {
        NSLog(@"AI: Now attacking");
        [self.layer setPlayer:self attacking:YES];
    }
} else {
    if (self.attacking) {
        NSLog(@"AI: Now defending");
        [self.layer setPlayer:self attacking:NO];
    }
}

// Make build decision
if (self.layer.aiQuirkValue < self.layer.humanZapValue && self.coins > COST_QUIRK) {
    [self.layer spawnQuirkForPlayer:self];
} else if (self.layer.aiZapValue < self.layer.humanMunchValue && self.coins > COST_ZAP) {
    [self.layer spawnZapForPlayer:self];
} else if (self.layer.aiMunchValue < self.layer.humanQuirkValue && self.coins > COST_MUNCH) {
    [self.layer spawnMunchForPlayer:self];
} else if (self.layer.humanTotalValue == 0) {
    while (self.coins > COST_ZAP + COST_QUIRK) {
        [self.layer spawnQuirkForPlayer:self];
        [self.layer spawnZapForPlayer:self];
    }
}

Every frame, the AI asks itself a simple question: am I more powerful than the human? If so, ATTACK!

It also uses a simple if/else statement to decide which unit to build next:

  • Quirks are strong vs. Zaps. So if the human has more value of Zaps than we has value of Quirks, the AI will build a quirk if it can afford it.
  • Zaps are strong vs. Munches. So if the human has more value of Munches than the AI has value of Zaps, the AI will build a zap if it can afford it.
  • Munches are strong vs. Quirks. So if the human has more value of Quirks than the AI has value of Munches, the AI will build a Munch if it can afford it.
  • Finally, if the human doesn’t have anything, the AI will attempt to rush it by blowing all it’s money on Zap + Quirk combos.

Pretty simple algorithm, eh? Nothing fancy about it – but it actually gives a fairly decent challenge! Build and run, and see if you have what it takes to beat the game!

Basic AI working in the game

Movement Problems

So far so good, but I don’t know about you, but that “jaggedy movement” behavior is driving me mad!

The reason this is happening is because of how Monster.m’s updateMove method is coded. Here’s what it looks like now:

- (void)updateMove:(ccTime)dt {
    
    if (self.maxAcceleration <= 0 || self.maxVelocity <= 0) return;

    BOOL hasTarget = FALSE;
    CGPoint moveTarget;
    GameObject * enemy = [self.layer closestEnemyToGameObject:self];
    float distance = ccpDistance(self.position, enemy.position);
    static float ATTACK_THRESHHOLD = 150;
    
    if (self.attacking || (enemy && distance < ATTACK_THRESHHOLD)) {
        if (!enemy) {
            enemy = [self.layer playerForTeam:[self.layer oppositeTeam:self.team]];
            distance = ccpDistance(self.position, enemy.position);
        }
        if (enemy) {
            hasTarget = YES;
            moveTarget = enemy.position;
            if (self.isRanged) {
                CGPoint vector = ccpNormalize(ccpSub(self.position, enemy.position));
                moveTarget = ccpAdd(enemy.position, ccpMult(vector, self.rangedRange));
                distance = ccpDistance(self.position, moveTarget);
            }
        }
    } else {
        Player * player = [self.layer playerForTeam:self.team];
        if (player) {
            hasTarget = YES;
            moveTarget = player.position;
        }
    }

    
    if (hasTarget) {
        CGPoint direction = ccpNormalize(ccpSub(moveTarget, self.position));
        CGPoint newPosition = ccpAdd(self.position, ccpMult(direction, self.maxVelocity * dt));        
        CGSize winSize = [CCDirector sharedDirector].winSize;
        newPosition.x = MAX(MIN(newPosition.x, winSize.width), 0);
        newPosition.y = MAX(MIN(newPosition.y, winSize.height), 0);
        self.position = newPosition;
    }
}

You see that each frame this moves the monster closer to a desired target, but:

  • There's no tolerance for "close enough"
  • There are small rounding errors that cause problems
  • The method has no concept of acceleration - only velocity
  • If a Quirk is near a Zap, it tries to move away from the Zap in the opposite direction of where the Zap is. But if the Zap is literally on top of the Quirk, problems arize, because the Zap is quicker than the Quirk so it sort of moves randomly in place and seems "stuck".

In other words, this movement algorithm is pretty simple, but stupid. You need something better!

You might not realize it, but AI programming is not only about deciding what to do, but also deciding how to do it intelligently. In other words, the AI in this game needs to not only decide when to attack and what units to build, but also how the units should intelligently move!

The fields of AI programming related to movement are known as pathfinding and steering. We've covered A* pathfinding in an earlier tutorial on this site, so this tutorial will focus on steeering, which is a fancy way of saying "figuring out the best way to get an object from here to there"!

Note: Wondering why you can't just use the plain old CCMove actions you know and love in Cocos2D? Those actions are great when you are trying to do simple things. However, the more complicated your game gets, the more likely you are to want to set velocity and acceleration directly with steering algorithms. This gives you much more flexibility and control.

Note: Wondering why you can't just use the plain old CCMove actions you know and love in Cocos2D? Those actions are great when you are trying to do simple things. However, the more complicated your game gets, the more likely you are to want to set velocity and acceleration directly with steering algorithms. This gives you much more flexibility and control.

Contributors

Over 300 content creators. Join our team.