В предыдущем уроке мы преследовали самонаводящиеся ракеты после одной цели. Из этого туториала вы узнаете, как преобразовать ваши самонаводящиеся ракеты в тепловые ракеты для нескольких целей.
Если вы не читали первое руководство по Homing Missile , вы можете скачать этот файл .zip , который содержит исходный код, с которого мы начнем в этом руководстве.
Окончательный результат предварительного просмотра
Давайте посмотрим на конечный результат, к которому мы будем стремиться:
Шаг 1: Изменить рисунок пушки
Единственный мувиклип в Библиотеке, который нам нужно изменить, — это « Пушка» , так как мы нацелим ее на ближайшую цель перед стрельбой. Помните, что поворот на 0 ° означает направление вправо, поэтому сделайте изображение соответствующим образом.

Шаг 2: Объявите переменные расстояния для пушки
Я собираюсь повторно использовать переменные targetX и targetY для вычисления расстояния от цели до пушки, поэтому я объявлю их в начале класса, а не внутри функции playGame , а также новую переменную для хранения Расчетное расстояние:
|
01
02
03
04
05
06
07
08
09
10
11
12
|
private var missile:Missile = new Missile();
private var speed:int = 15;
private var cannon:Cannon = new Cannon();
private var missileOut:Boolean = false;
private var ease:int = 10;
private var target:Target = new Target();
private var floor:int = 385;
private var gravity:Number = 0.5;
private var targetVY:Number = 0;//Current vertical velocity of the target
private var distance:int;
private var targetX:int;
private var targetY:int;
|
Теперь переменные targetX и targetY будут объявлены для функции playGame :
|
01
02
03
04
05
06
07
08
09
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
|
private function playGame(event:Event):void
{
if (missileOut)
{
if (missile.hitTestObject(target))
{
var explosion:Explosion = new Explosion();
addChild(explosion);
explosion.x = missile.x;
explosion.y = missile.y;
removeChild(missile);
missileOut = false;
}
else
{
targetX = target.x — missile.x;
targetY = target.y — missile.y;
var rotation:int = Math.atan2(targetY, targetX) * 180 / Math.PI;
if (Math.abs(rotation — missile.rotation) > 180)
{
if (rotation > 0 && missile.rotation < 0)
missile.rotation -= (360 — rotation + missile.rotation) / ease;
else if (missile.rotation > 0 && rotation < 0)
missile.rotation += (360 — rotation + missile.rotation) / ease;
}
else if (rotation < missile.rotation)
missile.rotation -= Math.abs(missile.rotation — rotation) / ease;
else
missile.rotation += Math.abs(rotation — missile.rotation) / ease;
var vx:Number = speed * (90 — Math.abs(missile.rotation)) / 90;
var vy:Number;
if (missile.rotation < 0)
vy = -speed + Math.abs(vx);
else
vy = speed — Math.abs(vx);
missile.x += vx;
missile.y += vy;
}
}
targetVY += gravity;
target.y += targetVY;
if (target.y > floor)
{
target.y = floor;
targetVY = -18;
}
}
|
Шаг 3: Направьте пушку на цель
Ранее в функции playGame нас интересовало только то, готова ли ракета к вращению и движению. Теперь нам нужно сначала узнать, не выпущена ли ракета, и обновить вращение пушки.
|
01
02
03
04
05
06
07
08
09
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
|
private function playGame(event:Event):void
{
if (!missileOut)
{
targetX = target.x — cannon.x;
targetY = target.y — cannon.y;
cannon.rotation = Math.atan2(targetY, targetX) * 180 / Math.PI;
}
else
{
if (missile.hitTestObject(target))
{
var explosion:Explosion = new Explosion();
addChild(explosion);
explosion.x = missile.x;
explosion.y = missile.y;
removeChild(missile);
missileOut = false;
}
else
{
targetX = target.x — missile.x;
targetY = target.y — missile.y;
var rotation:int = Math.atan2(targetY, targetX) * 180 / Math.PI;
if (Math.abs(rotation — missile.rotation) > 180)
{
if (rotation > 0 && missile.rotation < 0)
missile.rotation -= (360 — rotation + missile.rotation) / ease;
else if (missile.rotation > 0 && rotation < 0)
missile.rotation += (360 — rotation + missile.rotation) / ease;
}
else if (rotation < missile.rotation)
missile.rotation -= Math.abs(missile.rotation — rotation) / ease;
else
missile.rotation += Math.abs(rotation — missile.rotation) / ease;
var vx:Number = speed * (90 — Math.abs(missile.rotation)) / 90;
var vy:Number;
if (missile.rotation < 0)
vy = -speed + Math.abs(vx);
else
vy = speed — Math.abs(vx);
missile.x += vx;
missile.y += vy;
}
}
targetVY += gravity;
target.y += targetVY;
if (target.y > floor)
{
target.y = floor;
targetVY = -18;
}
}
|
Теперь пушка вращается относительно позиции цели.
Шаг 4: Совместите вращение ракеты с пушкой.
Пушка вращается, но ракета продолжает стрелять вверх. Замените жестко закодированное вращение текущим местоположением пушки в момент выстрела ракеты.
|
01
02
03
04
05
06
07
08
09
10
11
12
|
private function shoot(event:MouseEvent):void
{
if (!missileOut)
{
addChild(missile);
swapChildren(missile, cannon);//missile will come out from behind cannon
missileOut = true;
missile.x = cannon.x;
missile.y = cannon.y;
missile.rotation = cannon.rotation;
}
}
|
Теперь ракета будет выглядеть так, будто она действительно выходит из пушки.
Шаг 5: более чем одна цель
Прямо сейчас самонаводящаяся ракета — это программа, преследующая одну цель, но что, если у нас будет больше целей? Как это решит, какой идти после?
Сначала давайте решим, сколько будет целей, затем поместим каждую цель в массив. В этом примере я собираюсь сказать, что есть 2 цели, и я назначу каждой цели случайную позицию на экране.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
private var target:Target;
private var floor:int = 385;
private var gravity:Number = 0.5;
private var targetVY:Number = 0;//Current vertical velocity of the target
private var distance:int;
private var targetX:int;
private var targetY:int;
private var numTargets:int = 2;
private var targets:Array = [];
public function Main()
{
addChild(cannon);
cannon.x = 50;
cannon.y = 380;
addEventListener(Event.ENTER_FRAME, playGame);
stage.addEventListener(MouseEvent.CLICK, shoot);
for (var i:int = 0; i < numTargets; i++)
{
target = new Target();
addChild(target);
target.x = Math.random() * 600;
target.y = Math.random() * 400;
targets.push(target);
}
}
|
Теперь у нас есть несколько целей на экране.

