Панель управления

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

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

// миникарта
package main {
     import flash.display.Sprite;
     import flash.events.*;
     import main.*;

     dynamic public class minimap extends Sprite {
           var sky_mc:sky;  // ссылка на главный класс
           var xscale:Number, yscale:Number; // масштаб карты
           var bg:Sprite;   // здесь все рисуем

           public function minimap() {
                sky_mc = root.sky;
                // вычислим масштаб
                xscale = width/sky_mc.bg.width;
                yscale = height/sky_mc.bg.height;
                // Создаем слой на котором будем все рисовать
                bg = new Sprite;
                addChild(bg);
                // Перехватываем нажатие кнопки мыши по нашему мувику
                addEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
                // и передвижение мышки
                addEventListener(MouseEvent.MOUSE_MOVE, handleMouseMove);
                // Обновляем изображение на миникарте по onEnterFrame
                addEventListener(Event.ENTER_FRAME, Update);
           }

           // обновить изображение
           public function Update(event:Event=undefined):void {
                var obj:basic_object;
                bg.graphics.clear(); // очищаем
                // рисуем рамку, текущего обзора, толщина линии 1, цвет белый, 50% прозрачность
                bg.graphics.lineStyle(1, 0xFFFFFF, 0.5);
                bg.graphics.drawRect(-sky_mc.x*xscale,-sky_mc.y*yscale,
                     stage.stageWidth*xscale,stage.stageHeight*yscale);
                // рисуем все объекты
                bg.graphics.lineStyle(0);
                for each (obj in sky_mc.all_moving) {
                     // рисуем объект если он находится внутри игровой зоны
                     if (obj.x>0 && obj.y>0 && obj.x<sky_mc.bg.width && obj.y<sky_mc.bg.height){
                          bg.graphics.beginFill(obj.type==1?0xFF0000:(obj.type==2?0x0000FF:0xFFFF00));
                          bg.graphics.drawCircle(obj.x*xscale, obj.y*yscale,
                          Math.max(1, obj.radius*(xscale+yscale)/2));
                          bg.graphics.endFill();
                     }
                }
           }

           // Нажатие кнопки мыши
           function handleMouseDown(event:Event):void {
                // вычислим минимально возможные координаты игрового поля
                var min_x:Number = stage.stageWidth-sky_mc.bg.width;
                var min_y:Number = stage.stageHeight-sky_mc.bg.height;
                // максимально возможные у нас 0:0
                // Высчитываем, какие должны быть координаты игрового поля чтобы точка клика мышки
                // находилась точно в центре основного экрана
                sky_mc.x = Math.min(0, Math.max(min_x, stage.stageWidth/2-mouseX/xscale));
                sky_mc.y = Math.min(0, Math.max(min_y, stage.stageHeight/2-mouseY/yscale));
           }
           // Перемещение мышки
           function handleMouseMove(event:MouseEvent):void {
                // если передвигают мышку по миникарте с зажатой кнопкой,
                // то двигать карту вслед за мышкой
                if (event.buttonDown){
                     handleMouseDown(event);
                }
           }
     }
}

По правильному следовало сделать свое событие "Update" по которому миникарта обновляет изображение, и рассылать это событие везде, где происходит изменение игрового поля. Но т.к. у нас всегда на поле присутствуют астероиды, двигающиеся каждый кадр, то гораздо проще и обновлять карту каждый кадр.

Добавим на панель управления некоторую статистику: количество астероидов в игре и уровень жизни земли с прогресбаром. Эту информацию обновлять в каждом кадре будет неправильно, она изменяется гораздо реже, поэтому в главный класс sky добавим функцию, которая будет рассылать событие «обновить статистику»

// Генерит событие "нужно обновить статистику"
public function doUpdateStatistic() {
     dispatchEvent(new Event("UPDATE_STATISTIC"));
}

А в панели управления будем «ловить» это событие. Нужно на панель добавить динамические текстовые поля ascteroids_label – для количества астероидов и earths_label – для уровня жизни земли. И добавим мувик HPline, который использовали для отображения уровня жизни объектов по наведению мышки, дадим ему имя Earth_HP, будет наглядно показывать уровень жизни земли. Класс панели управления для обновления статистики будет таким:

// панель управления
package main {
     import flash.display.Sprite;
     import flash.events.*;
     import main.*;

     dynamic public class panel extends Sprite {
           var sky_mc:sky;  // ссылка на главный класс

           public function panel() {
                sky_mc = root.sky;
                // Обновляем статистику по событию от главного класса
                sky_mc.addEventListener("UPDATE_STATISTIC", Update);
           }

           // обновить статистику
           public function Update(event:Event):void {
                // Количество астероидов в игре (один объект в all_moving – это сама земля)
                this.ascteroids_label.text = sky_mc.all_moving.length>1 ? sky_mc.all_moving.length-1 : "";
                // Уровень жизни земли
                this.earths_label.text = sky_mc.earth.HP;
                // Прогресбар уровня жизни земли
                this.Earth_HP.gotoAndStop(Math.floor(sky_mc.earth.HP/sky_mc.earth.maxHP*100)+1);
           }
     }
}

И теперь главное, для чего нужна панель управления, создание комических кораблей.
 

Турели

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

Турелей будет несколько видов, но все они имеют некоторые общие свойства, их и стоит описать в базовом классе turret_object, от которого будут наследоваться классы, описывающие поведения конкретных турелей.

Кроме общих свойств у всех наших космических объектов: радиус, масса… турели еще имеют радиус поражения, скорость перезарядки пушки.

// базовый класс всех турелей
dynamic public class turret_object extends basic_object {
     public var trgt_radius :Number;     // радиус поражения цели
     public var max_weapon_recharge:Number;     // максимальное значение заряда пушки
     var weapon_recharge:Number// счетчик перезарядки пушки

Для определения какую именно турель игрок добавил на поле с панели управления, добавим еще тип турели.

     public var turret_type :Number;     // тип турели: 1-лазерная, 2-ракетная

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

     public var trgt        :basic_object;      // цель

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

// Переопределяем функцию "Переместиться"
override public function move():void {
     // передвигаться турель может только если ее толкнули
     x += Velocity.x;
     y += Velocity.y;
     // "гасим" скорость, турель пытается удержаться на своей позиции
     if (Math.abs(Velocity.x)>0.4) Velocity.x *= 0.93;
     else Velocity.x=0;
     if (Math.abs(Velocity.y)>0.4) Velocity.y *= 0.93;
     else Velocity.y=0;

Если пушка еще не заряжена, то заряжаемся, и отображаем процесс зарядки. На мувик турели добавляем отображение процента заряда пушки, это мувик recharge_mc в котором, как обычно, 100 кадров, что равно проценту заряда.

     // перезарядка пушки
     if (weapon_recharge < max_weapon_recharge){
           weapon_recharge++;
     }
     // отобразим процент заряда пушки
     if (max_weapon_recharge>0){
           this.bg.recharge_mc.gotoAndStop(Math.floor(weapon_recharge/max_weapon_recharge*100)+1);
     }

Далее работаем с целью. Если у цели нет HP (уничтожена), если цель вышла за радиус поражения, то нужно про нее забывать и искать новую цель.

     if (trgt){ // есть цель
           if (!(trgt.HP>0)){ // у цели нет HP... уничтожена?
                trgt = undefined; // будем искать другую
           } else { // цель еще жива
                // создадим вектор до цели
                v = new Vector(trgt.x-x, trgt.y-y);
                if (v.magnitude() > trgt_radius){ // цель ушла из радиуса поражения
                     trgt = undefined; // будем искать другую
                } else {
                     // с целью все ok

Теперь нужно повернуть ствол в сторону цели.

                     // повернемся к цели
                     // на сколько нужно повернуться до цели
                     angle = v.getDirection() - this.bg.rotation;
                     if (Math.abs(angle)>0){
                            // за раз поворачиваемся не более чем на 10 градусов
                            this.bg.rotation += Math.abs(angle)>10?(angle>0?10:-10):angle;
                     }

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

                     if (Math.abs(angle)<=5 && weapon_recharge>=max_weapon_recharge){
                            // пушка наведена на цель и заряжена
                            Fire();      // ПЛИИИИИИ!!!!!
                     }
                }
           }
     }

Если цель отсутствует, то вызываем функцию поиска новой цели.

     if (! trgt){ // нужно искать новую цель
           FindTarget();
     }

При тестировании обнаружилась проблема, связанная с углом поворота. Rotation измеряется в градусах от -180 до 180. И если, к примеру, цель находится в -175 градусов, а мы повернуты на 175, то в результате angle будет равен —350 градусов! Турель вместо того, чтобы повернуться на -10 градусов, начнет разворачиваться в другую сторону.

Решается это просто, если результат поворота получился по модулю больше 180 градусов, что не возможно, то отнимаем один оборот (360 градусов):

                     // проблема с переходом -180...180
                     if (angle>180){
                            angle-=360;
                     } else if (angle<-180){
                            angle+=360;
                     }

Поиск цели.
Турель должна выбрать ближайшую цель из области поражения, как предоставляющую наибольшую угрозу. Для этого пробегаемся по секторам, которые перекрывает область поражения, собираем оттуда в массив все астероиды (турель атакует только астероиды, а не другие турели или землю :), и сортируем в зависимости от расстояния, затем берем ближайшую. Стоит учесть, что цель, которая приближается к нам, должна быть приоритетней той, которая удаляется. Поэтому, оцениваем не текущее расстояние до цели, а с учетом скорости движения объекта. И астероид с меньшим количеством HP так же приоритетнее полностью «здорового», т.к. его быстрее уничтожить. Значит, берем расстояние, плюс скорость, плюс HP:

// Ищет цель в радиусе trgt_radius
public function FindTarget():void {
     var i:Number, j:Number;
     var obj:basic_object, s:String;
     var v:Vector;
     var list:Array = new Array(); // список возможных целей
     var o:Object;

     // пробегаемся по всем секторам, которые попадают в радиус поиска цели
     for (i=Math.floor((x-trgt_radius)/100); i<=Math.floor((x+trgt_radius)/100); i++){
           for (j=Math.floor((y-trgt_radius)/100); j<=Math.floor((y+trgt_radius)/100); j++){
                // название сектора
                s = i+"_"+j;
                for each (obj in parent.all_sectors[s]){ // все объекты в секторе
                     if (obj.type==1 && obj.HP>0){ // нас интересуют только "живые" астероиды
                            // получим основные характеристики для выбора
                            o = new Object();
                            o.obj = obj;
                            // создадим вектор от нас до цели
                            v = new Vector(obj.x-x, obj.y-y);
                            // добавим скорость
                            v.addVector(obj.Velocity);
                            // оцениваем по расстоянию до объекта, плюс HP
                            o.len = v.magnitude() + obj.HP;
                            // добавляем в массив возможных целей
                            list.push(o);
                     }
                }
           }
     }
     if (! list.length){ // не обнаружилось ни одной цели
           return;
     }
     // цели отсортируем по расстоянию
     list.sortOn("len", Array.NUMERIC);
     // берем "ближайшую"
     trgt = list[0].obj;
}

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

// Выстрел по цели
public function Fire():void {
     // функция переопределяется в последующих классах турелей
}

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

// Переопределяем функцию отображения информации об объекте
// добавим в турель показ зоны поражения
override public function ShowInfo(e:Event=undefined):void {
     graphics.clear();
     graphics.lineStyle(3, 0x0000FF, 0.3);
     graphics.drawCircle(0,0, trgt_radius);
     // вызываем функцию ShowInfo предка
     super.ShowInfo(e);
}
// при убирании информации об объекте нужно стереть круг
override public function HideInfoNow():void {
     graphics.clear();
     super.HideInfoNow();
}

Теперь можно создать первую турель.

Лазерная.

// лазерная турель
package main {
     import main.*;

     dynamic public class turret_laser extends turret_object {
           public function turret_laser() {
                turret_type = 1; // тип турели - лазерная
                trgt_radius = 200; // радиус поражения целей 2 сектора
                HP=maxHP=50; // уровень жизни маленький
                max_weapon_recharge=21; // время перезаряда пушки - одна секунда (21 кадр)
           }

           // Выстрел по цели trgt
           override public function Fire():void {
                // создаем лазерный луч до цели
                var ls:laser = new laser();
                ls.init(this, trgt);
                // добавляем на главный мувик sky _под_ всеми объектами
                parent.addChildAt(ls, 2);
                // снимаем HP у цели
                trgt.HP -= 20;
                // запустить процесс перезарядки пушки
                weapon_recharge = 0;
           }

           // Масса лазерной турели небольшая
           override public function getMassa():Number {
                return 100;
           }
     }
}

Тут все просто, определяем основные параметры и в функции выстрела создаем мувик лазерного луча, который добавляем к родителю (класс sky) под все остальные объекты (нулевой слой - это небо, первый - земля),

parent.addChildAt(ls, 2);

Нужно лазерный луч положить именно под объекты, чтобы он рисовался не поверх (функция addChild добавляет мувик на самый верх) турели и астероида

а под ней.

Тогда визуально все выглядит, словно лазерный луч вылетает из ствола турели.

Ну и создаем сам мувик лазерного луча, это простая белая полоска длиной в 100 пикселей, со своим классом:

// Луч лазера
package main {
     import flash.display.MovieClip;
     import flash.filters.GlowFilter;
     import flash.geom.ColorTransform;
     import main.Vector;

     dynamic public class laser extends MovieClip {
           var my_asteroid:basic_object;

           // ссылка на турель и на астероид
           public function init(turret:turret_object, asteroid:basic_object) {
                var x2:Number, y2:Number;
                var v:Vector;
                my_asteroid = asteroid; // запоминаем ссылку на астероид
                v = new Vector(asteroid.x - turret.x, asteroid.y - turret.y);
                // длина луча лазера равна расстоянию до цели
                width = v.magnitude();
                // поворачиваемся к цели
                rotation = v.getDirection();
                // позиция из центра турели
                x = turret.x;
                y = turret.y;
                // создаем яркое свечение вокруг луча
                var myFilters:Array = new Array();
                myFilters.push(new GlowFilter(0xDFF4FD, 0.9));
                filters = myFilters;
                // "засветим" цель белым
                my_asteroid.transform.colorTransform = new ColorTransform(1,1,1, 1, 100,100,100, 0);
          }

           // Самоудалиться. Вызов этой функции прописан в последнем кадре
           public function remove():void {
                // вернем естественный цвет астероиду
                my_asteroid.transform.colorTransform = new ColorTransform(1,1,1, 1, 0,0,0, 0);
                if (parent){ parent.removeChild(this) }
           }
     }
}

В функции init выставляем размеры луча и координаты, создаем яркое свечение, чтобы был похож на луч, а не на полоску и чтобы визуально отметить цель, делаем ее «белее», т.е. добавляем к цвету всего мувика по 100 красного, зеленого и синего (функция ColorTransform). В функции удаления лазерного луча, возвращаем нормальный цвет астероиду.  

Итак, у нас есть готовая лазерная турель, нужно ее разместить на панели управления, сделать возможность перетащить на поле боя и написать функцию добавления турели в игру. Размещаем турель на панели управления с именем "t1". В функцию создания панели управления дописываем код для перетаскивания турелей с панели управления на поле боя:

public function panel() {p
     . . .
     // даем возможность "перетащить" турели с панели управления на игровое поле
     var i:Number = 1;
     while (this["t"+i]){ // есть такая турель
           // запомним координаты, чтобы после перетаскивания вернуть на место
           this["t"+i].old_x = this["t"+i].x;
           this["t"+i].old_y = this["t"+i].y;
           this["t"+i].HP = this["t"+i].maxHP = 0; // нет полоски с HP
           // для перетаскивания перехватываем нажатие и отпускание кнопки мышки
           this["t"+i].addEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
           this["t"+i].addEventListener(MouseEvent.MOUSE_UP, handleMouseUp);
           i++;  // переходим к следующей турели
     }
}

function handleMouseDown(event:Event):void {
     // по какому именно объекту кликнули записано в событии: event.currentTarget
     // начнем перетаскивание
     event.currentTarget.startDrag(true);
     // поднимем эту турель на самый верх, чтобы при перетаскивании
     // она не проходила под другими объектами
     setChildIndex(event.currentTarget, numChildren-1);
}

function handleMouseUp(event:Event):void {
     // прекратили перетаскивание
     event.currentTarget.stopDrag();
     // запомним координаты
     var x:Number = event.currentTarget.x;
     var y:Number = event.currentTarget.y;
     // вернем турель на место
     event.currentTarget.x = event.currentTarget.old_x;
     event.currentTarget.y = event.currentTarget.old_y;
     // если кинули правее панели управления, т.е. на игровую область
     if (x>100){ // то добавим эту турель в игру
           sky_mc.addTurret(x-sky_mc.x, y-sky_mc.y, event.currentTarget.turret_type);
     }
}

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

// Добавить турель в игру
public function addTurret(x:Number, y:Number, turret_type:Number) {
     var obj:turret_object;
     if (turret_type==1){
           obj = new turret_laser();
     }
     // зададим координаты
     obj.x = x;
     obj.y = y;
     addChild(obj)// добавляем на наш мувиклип
     addNewObj(obj); // добавляем в сектора
     // обновить статистику
     doUpdateStatistic();
}

В этой функции все просто, но есть один серьезный нюанс. Игрок может бросить турель на любой другой объект в игре, хоть в центр планеты. Необходимо, перед тем как добавить турель проверить, не пересекается ли она с другими объектами, и отодвинуть на свободное место в случае такой необходимости.

     . . .
     addChild(obj)// добавляем на наш мувиклип

     // теперь двигаем турель так, чтобы она не пересекалась ни с одинм объектом на поле
     var check_again:Boolean;
     var cnt:Number=0;
     var s:String, obj2:basic_object;
     do{
           check_again=false;
           // Просим пересчитать в какие сектора попала турель
           obj.CalcSectors();
           for (s in obj.sectors)
                if (!check_again) { // еще не обнаружили столкновение
                     // проверяем на столкновение со всеми объектами в секторе
                     for each (obj2 in all_sectors[s]) {
                            if (obj.CheckCollision(obj2)){ // столкнулись
                                   // отодвинем, чтобы не пересекались
                                   PullBalls(obj, obj2, cnt);
                                   // проверяем на столкновения повторно
                                   check_again=true;
                                   // турель сдвинута, дальше проверять эти сектора нет смысла
                                   break;
                            }
                     }
                }
           cnt++;
     } while (check_again);
     // Разместили турель так, что она ни с кем не пересекается

     addNewObj(obj); // добавляем в сектора
     . . .

Здесь мы использовали цикл с неопределенным условием завершения. Проверяем, нет ли столкновения, если есть, отодвинуть турель и проверить все заново. Стоит задуматься, а не получится ли, что этот цикл будет бесконечным? Т.е. турель будет бесконечно отодвигаться и так и не найдет себе свободного места. Не рассматриваем случай, когда все поле завалено астероидами, это невозможно,… а вот в случае добавления турели (3) строго на одной линии между двумя астероидами (1 и 2):

При первой проверке, обнаруживается столкновение с астероидом (1), турель(3) отодвигается вправо, и проверяем все заново. При второй проверке обнаруживается столкновение с астероидом (2), турель отодвигается влево, и проверяем все заново. Получаем бесконечный цикл!

Или другой, более вероятный случай, турель (4) попадает внутрь треугольника из астероидов (1,2,3):

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

Возможны и другие случаи...

Что делать, чтобы избежать зацикливаний в подобных случаях? Я решил добавлять рандом к координате турели, и чем больше циклов проверки прошло, тем рандом больше и больше. Обратите внимание на переменную cnt в коде добавления турели. Она увеличивается на 1 при каждом проходе цикла проверки, и передается в функцию PullBalls. Там я случайно изменю координату турели и задаю небольшую начальную скорость, т.е. турель словно выпрыгнет из такого капкана астероидов:

// Отодвигает объект 1 от объекта 2, чтобы они не пересекались
function PullBalls(ball1:basic_object, ball2:basic_object, add_random:Number=0):void {
     var v:Vector = new Vector(ball1.x-ball2.x, ball1.y-ball2.y);
     . . .
     if (add_random>0){ // добавить небольшой рандом в положение ball1
           ball1.Velocity.addVector(v.getUnitVector());
           ball1.x += (Math.random()-0.5)*add_random;
           ball1.y += (Math.random()-0.5)*add_random;
     }
}

С лазерной турелью все. Можно тестировать, что получилось: добавлена панель управления с миникартой, можно добавлять на игровое поле лазерные турели (перетащить мышкой), которые самостоятельно ищут цели, поворачиваются к ним и уничтожают лазерным лучом. При добавлении лазерной турели поверх какого-либо объекта, она размещается рядом в свободной области.

Добавим еще два вида турелей.

Ракетная турель

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

В самом классе ракетной турели ничего интересного, только инициализация параметров:

// ракетная турель
package main {
     import main.*;

     dynamic public class turret_rocket extends turret_object {

           public function turret_rocket() {
                turret_type = 2; // тип турели - ракетная
                trgt_radius = 350; // радиус поражения большой - целей 3.5 сектора
                HP = maxHP = 25; // уровень жизни совсем маленький
                max_weapon_recharge=21*3; // время перезаряда пушки - 3 секунды
           }

           // Выстрел по цели trgt
           override public function Fire():void {
                // создаем ракету
                var r:rocket = new rocket();
                r.init(this, trgt);
                parent.addChild(r);      // добавляем на главный мувик sky
                parent.addNewObj(r);     // добавляем в сектора
                // запустить процесс перезарядки пушки
                weapon_recharge = 0;
           }

           // Масса в 2 раза больше, чем у лазерной турели
           override public function getMassa():Number {
                return 200;
           }
     }
}

В выстреле создаем новый объект «ракета», инициализируем параметры и добавляем ее к своему родителю (главный класс sky).

А теперь определимся с поведением ракеты. Она должна появиться рядом с турелью по направлению к цели. В начале движения скорость маленькая, затем быстро нарастает. При каждом смещении корректируется вектор на цель. Как только подлетит близко к цели – взрывается, нанося ей серьезный урон. В случае потери цели (ушла из игровой зоны или уже уничтожена), ракета еще какое-то время летит прямо и затем взрывается. Уровень жизни практически отсутствует 1 HP, т.е. ракета будет сразу взрываться при первом же столкновении с любым объектом.

// Ракета
package main {
     import main.*;

     dynamic public class rocket extends basic_object {
           var trgt:basic_object;         // цель
           var self_destruction:Number;   // счетчик на самоуничтожение
           var speed:Number;               // текущая скорость
           const MAX_SPEED:Number = 12;   // максимальная скорость

           // ссылка на турель и на астероид
           public function init(turret:turret_object, asteroid:basic_object) {
                type=4;     // тип "ракета"
                // вычислим наш радиус, основываясь на размере картинки
                radius = Math.floor((width+height-4)/4);
                // добавляем себя рядом с турелью
                Velocity.setMembers(asteroid.x-turret.x, asteroid.y-turret.y);
                Velocity.mulScalar( (turret.radius+radius) / Velocity.magnitude() );
                x = turret.x + Velocity.x;
                y = turret.y + Velocity.y;
                // запоминаем ссылку на астероид
                trgt = asteroid;
                // жизни практически нет, взрываемся от малейшего касания
                HP=maxHP=1;
                // начинаем разгон
                speed=1;
                // умираем через полторы секунды после потери цели
                self_destruction=40;
                // сразу делаем "первый шаг", чтобы отлететь от турели
                // и не произошло столкновения с ней
                move();
           }

           // Переместиться
           override public function move():void {
                if (!trgt || !trgt.HP){  // цель пропала или уничтожена, запускаем процесс самоуничтожения
                     if (self_destruction-- <= 0){ // время вышло
                            HP = 0; // взрываемся
                     }
                } else { // цель есть
                     // вектор скорости у нас постоянно меняется
                     // ракета летит за целью кратчайшим путем
                     Velocity.setMembers(trgt.x-x, trgt.y-y);
                     // вычислим расстояние до цели
                     var len:Number = Velocity.magnitude();
                     // набираем скорость
                     if (speed < MAX_SPEED){
                            speed += Math.min(2, Math.max(0.3, 1/(MAX_SPEED-speed)));
                     }
                     // приведем вектор к текущей скорости
                     Velocity.mulScalar( speed / len );
                     // поворачиваемся к цели
                     this.bg.rotation = Velocity.getDirection();
                     if (len <= trgt.radius+radius+speed){ // подлетели вплотную, взрываемся
                            trgt.HP -= 400; // цели наносим серьезный дамаг
                            HP = 0;
                     }
                }
                // летим
                x += Velocity.x;
                y += Velocity.y;
           }

           // Масса ракеты используется при расчете ударной волны при взрыве
           // поэтому ставим большую, словно взорвался большой астероид
           override public function getMassa():Number {
                return 200;
           }
     }
}

Пришлось ракете задать большую массу, т.к. при взрыве сила ударной волны зависит от массы (см. выше функция addExplosion). Рисуем схематичное изображение ракеты и задаем ей класс main.rocket

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

. . .
if (obj.type==4 || obj.type==3){ // у ракеты и турелей свой взрыв
     // создаем мувик взрыва ракеты
     ex_mc = new explosion_rocket();
} else { // обычный "пылевой" взрыв
     // аттачим несколько случайных осколков (5-15)
     j = Math.max(5, Math.floor(Math.random()*15));
     while (j--){
           fragment = new small_asteroid(); // создаем новый осколок
           fragment.init(obj);     // инициализация параметров
           addChild(fragment);     // добавляем его на наш мувиклип
     }
     // создаем мувик взрыва
     ex_mc = new explosion();
}
ex_mc.init(obj);     // координаты взрыва
addChild(ex_mc);     // добавляем на мувиклип
// учитываем влияние ударной волны взрыва на другие объекты
addExplosion(obj.x, obj.y, obj.getMassa());
. . .

По ракетной турели все. Для разнообразия сделаем еще одну «турель».

Турель-Крепость

Совсем простенькая, даже не турель, так как не стреляющая, но очень полезная. Большая по размеру, большая по массе, долгоживущая, выступающая только в роли крепостной стены – преграды на пути астероидов. Программировать в ней нечего, нужно только прописать параметры. Для красоты, добавим в нее пульсирующее свечение вокруг фоновой картинки, словно пульсирует защитное поле.

// турель-крепость
package main {
     import flash.filters.GlowFilter;

     dynamic public class turret_bastion extends turret_object {
           var phosphorescence:Number;    // скорость пульсации свечения
           var my_filter:Array;           // само свечение

           public function turret_bastion() {
                turret_type = 3;   // тип турели - крепость
                trgt_radius = 0;   // не имеет радиуса поражения
                HP = maxHP = 80;   // уровень жизни большой (с учетом массы)
                phosphorescence = 0.02;  // скорость изменения свечения
                // создаем свечение
                my_filter = new Array();
                my_filter.push(new GlowFilter(0x4099E3, 0.5, 20,20));
           }

           // Масса большая
           override public function getMassa():Number {
                return 800;
           }

           // Добавим изменение свечения защитного экрана
           override public function move():void {
                var al:Number = my_filter[0].alpha; // текущая прозрачность свечения
                if (al+phosphorescence>0.8 || al+phosphorescence<0.2){ // выходим за границы
                     phosphorescence *= -1;     // меняем направление изменения прозрачности
                }
                // меняем интенсивность свечения
                my_filter[0].alpha = al+phosphorescence;
                this.bg.filters = my_filter;
                // вызываем базовую функцию move
                super.move();
           }
     }
}


Страницы: 1 2 3 [4] 5