Статьи

Поразить цель смертельной самонаводящейся ракетой

Это руководство поможет вам добавить смертельно точные самонаводящиеся ракеты в арсенал вашей следующей игры.


Давайте посмотрим на конечный результат, к которому мы будем стремиться:


Создайте новый набор документов Flash для ActionScript 3.0. Я буду использовать размеры 600×400 и частоту кадров 30 кадров в секунду. Сохраните файл с именем по вашему выбору.

Создать документ .FLA

Помимо FLA, нам также нужно создать класс документа . Создайте новый файл Actionscript и добавьте этот код:

01
02
03
04
05
06
07
08
09
10
11
12
13
package
{
    import flash.display.Sprite;
     
    public class Main extends Sprite
    {
        public function Main()
        {
             
        }
    }
     
}

Сохраните этот файл в том же каталоге, что и наш FLA. Назовите это Main.as.


Чтобы скомпилировать код из класса Main , нам нужно связать его с FLA. На панели « Свойства» FLA рядом с « Класс» введите имя класса документа, в данном случае « Основной» .

Свяжите основной класс с FLA

Затем сохраните изменения в FLA.


Нам нужно изображение ракеты, которое будет отображаться при стрельбе. Вы можете импортировать растровое изображение или нарисовать фигуру прямо на Flash. Я буду использовать крошечную форму на этом примере.

Нарисуйте форму ракеты или импортируйте растровое изображение ракеты.

Здесь важно то, что вы должны направить точку ракеты вправо, так как это точка начала вращения. Таким образом, 0 ° означает направление вправо, -90 ° — вверх, 90 ° — вниз и 180 ° — влево. Позже нам нужно будет вращать ракету в соответствии с ее направлением.


Когда у вас есть изображение ракеты, выберите его и нажмите F8, чтобы создать видеоклип. Назовите его «Ракета», убедитесь, что точка регистрации находится в центре, и установите флажок «Экспорт для ActionScript».

Создайте мувиклип из ракеты, которую вы нарисовали.

В итоге вы получите Ракетный видеоклип в библиотеке.

Ракетный мувиклип в библиотеке.

Если у вас есть экземпляр Missile на сцене, удалите его. Мы будем добавлять Missile MovieClip по коду.


Первое, что нужно знать самонаводящейся ракете, — это где находится цель. Сначала мы собираемся установить вращение ракеты в соответствии с положением курсора мыши. Давайте поработаем с событием enterFrame для постоянного обновления ротации.

Добавьте экземпляр Missile на сцену, я помещаю его в центр (300, 200). Затем вычислите расстояние от ракеты до курсора мыши (я храню его в переменных targetX и targetY ). Наконец, угол ракеты будет арктангенсом обеих точек ( targetX , targetY ). Результат, который вы получите, будет в радианах, но вращение работает в градусах, поэтому вам нужно выполнить преобразование путем умножения на 180 / Pi. (Чтобы понять почему, проверьте эту статью .)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
import flash.events.Event;
 
public class Main extends Sprite
{
    private var missile:Missile = new Missile();
     
    public function Main()
    {
        addChild(missile);
        missile.x = 300;
        missile.y = 200;
        addEventListener(Event.ENTER_FRAME, playGame);
    }
     
    private function playGame(event:Event):void
    {
        var targetX:int = mouseX — missile.x;
        var targetY:int = mouseY — missile.y;
        missile.rotation = Math.atan2(targetY, targetX) * 180 / Math.PI;
    }
}

