Всем привет!
Во многих играх нам необходимо, чтобы какой-либо игровой объект поворачивался в сторону нашего курсора.
Давайте сделаем что-нибудь такое, чтобы это тоже было в нашей копилке наработок)
Что будем делать?
Я решил сделать 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