My first Artificial Intelligence and HitTestObject

Hey, what’s up ?
Since my last post, I have pursued the little game started. I have added 3 things : a shot function (space bare), collisions, and AI. You can see the application here, but you must click on the swf before using keyboard (I try to change this, but I always have the problem).

Now, I will explain a part of the code :
– collision : if an enemy hits the Starship, it will be removed. The function is called by an EnterFrame’s Event. At its creation, an enemy is put in the array “enemies”.

private function collisionManagement():void {
 
	for (var i:uint = 0; i < enemies.length; i++) {
		if (vaisseau.hitTestObject(enemies[i] as Enemy)) {
 
			var killed:Enemy = enemies[i] as Enemy;
			killed.die();
			enemies.splice(i, 1);
		}

– the shot management, in an EnterFrame too. And a shot is in a Array, containerShot.

private function shotManagement():void {
 
	for (var j:uint = 0; j < containerShot.length; j++) {
		for (var k:uint = 0; k < enemies.length; k++) {
			if ((containerShot[j] as Shot).hitTestObject(enemies[k] as Enemy)) {
				var killed:Enemy = enemies[k] as Enemy;
				killed.die();
				enemies.splice(k, 1);
 
				removeChild(containerShot[j] as Shot);
				var shotEnd:Shot = containerShot[j] as Shot;
				shotEnd.die();
			}	
		}
	}
}

– the most interesting : AI. The AI moves by itself, in fact AI depends of one thing : the timer’s duration chosen randomly. At each period it changes the angle of the AI. Like the Starship’s deplacement, it only needs angle to move in this direction. After that, if we want a special reaction if the two items are close, we must calculate the distance between them. That is a mathematical function. In my case :

if ((Math.sqrt(Math.pow((-conteneur.x + stage.stage.width / 2 - enemy.posX()), 2) + Math.pow((-conteneur.y + stage.stage.height / 2 - enemy.posY()), 2)) < 250)) {
 
	if (Math.random() > 0.6) { //dynamic probability
		enemy.goOnPoint(-conteneur.x + stage.stage.width/2, -conteneur.y + stage.stage.height / 2);
	}	
} else {
	enemy.startAgainTimer();
})

For the AI next step, I wanted that the enemy go on the Starship to crash on it. But that was not easy to find how find enemy’s angle to go on the Starship (it could be every where). I had as data enemy’s angle, position in x and y, and starship’x with y. And after some time, I found the solution : Pythagore… Never reused since several years. Shame on me !

public function goOnPoint(starshipX:int, starshipY:int):void {
	myTimer.stop();
	statusTimer = false;
	enemy.rotationZ = Math.atan2((starshipX - enemy.x), -(starshipY - enemy.y)) * 180 / Math.PI;
}

The enemy class :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package {
	import com.greensock.TweenLite;
 
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.utils.Timer;
 
	/**
	 * @author Aymeric
	 */
	public class Enemy extends MovieClip {
 
		private var enemy:MovieClip;
		private var myTimer:Timer;
		private var statusTimer:Boolean = false;
 
		public function Enemy(posX:int, posY:int, rotate:int, time:int) {
 
			enemy = new Ennemi();
			addChild(enemy);
			enemy.x = posX;
			enemy.y = posY;
			enemy.rotationZ = rotate;
 
			myTimer = new Timer(time, 0);
			myTimer.start();
			statusTimer = true;
 
			myTimer.addEventListener(TimerEvent.TIMER, move);
			enemy.addEventListener(Event.ENTER_FRAME, loop);
		}
 
		public function posX():int {
			return enemy.x;
		}
 
		public function posY():int {
			return enemy.y;
		}
 
		public function rotatePosition():int {
			return enemy.rotationZ;
		}
 
		public function goOnPoint(starshipX:int, starshipY:int):void {
			myTimer.stop();
			statusTimer = false;
			enemy.rotationZ = Math.atan2((starshipX - enemy.x), -(starshipY - enemy.y)) * 180 / Math.PI;
		}
 
		public function startAgainTimer():void {
			if (statusTimer == false) {
				myTimer.start();
				statusTimer = true;
			}
		}
 
		public function die():void {
			myTimer.stop();
			statusTimer = false;
			myTimer.removeEventListener(TimerEvent.TIMER, move);
			enemy.removeEventListener(Event.ENTER_FRAME, loop);
			TweenLite.to(enemy, 0.5, {alpha:0, onComplete:remove});
		}
 
		private function remove():void {
			removeChild(enemy);
		}
 
		private function move(t:TimerEvent):void {
			TweenLite.to(enemy, 0.5, {rotationZ:Math.round(Math.random() * 360)});
		}
 
		private function loop(e:Event):void {
			enemy.x += 7 * Math.sin(enemy.rotationZ * Math.PI / 180);
			enemy.y -= 7 * Math.cos(enemy.rotationZ * Math.PI / 180);
		}
	}
}

And the shot class :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package {
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.utils.Timer;
 
	/**
	 * @author Aymeric
	 */
	public class Shot extends MovieClip {
 
		private var shot:MovieClip;
		private var myTimer:Timer;
 
		public function Shot(rotate:int, posX:int, posY:int) {
 
			shot = new Fire();
			addChild(shot);
			shot.rotationZ = rotate;
			shot.x = posX;
			shot.y = posY;
 
			myTimer = new Timer(3000, 1);
			myTimer.start();
 
			myTimer.addEventListener(TimerEvent.TIMER_COMPLETE, endShot);
			shot.addEventListener(Event.ENTER_FRAME, loop);
		}
 
		//If it hits an enemy :
		public function die():void {
			myTimer.stop();
			shot.removeEventListener(Event.ENTER_FRAME, loop);
			myTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, endShot);
			removeChild(shot);
		}
 
		private function endShot(t:TimerEvent):void {
			myTimer.stop();
			shot.removeEventListener(Event.ENTER_FRAME, loop);
			myTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, endShot);
			removeChild(shot);
		}
 
		private function loop(e:Event):void {
			shot.x += 20 * Math.sin(shot.rotationZ * Math.PI / 180);
			shot.y -= 20 * Math.cos(shot.rotationZ * Math.PI / 180);
		}
	}
}

And I have seen a difference between two lines which should have the same result. The first works perfectly, but not the second… If you have any idea 🙂

conteneur.removeChild(enemies[k] as Enemy);
conteneur.removeChild(conteneur.getChildByName("enemy" + k));

There are some random bug (my favorites) but it should works normally. Don’t forget to update the page, if you want to restart !

Leave a Reply

Your email address will not be published. Required fields are marked *