Еще один способ создания изометрического мира(Часть2 - Совершенствуемся)
Здравствуйте, уважаемые коллеги. В предыдущей статье я рассказал о том как создавать клеточный фон для игр, о том как преобразовывать экранные координаты в изометрические и сортировать их по глубинах в списке отображения.
Учитывая замечания и советы - эти классы были немного переделаны и в изометрическую систему была добавлена третья координата. Получился своего рода фреймворк домашнего производства.
И так, пример: http://megaswf.com/serve/2010617
Представляю вам пакет "isometrics", в который входит несколько классов, цель которых:
- Упростить создание фона на основе клеток.
- Упростить создание изометрического контейнера, в котором находятся разные изометрические обьекты.
- Упростить сортировку этих обьектов в контейнере по глубинам в дисплей листе.
Недостатком является то, что алгоритм сортировки не учитывает геометрические размеры сортируемых обьектов. Поэтому нельзя создавать обьекты прямоугольной, или другой формы. Сортировка происходит по точкам. Чем ближе точка к зрителю - тем выше глубина обьекта, и наоборот. Чтобы создать обьект произвольной формы - необходимо его разделить на несколько частей, и поместить каждую часть на нужные координаты так чтобы они выглядели как одно целое.
То что алгоритм сортировки не учитывает геометрические размеры сортируемых обьектов, не значит что их надо создавать как угодно. Тут есть своя тонкость. Необходимо иметь пространственное воображение, и при создании каждого обьекта (будь то коробка, дерево, человек, дом) нужно представлять его как куб. Вернее не совсем куб, так как основа должна быть квадратной формы, а высота произвольная (см. рисунок 3). Дальше буду это называть "ограничительный куб". Его размеры нам будут нужны для определения столкновений с другими предметами.
Система координат, которой оперирует фреймворк показана на рисунке 1. Угол между осями isoX, isoZ и экранной осью X составляет приблизительно 26.6° (см. рисунок 2 - как создавать клетки).
Класс IsoTile
public class IsoTile extends Sprite - представляет клетку. Клетка, в отличии от предмета, не имеет высоты и она не должна обязательно добавляться в изометрический контейнер. Родителем клеток может быть любой экранный обьект. Также, у нее нет координат по оси isoY. То есть - она всегда находится на высоте 0. Все стороны клетки должны быть равны. Если клетку спроектировать на экран - то ее стороны должны быть наклонены под углом 26.6°. Этот угол можно изменить, задав в конструкторах клеток или обьектов нужный нам угол(в радианах). Тогда все расчеты изометрии будут происходить под этим углом. Соответственно, клетки и предметы нужно создавать под этим углом. Мне нравится угол 26.6°, так как с ним очень удобно создавать клетки.
Конструктор:
public function IsoTile(graphicObject:DisplayObject, zenithAngle:Number = 0.4636476090008062) - создает новую клетку.
Параметры:
- graphicObject - графический обьект (рисунок клетки).
- zenithAngle - угол между осью isoX(isoZ) и экранной осью X в радианах. По умолчанию приблизительно 26.6° (см. рисунок1, 2).
- graphicObject:DisplayObject - графический обьект(рисунок клетки). Может быть MovieClip, Sprite, Bitmap - чем угодно.
- isoWidth:int [статическое][только для чтения] - ширина клетки вдоль изометрических осей. Определяется автоматически при создании первой клетки в приложении.
- isoX:Number, isoZ:Number - координаты клетки по изометрических осях. При задании этих координат - автоматически изменяются экранные координаты. Если необходимо изменить обе координаты одновременно - нужно использовать метод setIsoPosition();
- public function setIsoPosition(isoX:Number = 0, isoZ:Number = 0):Vector.<Number> - изменяет позицию клетки в изометрических координатах и автоматически изменяет экранные координаты. Вызывать в случаях, когда нужно изменить две изометрические координаты одновременно. Экономит количество преобразований координат в 2 раза (по сравнению с изменением координат по отдельности). Возвращает вектор экранных координат клетки [screenX, screenY].
Параметры: isoX - новая координата по оси isoX; isoZ - новая координата по оси isoZ.
Допустим, в библиотеке у нас есть мувиклип клетки SWCTile. В мувиклипе есть два кадра: травы и асфальта. Нам нужно создать поле из этих клеток 10х15 (isoX x isoZ).
//создаем спрайт - контейнер для плиток var tilesContainer:Sprite = new Sprite(); addChild(tilesContainer); var tile:IsoTile; for (var x:uint = 0; x < 10; x++){ for(var z:uint = 0; z < 15; z++){ tile = new IsoTile(new SWCTile()); tile.setIsoPosition(x*IsoTile.isoWidth, z*IsoTile.isoWidth); tilesContainer.addChild(tile); //Здесь условие, которое определяет что должно быть изображено: //трава, или асфальт if(condition){ (tile.graphicObject as MovieClip).gotoAndStop("asphalt"); } else { (tile.graphicObject as MovieClip).gotoAndStop("grass"); } } } //после этого tilesContainer можно отрисовать в битмап(по желанию), если //клетки больше не понадобятся
public class IsoContainer extends Sprite - представляет контейнер изометрических обьектов (IsoItem). Для добавления предметов в контейнер необходимо воспользоваться методом addChild(). Контейнер добавит обьект в дисплей лист и автоматически поместит его на нужную глубину. Чтобы удалить обьект - надо использовать метод removeChild(). В контейнер можно добавлять лишь экземпляры класса IsoItem, в противном случае - RTE.
Методы:
- public function destroy():void - удаляет внутренние слушатели событий контейнера, обьявленные при его создании. Необходимо вызвать, когда контейнер больше не понадобится в приложении, по такой схеме: removeChild(container); container.destroy(); container = null;
public class IsoItem extends Sprite - представляет изометрический предмет. Это обьект который находится в списке отображения изометрического контейнера IsoContainer. Для создания такого обьекта можно создать другой класс и унаследовать его от IsoItem. Или же создать новый обьект IsoItem и определить его свойство graphicObject, которое будет представлять изображение обьекта.
Конструктор:
public function IsoItem(graphicObject:DisplayObject = null, zenithAngle:Number = 0.4636476090008062, sortImmediately:Boolean = false, mouseChildren:Boolean = false) - создает новый предмет, который будет добавлен в изометрический контейнер.
Параметры:
- graphicObject - графический обьект наполняющий экземпляр IsoItem;
- zenithAngle - угол между осью isoX(isoZ) и экранной осью X в радианах. По умолчанию приблизительно 26.6° (см. рисунок1, 2).
- sortImmediately - при добавлении обьекта в дисплей лист отсортировать его немедленно. По умолчанию - false (то есть обект будет отсортирован при следующей перерисовке). Если за один кадр добавляется, например 100 обьектов со значением sortImmediately = true - то за один кадр произойдет 101 сортировка, что не есть хорошо для процессора. Если false - сортировка в кадре произойдет лишь один раз, но обьекты будут на самой высшей глубине, пока не произойдет следующее событие enterFrame, что вызывает не очень красивый эффект. Поэтому - значение этому параметру нужно присваивать в зависимости от обстоятльств;
- mouseChildren - мышиная активность детей(графических обьектов).
- isoWidth:Number - ширина предмета по осях isoX, isoZ;
- isoHeight:Number - высота обьекта по оси isoY;
- isoWidth и isoHeight - это размеры "ограничительного куба" предмета;
- graphicObject:DisplayObject - графический обьект "наполняющий" экземпляр IsoItem грфическим содержимим. Может быть MovieClip, Sprite, Bitmap - чем угодно;
- isoX:Number, isoZ:Number, isoY:Number - координаты по изометрическим осям. При изменении любой из этих координат - автоматически изменяются экранные координаты. Если необходимо изменить две и больше координат одновременно, нужно использовать метод setIsoPosition(), так количество преобразований уменьшится в 2 и три раза соответственно.
- public function setIsoPosition(isoX:Number = 0, isoZ:Number = 0, isoY:Number = 0):Vector.<Number> - изменяет позицию обьекта в изометрических координатах и автоматически изменяет экранные координаты. Вызывать в случаях, когда нужно изменить две или больше изометрических координат одновременно. Экономит количество преобразований координат в 3 раза (по сравнению с изменением координат по отдельности). Возвращает вектор экранных координат обьекта в контейнере [screenX, screenY];
- public function setScreenPosition(x:Number = 0, y:Number = 0):Vector.<Number> - изменяет позицию обьекта в экранных координатах и автоматически изменяет изометрические координаты. Вызывать в случаях, когда нужно изменить две экранные координаты одновременно. Экономит количество преобразований координат в 2 раза (по сравнению с изменением x или y по отдельности). Возвращает вектор изометрических координат обьекта в контейнере [isoX, isoZ, isoY];
- public function hitTesItem(item:IsoItem):Boolean - определяет, пересекается ли "ограничительный куб" обьекта с "кубом" другого (переданного) обьекта. Параметр item - исследуемый на пересечение другой обьект.
Допустим в библиотеке есть мувиклип самолета SWCPlane. В этом мувиклипе еще один мувиклип с instance name: "engine". Этот мувик engine отвечает за изображение огня из сопла самолета. Когда нажата клавиша вперед - огонь увеличивается.
Также в библиотеке есть мувиклип коробки SWCBox. В этом мувике два кадра: зеленая коробка и красная.
//создаем контейнер для предметов var container:IsoContainer = new IsoContainer(); addChild(container); //создаем самолет var plane:Plane = new Plane(); plane.setIsoPosition(150, 100, 50); //создаем коробку var box:IsoItem = new IsoItem(new SWCBox()); box.setIsoPosition(300, 200, 0); box.isoWidth = 40; box.isoHeight = 40; //делаем коробку зеленой (box.graphicObject as MovieClip).gotoAndStop("green"); //добавляем наши предметы в контейнер и они //автоматически поместятся на нужную глубину container.addChild(plane); container.addChild(box); //так надо управлять графическими обьектами предметов: plane.planeMovie.engine.gotoAndStop(2); (box.graphicObject as MovieClip).gotoAndStop("red"); //чтобы самолет переместиь, например, на isoX = 100, нужно: plane.isoX = 100; //чтобы самолет переместиь, например, на isoX = 100, //isoZ = 200, isoY = 0, нужно plane.setIsoPosition(100, 200); //Класс самолета internal class Plane extends IsoItem { //мувиклип самолета internal var planeMovie:SWCPlane; public function Plane(){ super(); //задаем размеры "ограничительного куба" самолета this.isoHeight = 15; this.isoWidth = 40; planeMovie = new SWCPlane(); planeMovie.engine.gotoAndStop(1); } }
Внимание! Касается начинающих разработчиков: исходник с "самолетом" - это лишь пример того как пользоваться пакетом isometrics, а не то как создавать игры.
Спасибо за внимание.
Всего комментариев 27
Комментарии
25.01.2012 21:42 | |
Цитата:
Почему предметы только квадратные, в чем сложность сделать прямоугольные
Цитата:
Почему изометрия только 45град. Можно сделать это настраиваемым параметром. Например во всех наших проектах изометрия 0.66%
PS: Статью отредактировал: обьяснить как изменить угол. Спасибо за подсказку. |
|
Обновил(-а) HardCoder 25.01.2012 в 21:52
|
26.01.2012 10:31 | |
Для примера мы находимся недалеко от дома длиной в 1 км. За 800м от нас, возле этого дома стоит ларек. Вопрос: почему ларек перекрывает нам некую часть дома, ведь ларек дальше от нас чем дом? Ответ очевиден: ларек ближе чем самая дальняя часть дома, которая находится в км отсюда. Теперь нам надо написать программу которая, учитывая геометрические размеры и координаты обеих зданий - разместит дом ПОД ларек, даже если этот дом находится ближе к зрителю чем ларек.
Я несколько дней выводил разные формулы и мало что получилось. Пробовал сортировать перебором, не перебором... Но так и не смог найти отличительную черту двух зданий, по которой их бы отсортировать. Что в доме и ларьке есть особенного? Как учитывая координаты и геометрические размеры здания найти некоторое уникальное для него свойство, которое и будет отвечать за его глубину? Видать - эта задача не для меня. Так что именно в данном случае остановлюсь на "квадратах". [IMG]http://************/2390660m.jpg[/IMG] Цитата:
Во флеше принято высотой называть размеры по игреку, шириной по иксу, для той высоты о которой ты - лучше что-то другое придумать. В английском еще много неиспользованных слов.
|
|
Обновил(-а) HardCoder 26.01.2012 в 10:51
|
26.01.2012 12:42 | |
Jewelz, если я правильно расшифровал что такое L1, L2, то вот что получится:[IMG]http://************/2383514m.jpg[/IMG]
|
26.01.2012 13:35 | |
Посмотрите реализации z-сортировки для 3D движков.
|
26.01.2012 15:43 | |
HardCoder, у вас x и y наоборот, тогда в формуле поменяйте тоже
|
26.01.2012 16:51 | |
http://***********/content/xEoq7tkOhk
Я примерно это имел в виду. |
26.01.2012 18:40 | |
Кстати, константу ALPHA, все-таки, лучше сделать параметром в конструкторе, как советовал Dukobpa3. В игре могут одновременно присутствовать локации с разными проекциями.
|
26.01.2012 20:32 | |
А мне все больше нравится. А будет пример управления мышкой?
|
01.02.2012 22:57 | |
как вариант сортировки
"полу BSP Tree" http://www.flasher.ru/forum/showpost...8&postcount=16 разбить все спрайты (если у Вас спрайты) друг другом чтобы получить минимальные неделимые куски отсортировать эти куски по степени удаленности Один из плюсов такого подхода - Вы сможете правильно отсортировать пересекающиеся объекты Тема тут, может на мысль подтолкнет http://www.flasher.ru/forum/showthre...=173657&page=2 |
|
Обновил(-а) djyamato 01.02.2012 в 23:03
|
Последние записи от HardCoder