Quantcast
Channel: Emanuele Feronato | RSS Feed
Viewing all articles
Browse latest Browse all 6

Create a Flash racing game tutorial – Artificial intelligence

$
0
0

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 ilines, 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!!


Viewing all articles
Browse latest Browse all 6

Trending Articles