Finally it’s time to start talking about a real Artificial Intelligence algorithm. As said, I won’t use waypoints because I want to focus on “real” artificial intelligence, I want to make cars drive like if they were controlled by a player.
The idea
Let’s imagine you are driving a car, you aren’t blind so you can see the track.
If you see the track has a left turn, you turn left.
If you see the track has a right turn, you turn right.
You always accelerate, unless you see the turn is sharp
The code
This is how I made: the car has a line of sight like the one developed for the survival horror prototype.
This means is have i
lines of sight subdivided in j
segments.
Every line of sight can be done with all j
segments, if it never hits the track boundaries, or with a number n
from 0
and j-1
if it hits the track boundaries while drawing the n-th
segment.
When I have a line of sight made by j
or n
segments, I have to determine if this line is on the left or on the right side of the car. This can be easily done because if I have i
lines, all lines < i/2
will be on the left and the remaining ones will be on the right.
Now I have the number of segments before I hit track boundaries and I know if it’s on the left or on the right side of the car.
I increase a variable called turn_left
by the number of segments if the line is on the left side, or I decrease it if the line is on the right side.
In the ideal situation, when no lines hit track boundaries, turn_left
is zero
and the car continues straight.
If turn_left
is positive, I should turn left, if it’s negative I should turn right, but only if the absolute value is greater than a certain tolerance.
I don’t want my car to turn left and right just because a single line of sight hit the track boundaries.
Same thing for the acceleration: I do not accelerate only if the absolute value of turn_left
is greater than a certain tolerance.
Let’s translate it into AS3:
package { import flash.display.Sprite; import flash.events.Event; import flash.geom.Point; public class car_mc extends Sprite { public var acceleration:Number=0.4; public var speed_decay:Number=0.96; public var rotation_step:Number=15; public var max_speed:Number=10; public var back_speed:Number=1; public var speed:Number=0; // defining the angle of sight in degrees public var angle_of_sight:Number=90; // this is just a graphical representation of the line of sight public var line_of_sight:Sprite = new Sprite(); // this is the accuracy: the higher, the better, the slower the simulation public var sight_accuracy:int=15; // this is the lenght of sight public var sight_lenght:int=100; // this is the number of steps required to complete the lenght of sight public var sight_steps:int=20; // this is the turning tolerance public var turn_tolerance:int=10; public function car_mc(posx:int,posy:int):void { addChild(line_of_sight); line_of_sight.graphics.lineStyle(1,0x000000); line_of_sight.graphics.lineTo(100,100); x=posx; y=posy; addEventListener(Event.ENTER_FRAME,on_enter_frame); } public function on_enter_frame(e:Event):void { var angle_step:Number=angle_of_sight*2/sight_accuracy; var sight_point:Point; var global_sight_point:Point; var par:racing=this.parent as racing; // variable to hold line of sight collision results var turn_left:int=0; // variable to determine if the car is colliding with the ground var colliding:Boolean=false; line_of_sight.graphics.clear(); line_of_sight.graphics.lineStyle(1,0x000000); for (var i:int=0; i<=sight_accuracy; i++) { for (var j:int=1; j<=sight_steps; j++) { line_of_sight.graphics.moveTo(0,-15); sight_point= new Point(sight_lenght/sight_steps*j*Math.cos((-90-angle_of_sight+angle_step*i)*0.0174532925),sight_lenght/sight_steps*j*Math.sin((-90-angle_of_sight+angle_step*i)*0.0174532925)); global_sight_point=localToGlobal(sight_point); if (par.ground.hitTestPoint(global_sight_point.x,global_sight_point.y,true)) { // leaving the loop if the j-th segment of the i-th line of sight break; } } // if the line of sight is on the left, add the number of segments to turn_left variable if (i<sight_accuracy/2) { turn_left+=j; } else { // if the line of sight is on the right, add the number of segments to turn_left variable turn_left-=j; } line_of_sight.graphics.lineTo(sight_point.x,sight_point.y); } // accelerate if turn_left is smaller than turn_tolerance*2 if (speed<max_speed&&Math.abs(turn_left)<turn_tolerance*2) { speed+=acceleration; } var speed_x:Number=Math.sin(rotation*0.0174532925)*speed; var speed_y:Number=- Math.cos(rotation*0.0174532925)*speed; y+=speed_y; x+=speed_x; var point_left:Point=new Point(-9,0); var point_right:Point=new Point(9,0); var point_front:Point=new Point(0,-13); var point_back:Point=new Point(0,13); point_left=localToGlobal(point_left); point_right=localToGlobal(point_right); point_front=localToGlobal(point_front); point_back=localToGlobal(point_back); // car versus track boundaries collision management if (par.ground.hitTestPoint(point_left.x,point_left.y,true)&&par.ground.hitTestPoint(point_front.x,point_front.y,true)) { rotation+=5; speed*=0.85; colliding=true; } if (par.ground.hitTestPoint(point_right.x,point_right.y,true)&&par.ground.hitTestPoint(point_front.x,point_front.y,true)) { rotation-=5; speed*=0.85; colliding=true; } if (par.ground.hitTestPoint(point_front.x,point_front.y,true)) { speed*=0.55; colliding=true; } if (par.ground.hitTestPoint(point_back.x,point_back.y,true)) { speed*=0.55; colliding=true; } if (! colliding) { // turn left or right according to tolerance and turn_left value if the car is not colliding with track boundaries if (Math.abs(turn_left)>turn_tolerance) { if (turn_left>0) { rotation -= rotation_step*(speed/max_speed); } else { rotation += rotation_step*(speed/max_speed); } } } if (Math.abs(speed)>0.3) { speed*=speed_decay; } else { speed=0; } } } }
And this is the result:
As you can see, playing with rotation_step
, angle_of_sight
, sight_accuracy
, sight_lenght
, sight_steps
and turn_tolerance
you can have the car run by itself along the track.
The code is way to be perfect, and I know there are some cases it won’t work (but I won’t tell you when :))… anyway if you want to try it by yourself, just copy this new script into car_mc
class you can find at step 1.
Want to learn more? Learn by example!
Get the full commented source code of an actual commercial cross platform HTML5 game!!