(Не уверен, для чего предназначен Math.atan2() ? Ознакомьтесь с этой статьей о тригонометрии .

Если вы публикуете (Ctrl + Enter) документ на этом этапе, вы должны получить что-то вроде этого:

Подведите мышь к ракете, чтобы увидеть, как она вращается.


Мы получили вращение, теперь нам нужно движение. Ракета должна искать цель, независимо от того, устойчивая она или движущаяся. Что мы будем делать, так это рассчитать движение в соответствии с текущим оборотом ракеты. Давайте установим значение скорости и сделаем погоню за ракетой после курсора мыши.

Мы включим пару новых переменных для расчета скорости ( vx , vy ). Когда ракета направлена ​​вправо, ее угол меньше 90 ° и больше -90 °, поэтому он всегда ниже абсолютного значения 90 °. Когда он указывает налево, его угол имеет абсолютное значение выше 90 °. Это определит vx в соответствии со скоростью , тогда vy будет разницей скорости и vx .

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 speed:int = 10;
 
public function Main()
{
    addChild(missile);
    missile.x = 300;
    missile.y = 200;
    addEventListener(Event.ENTER_FRAME, playGame);
}
 
private function playGame(event:Event):void
{
    var targetX:int = mouseX — missile.x;
    var targetY:int = mouseY — missile.y;
    missile.rotation = Math.atan2(targetY, targetX) * 180 / Math.PI;
    //Velocity in x is relative to the angle, when it’s 90°
    var vx:Number = speed * (90 — Math.abs(missile.rotation)) / 90;
    var vy:Number;//Velocity in y is the difference of speed and vx.
    if (missile.rotation < 0)
        vy = -speed + Math.abs(vx);//Going upwards.
    else
        vy = speed — Math.abs(vx);//Going downwards.
     
    missile.x += vx;
    missile.y += vy;
}

Вы получите ракету, преследующую ваш курсор.

Вы можете использовать другую скорость, если хотите.


Ракеты не выходят из воздуха, они стреляют из ракетных пусковых установок. Давайте создадим MovieClip, представляющий пушку (я буду использовать простой прямоугольник), и назовем его Cannon . Я собираюсь добавить экземпляр Cannon по коду, поэтому я буду держать сцену пустой.

Cannon MovieClip в библиотеке.

Теперь, вместо того, чтобы добавить ракету, я просто добавлю пушку, и ракета будет добавлена ​​в положение пушки, когда я нажму на сцену. Мы добавим логическое значение, чтобы проверить, была ли выпущена ракета, а также новую функцию для стрельбы после щелчка.

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
import flash.events.MouseEvent;
 
public class Main extends Sprite
{
    private var missile:Missile = new Missile();
    private var speed:int = 10;
    private var cannon:Cannon = new Cannon();
    private var missileOut:Boolean = false;//Has the missile been shot?
     
    public function Main()
    {
        addChild(cannon);
        cannon.x = 50;
        cannon.y = 380;
        addEventListener(Event.ENTER_FRAME, playGame);
        stage.addEventListener(MouseEvent.CLICK, shoot);
    }
     
    private function playGame(event:Event):void
    {
        if (missileOut)
        {
            var targetX:int = mouseX — missile.x;
            var targetY:int = mouseY — missile.y;
            missile.rotation = Math.atan2(targetY, targetX) * 180 / Math.PI;
             
            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;
        }
    }
     
    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;
        }
    }

Вот что вы получите:

Это не выглядит красиво. Мы должны либо заставить пушку вращаться, либо заставить ракету подняться вверх сразу после выстрела. Поскольку вариант № 1 является самым простым подходом, мы возьмем вариант № 2.


Если орудие расположено вертикально, мы ожидаем, что ракета запустится вверх, а затем направится к своей цели. Подход, который я буду использовать для достижения этой цели, состоит в том, чтобы дать ракете стартовый угол -90 ° (направленный вверх) и плавно поворачивать, чтобы добраться до курсора мыши. Мы добавим переменную замедления, чтобы определить плавность или резкость вращения. Затем мы создадим другую переменную, чтобы отслеживать фактическое вращение, которое указывает прямо на цель, в то время как вращение ракеты будет изменяться в соответствии с установленной нами легкостью ( ease = 1 будет вести себя так же, как и раньше, все, что выше, сделает плавный поворот ).

Поскольку половина значений вращения отрицательна, в некоторых случаях нам нужно рассчитать их по 360, чтобы получить фактическую разницу между углом цели и вращением ракеты.

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
private var ease:int = 10;
 
public function Main()
{
    addChild(cannon);
    cannon.x = 50;
    cannon.y = 380;
    addEventListener(Event.ENTER_FRAME, playGame);
    stage.addEventListener(MouseEvent.CLICK, shoot);
}
 
private function playGame(event:Event):void
{
    if (missileOut)
    {
        var targetX:int = mouseX — missile.x;
        var targetY:int = mouseY — 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;
    }
}
 
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 = -90;//missile will start pointing upwards
    }
}

Проверьте это:

Обратите внимание, что происходит, когда вы перемещаете мышь из SWF-файла, и чем это отличается от предыдущего примера.


Помимо Missile Movie Clip нам нужна анимация взрыва. В моем случае я сделаю отдельный мувиклип с простой анимацией круга, которая расширяется. Я экспортирую это как Взрыв . Нажмите O, чтобы выбрать Овальный инструмент , и удерживайте Shift, пока рисуете овал, чтобы получить круг.

Круг внутри собственного мувиклипа с фильтром Bevel.

Для более приятного визуального эффекта я помещу кружок внутри другого собственного мувиклипа и добавлю ему фильтр « Скос», чтобы получить более темный цвет снизу и более светлый цвет вверху. Далее я перейду к кадру 10 и нажмите клавишу F6, чтобы создать ключевой кадр , затем щелкните правой кнопкой мыши между кадрами 1 и 10 и создайте классический анимационный ролик . Вернувшись к кадру 10, нажмите Q, чтобы выбрать инструмент « Свободное преобразование», и увеличьте круг.

