
Plan your behavior
So I have introduced how to plan out simple AI behavior from my previous post – Cheating AI from Hardcourt – Plan it out – if you haven’t, read that one first before this.
Our next step would be writing the data charts into codes. It might look quite easy when you see the simple flow charts but remember that the AI must process this through out the game with a character’s lifespan.
What I’m about to share is simply a psuedo codish (partly actionscript syntax) of the AI’s logic. Eventually you can port it into which ever approach you are comfortable with, like OOP or the old school timeline.
Remember that this is my first published game where as I still used AS2 using prototypes and functions but I’ll discuss this in a little of close objective approach. First of we prepare a few world declarations & preparation in a quick manner.
Prepare the world
In the game world object or whichever the game’s entities reside, we’ll be having a calculateDistance method that pre-calculates distances of each of the characters then stores each of their enemies distance on an enemyDist container object.
// from the game world // called within the main loop function calculateDistances() { // pre-calculate distances of: // ball to char - store distance to the character's ballDist variable // char to char - store all other character's (enemy's) distance to character's enemyDist container variable }
The code below acts as base methods for all characters. This is primarily an advantage from inheritance whereas properties and methods of the base class is inherited by the class that extends it. Basically all the characters will be capable of methods such as moving (moveChar), getting the ball (getBall), shooting it (shoot) and punching their enemies (punch).
// used to store the ball object also do determine if the we have the ball; set to null if we not var myBall:Ball; // the distance of the ball from character in Vector2D (x, y) var ballDist:Vector2D; // contains enemies distances var enemyDist:Object; // assuming that 25 is the range where the character can punch it's enemies var punchRange:Number = 25; // moves the character function moveChar( xdir, ydir ) { // xdir - move left (-1) or right (1) // ydir - move up (-1) or down (1) } // determines if we have the ball else if the ball is close & not owned (by enemy) then get it function getBall():Object { // use ballDist for the calculated distance; // returns: ball object - if our char already has it // null - if the ball is nowhere near } // shoots the ball function shoot() { // release the ball towards the ring // set myBall to null } // attacks the enemy // params: enemy:Object - the enemy instance to punch function punch( enemy:BaseChar ):Boolean { // returns: true - if successful hit // false - else not }
Note: for the Vector2D, some might be wondering what it is, well it is a class whose purpose is to calculate Vector operations in two dimensions.
Now we have prepared the initial methods, let’s go with the proposed coding of the flow charts.
Basic Behaviors
The AI controlled character behavior:
// runs with the main loop for each AI character function thinkAI(){ var xdir = 0; var ydir = 0; // if we have the ball if ( getBall() ) { // then shoot it!! shoot(); // if not }else { // get the direction of the ball xdir = ballDist.x ? ballDist.x / Math.abs( ballDist.x ) : 0; ydir = ballDist.y ? ballDist.y / Math.abs( ballDist.y ) : 0; // and move towards it moveChar( xdir, ydir ); } }
Expanding for more Interactions
What we do next is implement the second chart (Interactive Chart) and consider situations with the enemy and interact with it.
So improving the thinkAI() method:
function thinkAI() { var xdir = 0; var ydir = 0; // if we have the ball if ( getBall() ) { // then shoot it!! shoot(); // if not, }else { var ball = GameWorld.ball; var dist:Vector2D; // check if another char has the ball, since definitely it isn't this char if ( ball.owner ) { // store the enemy who has the ball in a local var var targetEnemy = enemyDist[ ball.owner.id ]; // this is the distance of the enemy char with respect to this char dist = targetEnemy.dist; // if this enemy is within punching range if ( dist.len < punchRange ) { punch( ball.owner ); // no need to do the processes below... return; } } else { dist = ballDist; } // get the direction xdir = dist.x ? dist.x / Math.abs( dist.x ) : 0; ydir = dist.y ? dist.y / Math.abs( dist.y ) : 0; // and move towards it moveChar( xdir, ydir ); } }
Then we take this into the 2nd chart.
function thinkAI() { var xdir = 0; var ydir = 0; // if we have the ball if ( getBall() ) { // then shoot it!! shoot(); // if not, } else { var ball = GameWorld.ball; var dist:Vector2D; // check if another char has the ball, since definitely it isn't this char if ( ball.owner ) { // store the enemy who has the ball in a local var var targetEnemy = enemyDist[ ball.owner.id ]; // targetEnemy.dist is the distance of the enemy char with respect to this char dist = targetEnemy.dist; // if this enemy is within punching range if ( dist.len < punchRange ) { punch( ball.owner ); // no need to do the processes below... return; } } else { // default the distance from the ball var dist = ballDist; var ds; // loop through enemies for ( c in enemyDist ) { ds = enemyDist[ c ].dist; // see if they are closer if ( ds.len < dist.len ) dist = ds; // it seems that this enemy is already in range, no need to check the others if ( ds.len < punchRange ) break; } // if this is not the ball distance then it could only be the enemy if ( dist != ballDist ) { // if this enemy is within punching range if ( dist.len < punchRange ) { // attack!! punch( ball.owner ); // no need to do the processes below... return; } } } // get the direction xdir = dist.x ? dist.x / Math.abs( dist.x ) : 0; ydir = dist.y ? dist.y / Math.abs( dist.y ) : 0; // and move towards it moveChar( xdir, ydir ); } }
Now we have our competitive AI ready! Or not. Feeling like something is missing? Yes, its “difficulty!” Basically as you might have suspected that this AI behavior has the perfect reflex (well not so perfect really). It does what it has to depending on exact situations at the exact time on the next loop.
Example this:
Just at the same moment you grab the ball the enemy would instantly steal the ball or, simply saying, you can’t even run away, your character’s very change in position and angle is updated and read by the AI at the same time or yet exactly at the next frame therefore it knows where exactly you are same way the moment you move anywhere.
And another problem with the perfect reflex is same way we planned it, the moment the AI character grabs the ball he just shoots it at once.
For this problem, a good fix I did was based around with numbers. Yes, “numbers” will help with everything, you can imagine setting a level of intelligence for the AI and how much well it executes it’s task depends on the numbers assigned. But I’ll leave that to another post to discuss about it more…
What do you think? Play hardcourt now! to experience the AI in action.









•