Что такое изометрия, простым, человеческим языком, — это псевдо трехмерная проекция, при которой все объекты повернуты к нам под углом в 45 градусов, так, что мы видим три стороны фигуры. При этом масштаб фигур не изменяется в зависимости от их удаленности. Доступных к общему пользованию изометрических движков на AS3 на самом деле не так уж и много, можно пересчитать на пальцах и двух рук хватит. Расскажу про один из них — as3Isolib.
Библиотека с открытым исходным кодом. Легко допиливается под свои нужды. Глюков особо замечено не было. Скоростью, правда, не хвастается, но и жутко не тормозит. В либе присутствует только базовый функционал по отображению и сортировке, все прочие навороты придется допиливать самому. Из недостатков могу подметить, что последнее обновление на Google Code было в 2010 году. Скачать саму либу можно здесь.
Итак как ее использовать. Весь «изометрический мир» представляет собой одну (для простых проектов) или несколько изометрических сцен IsoScene
, которые отображаются через вьюпорт IsoView
. Создадим простейшую изометрическую сцену.
1 2 3 4 5 6 7 8 9 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 |
package { import as3isolib.display.IsoView; import as3isolib.display.scene.IsoGrid; import as3isolib.display.scene.IsoScene; import flash.display.Sprite; import flash.events.Event; public class Main extends Sprite { 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 view:IsoView = new IsoView(); // Задаем ему размеры равные нашей флешке view.setSize(stage.stageWidth, stage.stageHeight); // Добавляем его на сцену addChild(view); // Создаем изометрическую сцену var scene:IsoScene = new IsoScene(); // Добавляем сцену во вьюпорт view.addScene(scene); // Для наглядности добавим на сцену сетку scene.addChild(new IsoGrid()); // Рендерим сцену scene.render(); } } } |
Собственно сетку IsoGrid
можно было и не добавлять, но тогда бы мы увидели просто пустой экран, что было бы не очень наглядно.
В библиотеке присутствуют стандартные примитивы например IsoBox
, которые больше подходят для тестирования и отладки, чем для практического использования, но для примера вполне подойдут. Кроме того имеется IsoSprite
, это уже более полезный объект, сам он не имеет внешнего представления и является контейнером для DisplayObject
-ов. Проще говоря помещаем DisplayObject
или набор нескольких в IsoSprite и они становятся частью «изометрического мира». Добавим на сцену примитив IsoBox
, IsoHexBox
и IsoSprite
.
1 2 3 4 5 6 7 8 9 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 |
package { import as3isolib.display.IsoSprite; import as3isolib.display.IsoView; import as3isolib.display.primitive.IsoBox; import as3isolib.display.primitive.IsoHexBox; import as3isolib.display.scene.IsoScene; import as3isolib.graphics.SolidColorFill; import flash.display.Sprite; import flash.events.Event; public class Main extends Sprite { 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 view:IsoView = new IsoView(); // Задаем ему размеры равные нашей флешке view.setSize(stage.stageWidth, stage.stageHeight); // Добавляем его на сцену addChild(view); // Создаем изометрическую сцену var scene:IsoScene = new IsoScene(); // Добавляем сцену во вьюпорт view.addScene(scene); // Создаем примитив IsoBox var box:IsoBox = new IsoBox(); // Устанавливаем ему размер (ширина, длина, высота) box.setSize(50, 50, 50); // Добавляем его на сцену scene.addChild(box); // Создаем примитив IsoHexBox var hexBox:IsoHexBox = new IsoHexBox(); // Задаем ему размер hexBox.setSize(50, 50, 40); // Перемещаем его (x, y, z) hexBox.moveTo( -50, 0, 0); // Зальем его красным цветом, чтоб не сливался с коробкой hexBox.fill = new SolidColorFill(0xFF0000, 1); // Добавляем на сцену scene.addChild(hexBox); // Создаем контейнер IsoSprite var sprite:IsoSprite = new IsoSprite(); // Добавлем в него спрайты (массив DisplayObject'ов) sprite.sprites = [new Tree()]; // Задаем размеры контейнера sprite.setSize(50, 50, 50); // Перемещаем его sprite.moveTo(60, 25, 0); // И добавляем на сцену scene.addChild(sprite); // Рендерим сцену scene.render(); } } } |
О чем здесь следует сказать. Устанавливать размеры объекта (setSize(width, length, height
) нужно обязательно. Иначе он не будет принимать участия в сортировке. Это не размер DisplayObject
‘a, а размер объекта в изометрическом мире.
Перемещать объекты по изометрической сцене не сложнее, чем по обычной, у всех изометрических объектов имеются методы moveTo(x, y, z)
и moveBy(x, y, z)
. Первый перемещает объект в указанные координаты. Второй смещает объект на указанное количество пикселей относительно текущей позиции. Координаты конечно нужно указывать в изометрической проекции, а не в экранной. Самим ничего естественно пересчитывать не придется, для этого в библиотеке есть класс IsoMath
с соответствующими статическими методами isoToScreen()
и screenToIso()
. Кроме того у объекта IsoView
есть удобные методы для перевода координат isoToLocal()
, localToIso()
.
Напишем код, который будет перемещать объект IsoBox
в координаты клика.
1 2 3 4 5 6 7 8 9 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 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
package { import as3isolib.display.IsoSprite; import as3isolib.display.IsoView; import as3isolib.display.primitive.IsoBox; import as3isolib.display.primitive.IsoHexBox; import as3isolib.display.scene.IsoGrid; import as3isolib.display.scene.IsoScene; import as3isolib.geom.Pt; import as3isolib.graphics.SolidColorFill; import eDpLib.events.ProxyEvent; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; public class Main extends Sprite { private const GRIDSIDE:int = 25; private const GRIDSIZE:int = 10; private var _view:IsoView; private var _scene:IsoScene; private var _box:IsoBox; private var _hexBox:IsoHexBox; public function Main():void { // Создаем вьюпорт _view = new IsoView(); // Создаем изометрическую сцену _scene = new IsoScene(); // Создаем примитив IsoBox _box = new IsoBox(); // Создаем примитив IsoHexBox _hexBox = new IsoHexBox(); if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); // Задаем вью размеры равные нашей флешке _view.setSize(stage.stageWidth, stage.stageHeight); // Добавляем его на сцену addChild(_view); // Добавляем сцену во вьюпорт _view.addScene(_scene); // Устанавливаем примитиву IsoBox размер (ширина, длина, высота) _box.setSize(GRIDSIDE, GRIDSIDE, GRIDSIDE); // Добавляем его на сцену _scene.addChild(_box); // Задаем примитиву IsoHexBox размер _hexBox.setSize(GRIDSIDE, GRIDSIDE, GRIDSIDE); // Перемещаем его (x, y, z) _hexBox.moveTo(GRIDSIDE*2, 0, 0); // Зальем его красным цветом, чтоб не сливался с коробкой _hexBox.fill = new SolidColorFill(0xFF0000, 1); // Добавляем на сцену _scene.addChild(_hexBox); // Создаем сетку var grid:IsoGrid = new IsoGrid(); // устанавливаем ей размер grid.setGridSize(GRIDSIZE, GRIDSIZE, 0); // сдвигаем на 100 пикселей для удобства grid.moveTo( -100, -100, 0 ); // добавляем ее на сцену _scene.addChild(grid); // Рендерим сцену _scene.render(); // вешаем слушатель клика на сцену _scene.addEventListener(MouseEvent.CLICK, handlerClick); } private function handlerClick(e:ProxyEvent):void //ловим клики { // Преобразуем координаты клика из экранных в изометрические var pt:Pt = _view.localToIso(new Pt(stage.mouseX, stage.mouseY)); // Округляем точку x по клетке pt.x = Math.round(pt.x / GRIDSIDE) * GRIDSIDE; // Округляем точку y pt.y = Math.round(pt.y / GRIDSIDE) * GRIDSIDE; // Перемещаем примитив Box в новые координаты _box.moveTo(pt.x, pt.y, pt.z); // рендерим сцену _scene.render(); } } } |
Как можно увидеть я беру не точные координаты клика, а округленные до ближайшего кратного GRIDSIDE
(25), т.е. до ширины / длины ячейки. Хитрости никакой здесь нет, это делается чтобы объекты не пересекались, просто некрасиво.
Ну вот впрочем и все, остальное дело техники, переписывать сюда документацию особого смысла не вижу. Вводный экскурс по использованию библиотеки я вам дал, те кто заинтересуется может продолжить изучение либы в доках и ее исходниках.
И примерчик самого простенького редактора изометрической карты: