Перед тем, как вводить в игру защитные космические корабли, нужно создать панель управления, с помощью которой можно будет эти корабли добавлять. На панели нужно разместить миникарту, какую-то статистическую информацию (очки) и выбор, какой корабль добавить. Т.к. наша флешка по ширине больше, чем по высоте, то стоит расположить панель управления сбоку, например, слева.
Для начала сделаем миникарту. На ней нужно нарисовать все объекты и рамку текущей видимой области. Так же при клике мышкой по миникарте нужно позиционировать в это место игровое поле. Добавим и возможность перемещать игровое поле, перетаскивая мышку по миникарте с зажатой кнопкой.
// миникарта
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