Увеличить круг в классической анимации.

Затем создайте еще одну классическую анимацию для кадра 20, я добавлю эффект фильтра Blur .

Добавьте фильтр размытия в классическом анимации.

Наконец, заставьте его исчезнуть в последнем классическом анимации до кадра 30 с альфа- эффектом цвета, равным 0.

Альфа-цветовой эффект установлен на 0 в классическом анимации.

Анимация взрыва должна быть удалена после ее завершения, иначе она будет повторяться бесконечно. Добавьте новый слой и нажмите F6 в последнем кадре, затем нажмите F9, чтобы открыть панель « Действия» , и добавьте этот код:

1
stop();<br />parent.removeChild(this);

Это заставит экземпляр Explosion удалить себя после завершения анимации.

Экземпляр взрыва удалит себя.

Теперь, когда ракета встретится с курсором, мы заменим ее на экземпляр Explosion . Нам просто нужно добавить новое условие в 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
private function playGame(event:Event):void
{
    if (missileOut)
    {
        if (missile.hitTestPoint(mouseX, mouseY))
        {
            var explosion:Explosion = new Explosion();
            addChild(explosion);
            explosion.x = missile.x;
            explosion.y = missile.y;
            removeChild(missile);
            missileOut = false;
        }
        else
        {
            var targetX:int = mouseX — missile.x;
            var targetY:int = mouseY — 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;
        }
    }
}

Взглянем:


Погоня за курсором мыши была интересной, но в игре это бессмысленно; нам нужно сделать цель. Я собираюсь нарисовать кучу кругов, чтобы сформировать целевой видеоклип.

Создайте и экспортируйте целевой видеоклип.

Теперь мы добавим экземпляр Target, чтобы ракета имела более осязаемую цель. Поэтому мы заменим любую ссылку курсора мыши на позицию цели. Кроме того, мы будем проверять не точку попадания, а объект.

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
private var target:Target = new Target();
 
public function Main()
{
    addChild(cannon);
    cannon.x = 50;
    cannon.y = 380;
    addEventListener(Event.ENTER_FRAME, playGame);
    stage.addEventListener(MouseEvent.CLICK, shoot);addChild(target);
    target.x = 550;
    target.y = 50;
}
 
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
        {
            var targetX:int = target.x — missile.x;
            var targetY:int = 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;
        }
    }
}
 
private function shoot(event:MouseEvent):void
{
    if (!missileOut)
    {
        addChild(missile);
        swapChildren(missile, cannon);
        missileOut = true;
        missile.x = cannon.x;
        missile.y = cannon.y;
        missile.rotation = -90;//missile will start pointing upwards
    }
}

Метод hitTestObject() самом деле проверяет только перекрытие между ограничивающими прямоугольниками двух объектов (т.е. синими прямоугольниками, которые появляются, когда вы щелкаете по экземпляру объекта на сцене), так что следите за этим; это не идеальное пиксельное обнаружение столкновений. Тем не менее, он отлично справляется с этой задачей.

Вы можете попытаться разместить цель в разных местах, а также орудие.


Мы уже видели, что ракета будет преследовать движущуюся цель, такую ​​как курсор мыши, поэтому теперь давайте заставим экземпляр Target немного двигаться.

Это не реалистичная физика, я просто заставлю цель отскочить вертикально. Я выберу контрольную точку в качестве уровня земли и добавлю значение силы тяжести, чтобы повлиять на цель. И чтобы сделать его более динамичным, я увеличу скорость ракеты до 15.

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
private var floor:int = 385;
private var gravity:Number = 0.5;
private var targetVY:Number = 0;//Current vertical velocity of the target
 
public function Main()
{
    addChild(cannon);
    cannon.x = 50;
    cannon.y = 380;
    addEventListener(Event.ENTER_FRAME, playGame);
    stage.addEventListener(MouseEvent.CLICK, shoot);addChild(target);
    target.x = 550;
    target.y = 50;
}
 
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
        {
            var targetX:int = target.x — missile.x;
            var targetY:int = 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;
    }
}

Если вы опубликуете это сейчас, вы должны получить движущуюся цель.


Если вы хотите точную самонаводящуюся ракету или предпочитаете плавную анимацию, вы можете получить оба результата на основе этого примера. Теперь у вас есть новое оружие, которое вы можете добавить в свой арсенал, возможно, вы можете попробовать сделать игру, похожую на червяка, или даже использовать алгоритм на чем-то, кроме ракеты, например, на странном комаре, который следует за вашим персонажем.

Надеюсь, вы нашли этот урок полезным. Спасибо за прочтение!