Ракета все еще только подтверждает существование одной цели. Мы исправим это дальше.
Шаг 6: Ищите ближайшую цель
У нас есть ракета, ищущая переменную цели , поэтому давайте проверим массив targets и посмотрим, какая из них ближе. Целевая переменная будет ссылаться на ближайшую в начале функции playGame .
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
private function playGame(event:Event):void
{
for (var i:int = 0; i < targets.length; i++)
{
targetX = targets[i].x — missile.x;
targetY = targets[i].y — missile.y;
var dist:int = Math.sqrt(targetX * targetX + targetY * targetY);//the distance from one point to another in a 2D space.
if (i == 0 || dist < distance)
{
distance = dist;
target = targets[i];
}
}
|
На данный момент ближайшая цель — единственная движущаяся, но ракета признает существование обоих:
Шаг 7: исправить цель пушки
Возможно, вы заметили, что в то время как ракета действительно ищет ожидаемую цель, пушка застряла, указывая на ту же цель, независимо от того, ближе она или дальше, чем другая. Расстояние устанавливается в зависимости от положения ракеты, поэтому, если ракеты на сцене нет, нам нужно обновить ее положение, чтобы оно соответствовало положению пушки, чтобы она всегда знала, какая из них ближе.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
private function playGame(event:Event):void
{
for (var i:int = 0; i < targets.length; i++)
{
targetX = targets[i].x — missile.x;
targetY = targets[i].y — missile.y;
var dist:int = Math.sqrt(targetX * targetX + targetY * targetY);
if (i == 0 || dist < distance)
{
distance = dist;
target = targets[i];
}
}
if (!missileOut)
{
missile.x = cannon.x;
missile.y = cannon.y;
targetX = target.x — cannon.x;
targetY = target.y — cannon.y;
cannon.rotation = Math.atan2(targetY, targetX) * 180 / Math.PI;
}
|
Теперь пушка всегда будет стремиться к ближайшей цели.
Шаг 8: переместить пушку
Перед выстрелом ракеты пушка уже указывает на ближайшую цель и изменит направление, если приблизится к другой цели. Давайте добавим пару строк, чтобы расположить пушку с помощью курсора мыши.
|
1
2
3
4
|
private function playGame(event:Event):void
{
cannon.x = mouseX;
cannon.y = mouseY;
|
Теперь вы можете свободно перемещать пушку.
Шаг 9: попадание в цель, уничтожение цели
Чтобы сделать это более динамичным, я собираюсь переместить цель после удара ракетой или заменить ее новой и оставить экземпляр Explosion на своем месте.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
if (missile.hitTestObject(target))
{
var explosion:Explosion = new Explosion();
addChild(explosion);
explosion.x = missile.x;
explosion.y = missile.y;
removeChild(missile);
missileOut = false;
explosion= new Explosion();
addChild(explosion);
explosion.x = target.x;
explosion.y = target.y;
explosion.scaleX = explosion.scaleY = 1.5;
target.x = Math.random() * 600;
}
|
Вот что вы получите:
Шаг 10: несколько ракет
Мы сделали несколько целей, поэтому теперь мы можем делать несколько ракет одинаково. Разница здесь в том, что все ракеты должны двигаться все время, пока они не попали, и мы фактически собираемся удалить те, которые уже взорвались, поэтому нам нужно изменить несколько строк нашего кода, чтобы это работало. Во-первых, нам понадобится массив для ракет.
|
1
|
private var missiles:Array = [];
|
Затем нам нужно убедиться, что все ракеты ведут себя правильно:
|
01
02
03
04
05
06
07
08
09
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
private function playGame(event:Event):void
{
cannon.x = mouseX;
cannon.y = mouseY;
for (var i:int = 0; i < targets.length; i++)
{
targetX = targets[i].x — missile.x;
targetY = targets[i].y — missile.y;
var dist:int = Math.sqrt(targetX * targetX + targetY * targetY);
if (i == 0 || dist < distance)
{
distance = dist;
target = targets[i];
}
}
if (!missileOut)
{
missile.x = cannon.x;
missile.y = cannon.y;
targetX = target.x — cannon.x;
targetY = target.y — cannon.y;
cannon.rotation = Math.atan2(targetY, targetX) * 180 / Math.PI;
}
else
{
for (i = 0; i < missiles.length; i++)//each missile must keep moving
{
missile = missiles[i];
if (missile.hitTestObject(target))
{
var explosion:Explosion = new Explosion();
addChild(explosion);
explosion.x = missile.x;
explosion.y = missile.y;
removeChild(missile);
missiles.splice(i, 1);//out of the Array
if (missiles.length < 1)//only if no missiles are out at all
missileOut = false;
explosion= new Explosion();
addChild(explosion);
explosion.x = target.x;
explosion.y = target.y;
explosion.scaleX = explosion.scaleY = 1.5;
target.x = Math.random() * 600;
}
else
{
targetX = target.x — missile.x;
targetY = target.y — missile.y;
var rotation:int = Math.atan2(targetY, targetX) * 180 / Math.PI;
if (Math.abs(rotation — missile.rotation) > 180)
{
if (rotation > 0 && missile.rotation < 0)
missile.rotation -= (360 — rotation + missile.rotation) / ease;
else if (missile.rotation > 0 && rotation < 0)
missile.rotation += (360 — rotation + missile.rotation) / ease;
}
else if (rotation < missile.rotation)
missile.rotation -= Math.abs(missile.rotation — rotation) / ease;
else
missile.rotation += Math.abs(rotation — missile.rotation) / ease;
var vx:Number = speed * (90 — Math.abs(missile.rotation)) / 90;
var vy:Number;
if (missile.rotation < 0)
vy = -speed + Math.abs(vx);
else
vy = speed — Math.abs(vx);
missile.x += vx;
missile.y += vy;
}
}
}
targetVY += gravity;
target.y += targetVY;
if (target.y > floor)
{
target.y = floor;
targetVY = -18;
}
}
private function shoot(event:MouseEvent):void
{
missile = new Missile();
missiles.push(missile);//into the Array
addChild(missile);
swapChildren(missile, cannon);//missile will come out from behind cannon
missileOut = true;
missile.x = cannon.x;
missile.y = cannon.y;
missile.rotation = cannon.rotation;
}
|
Теперь, когда цель уничтожена, ракеты будут искать следующую цель.
В этот момент все ракеты преследуют одну и ту же цель. Чтобы каждая ракета искала свою цель, было бы лучше создать отдельный класс для ракет, в котором вы будете определять ближайшую цель индивидуально.
Шаг 11: Сделай перекрестие
На данный момент вы уже поняли основную идею этого урока, но давайте посмотрим правде в глаза, враг не будет двигаться в зависимости только от его расстояния до вас или ваших ракет. Вы можете использовать другой индикатор, например, перекрестие. Сделайте это муви клип и экспортируйте его в Actionscript.

Шаг 12: Используйте перекрестие
Теперь всем станет очевидно, на кого нацелена цель. Просто добавьте экземпляр Crosshair Movie Clip.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
private var crosshair:Crosshair = new Crosshair();
public function Main()
{
addChild(cannon);
cannon.x = 50;
cannon.y = 380;
addEventListener(Event.ENTER_FRAME, playGame);
stage.addEventListener(MouseEvent.CLICK, shoot);
for (var i:int = 0; i < numTargets; i++)
{
target = new Target();
addChild(target);
target.x = Math.random() * 600;
target.y = Math.random() * 400;
targets.push(target);
}
addChild(crosshair);
}
|
Затем поместите его на позицию цели в качестве последней инструкции в функции playGame .
|
01
02
03
04
05
06
07
08
09
10
|
targetVY += gravity;
target.y += targetVY;
if (target.y > floor)
{
target.y = floor;
targetVY = -18;
}
crosshair.x = target.x;
crosshair.y = target.y;
}
|
Вы получите перекрестие, отмечающее положение ближайшей цели.

Шаг 13: переместить все цели
Помните, что я сказал о ракетах? То же самое относится и к целям: они будут выглядеть лучше в своем классе с набором собственных инструкций. Это только быстрый пример, но в вашей игре я не рекомендую кодировать все объекты в классе Main . Чем сложнее ваша игра, тем меньше кода вы будете кодировать в классе Main .
Цели уже находятся в массиве, который уже проверяется в цикле for , поэтому я переместу прыгающие инструкции внутри цикла for , чтобы все цели, независимо от их количества, всегда перемещались одинаково.
|
01
02
03
04
05
06
07
08
09
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
81
82
83
|
private function playGame(event:Event):void
{
cannon.x = mouseX;
cannon.y = mouseY;
targetVY += gravity;
for (var i:int = 0; i < targets.length; i++)
{
targetX = targets[i].x — missile.x;
targetY = targets[i].y — missile.y;
var dist:int = Math.sqrt(targetX * targetX + targetY * targetY);
if (i == 0 || dist < distance)
{
distance = dist;
target = targets[i];
}
targets[i].y += targetVY;
if (targets[i].y > floor)
targets[i].y = floor;
}
if (target.y >= floor)
targetVY = -18;
if (!missileOut)
{
missile.x = cannon.x;
missile.y = cannon.y;
targetX = target.x — cannon.x;
targetY = target.y — cannon.y;
cannon.rotation = Math.atan2(targetY, targetX) * 180 / Math.PI;
}
else
{
for (i = 0; i < missiles.length; i++)
{
missile = missiles[i];
if (missile.hitTestObject(target))
{
var explosion:Explosion = new Explosion();
addChild(explosion);
explosion.x = missile.x;
explosion.y = missile.y;
removeChild(missile);
missiles.splice(i, 1);
if (missiles.length < 1)
missileOut = false;
explosion= new Explosion();
addChild(explosion);
explosion.x = target.x;
explosion.y = target.y;
explosion.scaleX = explosion.scaleY = 1.5;
target.x = Math.random() * 600;
}
else
{
targetX = target.x — missile.x;
targetY = target.y — missile.y;
var rotation:int = Math.atan2(targetY, targetX) * 180 / Math.PI;
if (Math.abs(rotation — missile.rotation) > 180)
{
if (rotation > 0 && missile.rotation < 0)
missile.rotation -= (360 — rotation + missile.rotation) / ease;
else if (missile.rotation > 0 && rotation < 0)
missile.rotation += (360 — rotation + missile.rotation) / ease;
}
else if (rotation < missile.rotation)
missile.rotation -= Math.abs(missile.rotation — rotation) / ease;
else
missile.rotation += Math.abs(rotation — missile.rotation) / ease;
var vx:Number = speed * (90 — Math.abs(missile.rotation)) / 90;
var vy:Number;
if (missile.rotation < 0)
vy = -speed + Math.abs(vx);
else
vy = speed — Math.abs(vx);
missile.x += vx;
missile.y += vy;
}
}
}
crosshair.x = target.x;
crosshair.y = target.y;
}
|
Взглянем:
Вывод
Самонаводящиеся ракеты, ракеты, ищущие тепло, и то, и другое — забавное и полезное оружие, которое можно использовать в стрельбе или, возможно, в каком-либо другом приложении. В этом руководстве показан пример его использования и алгоритм его создания, но для передовой практики рекомендуется иметь отдельные классы для ракет и целей, если ваше приложение не такое простое и короткое, как это.
Надеюсь, вы нашли этот урок полезным. Спасибо за прочтение!