Всем привет!
Во многих играх нам необходимо, чтобы какой-либо игровой объект поворачивался в сторону нашего курсора.
Давайте сделаем что-нибудь такое, чтобы это тоже было в нашей копилке наработок)
Что будем делать?
Я решил сделать 2 вида объекта для примера.
1-й объект — это лук со стрелой, будет с ограниченным поворотом по углам, поворачиваться будет моментально за мышкой.
2-й объект — это башня танка, у неё нет ограничений по углам поворота и она будет поворачиваться с определённой скоростью.
В результате мы хотим получить вот такую картину:
Графика
Графику мы будем использовать по такому же принципу как описано в статье Рисуем в IDE, а кодим в FD. Я заранее подготовил графику в виде 4-х png файлов:
Далее импортировал эти файлы в библиотеку созданного .fla файла. Лук и стрелу объединил в один клип, а корпус танка и башню в разные, т.к. башня будет поворачиваться отдельно от корпуса.
Все объекты в графике изначально повёрнуты вправо, т.к. это совпадает с углом флеш-объекта = 0 градусов.
Пушка танка на png повёрнута вниз, поэтому я повернул её вправа уже в самом клипе пушки.
Далее я объединяю клипы лука, корпуса танка и пушки в 1 клип (mcScene), располагаю их там как хотел бы видеть их в игре и даю имена клипам лука (bow) и пушки (tower), чтобы потом легко определить их в коде.
Паблишим библиотеку в swc и всё, на этом с графикой закончили.
Код
Создаём новый проект в FD, подключаем библиотеку swc и кодим.
Приведу результат трудов в виде листинга кода главного класса:
package
{
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Point;
import monax.MyMath;
/**
* Пример поворота объектов за курсором мыши. Специально для cyber-code.ru
* @author Monax
*/
[Frame(factoryClass="Preloader")]
public class Main extends Sprite
{
private var _bow: MovieClip;
private var _tower: MovieClip;
// текущее положение мыши на нужном объекте.
// пример: если нужно чтобы танк поворачивал дуло туда, куда указывает мышь, то этот объект - это земля, на которой находится танк
// в нашем случае - это родительский объект - этот класс
private var _mousePosX: Number;
private var _mousePosY: Number;
// параметры поворота лука
private var _bowMaxAn: Number = 60; // максимальный угол поворота = 60 градусов
private var _bowMinAn: Number = -90; // минимальный угол поворота = -90 градусов
// параметры поворота башни
private const TOWER_TURN_SPD: Number = 45; // скорость поворота башни, (градус / сек)
public function Main():void
{
if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// создаём сцену
var scene: MovieClip = new Scene_mc();
scene.x = stage.stageWidth / 2;
scene.y = stage.stageHeight / 2;
addChild(scene);
// записываем клипы лука и башни танка
_bow = scene.getChildByName("bow") as MovieClip;
_tower = scene.getChildByName("tower") as MovieClip;
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function turnTankTower(aDT: Number):void
{
/**
* можно было бы просто ориентироваться по координатам текущей сцены,
* НО это было бы справедливо только для той игровой области, которая не смещалась (например по причине двигающейся камеры в игре)
* поэтому я сделаю расчёт на это сразу. т.е. алгоритм будет универсальным и более общим, а не частным.
*
* Мы переведём сначала все координаты в глобальные, там найдём угол поворота и будем к нему стремиться - вертеть пушку
*/
// получаем глобальные координаты мыши (это координаты относительно левого верхнего угла плеера, независимо от смещений родительских спрайтов)
var mousePos: Point = localToGlobal(new Point(_mousePosX, _mousePosY));
// глобальные координаты центра пушки
var towerPos: Point = _tower.localToGlobal(new Point());
// новый угол поворота пушки
// силами своей функции нахождения угла между двумя точками, вершина угла в (0;0)
var nRot: Number = MyMath.getAngle2Deg(1, 0, mousePos.x - towerPos.x, mousePos.y - towerPos.y);
// обрезаем угол согласно максимальному(180) и минимальному(-180) углам флеш-объектов
while (nRot > 180) nRot -= 360;
while (nRot < -180) nRot += 360;
// текущий угол поворота пушки
var tRot: Number = _tower.rotation;
// рассчитываем направление поворота пушки, выбирая минимальный угол поворота, тем самым кротчайшую дугу
var towerTurnDir: int = nRot - tRot <= 180 ? 1 : -1;
if (nRot - tRot < 0) towerTurnDir = -1;
if (nRot - tRot < -180) towerTurnDir = 1;
// дельта-поворот за данный тик времени
var dtTowerTurn: Number = towerTurnDir * TOWER_TURN_SPD * aDT;
// поворачиваем
if (_tower.rotation != nRot) {
// меры для того, чтобы пушка не дёргалась, когда достигнет нужного угла взгляда
var rotAbs: Number = Math.abs(nRot - _tower.rotation);
var dtAbs: Number = Math.abs(dtTowerTurn);
// смягчения при достижении целевого угла
if (rotAbs <= dtAbs / 2) {
dtTowerTurn = nRot - _tower.rotation;
}
else if (rotAbs <= dtAbs) {
dtTowerTurn *= 0.5;
}
_tower.rotation += dtTowerTurn;
}
}
private function turnBow():void
{
// получаем глобальные координаты мыши (это координаты относительно левого верхнего угла плеера, независимо от смещений родительских спрайтов)
var mousePos: Point = localToGlobal(new Point(_mousePosX, _mousePosY));
// глобальные координаты центра пушки
var bowPos: Point = _bow.localToGlobal(new Point());
// новый угол поворота пушки
// силами своей функции нахождения угла между двумя точками, вершина угла в (0;0)
var nRot: Number = MyMath.getAngle2Deg(1, 0, mousePos.x - bowPos.x, mousePos.y - bowPos.y);
// применяем ограничения
if (nRot < _bowMinAn) nRot = _bowMinAn;
if (nRot > _bowMaxAn) nRot = _bowMaxAn;
_bow.rotation = nRot;
}
private function onEnterFrame(e:Event):void
{
update(1 / stage.frameRate);
}
private function update(aDT:Number):void
{
// записываем координаты, куда башне надо смотреть - это координаты мышки на земле
// земля - это родитель танка, в нашем случае родитель - это текущий класс.
_mousePosX = mouseX;
_mousePosY = mouseY;
turnTankTower(aDT);
turnBow();
}
}
}
Всё уместилось в 1 файле и + 1 функция (MyMath.getAngle2Deg()) из моих личных запасов, которая находит угол между двумя точками с вершиной в координатах (0;0).
Я хорошо прокомментировал код, поэтому непоняток должно быть минимум.
Вся соль заключается в формулах и алгоритмах расчёта правильного угла поворота.
Материалы
Архив с исходниками: rotateToMouse.rar