| |||
Свежие новости выписываемой вами ленты "Почтовая".
"Demiart.ru" [ наверх ] Клуб аццких флудеров №22 Лого для web Цитирую (LadyEvil) muppy, вот они как раз часть моего раздумья, есть вариант где они посередине и чуть поменьше) [attachmentid=1862853] Born to be free Цитирую (annaphoenix) Помню, что Рашап говорил об этом на своих мк. Да, Анют, я его слова тоже помню и абсолютно согласна с ними. Но обеспокоенность замедленностью роста это, к сожалению, не унимает. С некоторых пор мне кажется, что я мало работаю, а когда пытаюсь себя подстегивать, то наталкиваюсь на внутреннее сопротивление, есть ощущение, что энергии не хватает, постоянно хочется спать-спать-спать. А спать никак нельзя, и постоянно идет эта борьба между "ляг, полежи и давай, работай!" Надеюсь, что это просто зимний аветоменоз. )) Макраме украшения Фотостудия 8 [attachmentid=1862852] Как сделать в 3dMax Цитирую (Amps) DJ.Rin, во вкладке keyboard entry вводите первое значение вершины сплайна, жмете add point, затем второе. Все. Спасибо! И как это я не заметил эту вкладку. Вроде не первый день в максе работаю. Всё, до допоздна больше не засиживаюсь за компом. Tertium non datur [attachmentid=1862845] Панорама системы сводов потолка, собранная из 6 снимков. [attachmentid=1862847] [attachmentid=1862846] [ скрытый текст ] [attachmentid=1862849] Должна признаться, что на конкурс я решила отправить именно ее. Это решение далось очень не просто, но я уже по многолетнему опыту знаю, что вариант с лучами и падающим снегом был бы расценен жюри кичем, здесь всем этим романтическим компонентам предпочитают естественность и если красоту, то, чтобы в ней было побольше унылости и печали. )) Мои преподаватели часто говорили, что фотограф должен при отборе своих работ действовать по принципу 'Kill your darlings.' Быть беспощадным к собственным субъективным ощущениям. И еще уметь смотреть на свои фото не только собственными глазами, но и глазами людей разных культур, национальностей и профессий. Это, правда, нифига не просто, но я стараюсь. ) Ну и в качестве маленького бонуса, нашла на территории руин план, каким это строение было в свои лучшие времена. Сцена со скорбящей фигуркой разыгрывалась как раз под самой высокой башней, ныне уже не существующей. [attachmentid=1862848] ФОТО-САЛУН(4) Стрип-кафе 2 Цитирую (АленаСнежная) свой новый планшетик Ваком Фэндомная воля. Еженедельный мини конкурс. Игра в изометрии, часть 1 [ скрытый текст ] Цитирую (ActionScript) package gameObjects { import base.SpriteBase; import flash.display.DisplayObject; import flash.events.MouseEvent; import flash.utils.getTimer; /** * ... * @author qwe */ public class World extends SpriteBase { public var planesContainer:PlanesContainer; public var needUpdate:Boolean; private var inited:Boolean = false; public function World() { planesContainer = new PlanesContainer();//Создаём контейнер поверхностей super.addChild(planesContainer); addEventListener(MouseEvent.CLICK, onClick);//Слушаем событие клика мыши } public function init(size:int):void { needUpdate = false; planesContainer.init(size); if (!inited) {//Если инициализируем в 1 раз, то inited = true; //В массив функций обновления добавляем GV.onUpdate.push(update);//Обновление мира GV.onUpdate.push(planesContainer.drawOutScreen);//Рисование всего, даже того, что находится вне сцены } } public override function addChild(child:DisplayObject):DisplayObject { throw "Use addPlane";//Что б не забыться и не вызвать этот метод return child; } public function addPlane(plane:Plane):void { needUpdate = true;//Мир был изменён, его надо будет обновить planesContainer.addPlane(plane);//Добавление поверхности в контейнер поверхностей } public function update(timeToUpdate:int):void { if (needUpdate) {//Если надо обновить мир needUpdate = !planesContainer.update();//Обновляем и смотрим, до конца ли обновили, или потом надо будет ещё раз } } private function onClick(e:MouseEvent):void { planesContainer.onClick();//Т. к. пока у нас нет ничего, кроме поверхностей, клик по миру - 100% клик по поверхностям } public override function dispose():void { planesContainer.dispose(); super.dispose(); } } } DemiColor 1.0.1 Класс GV изменим до такого состояния: Код ActionScript: [ скрытый текст ] Цитирую (ActionScript) package { import flash.display.Stage; import flash.geom.Point; import gameObjects.World; import utils.IsoUtils; /** * ... * @author qwe */ public class GV { public static const SCALE:int = 32;//Множитель координат, равен высоте картинки поверхности public static var stage:Stage;//Сцена public static var world:World;//Мир public static const onUpdate:Array = [];//Массив функций обновления //Перемещение "камеры" (пользователя) в указанную изометрическую точку public static function camGoto(isoPos:Point):void { //Перевод в координаты экрана const camFlashPos:Point = IsoUtils.isoToFlash(isoPos.x * SCALE, 0, isoPos.y * SCALE); //Перемещение камеры = перемещение мира с обратным знаком world.x = stage.stageWidth / 2 - camFlashPos.x; world.y = stage.stageHeight / 2 - camFlashPos.y; world.needUpdate = true;//Обновить } } } DemiColor 1.0.1 Resources - до такого (сделаем подобие шахматного поля, что б не так однообразно всё было): Код ActionScript: [ скрытый текст ] Цитирую (ActionScript) package { import flash.display.BitmapData; import flash.display.Sprite; /** * ... * @author qwe */ public class Resources { public static var planes:Array; private static function drawRomb(spr:Sprite, color:int):void { spr.graphics.clear(); spr.graphics.beginFill(color); spr.graphics.lineStyle(1, 0x000000); spr.graphics.moveTo(0, 16); spr.graphics.lineTo(32, 0); spr.graphics.lineTo(64, 16); spr.graphics.lineTo(32, 32); spr.graphics.lineTo(0, 16); } public static function init():void { var spr:Sprite = new Sprite();//Создаём спрайт drawRomb(spr, 0xFFFF00);//В нём рисуем жёлтый (0xFFFF00) ромб //Перерисовываем это в битмапдату var yellow:BitmapData = new BitmapData(64, 32, true, 0); yellow.draw(spr); //Аналогично с тёмно-красным (0xA00000) ромбом drawRomb(spr, 0xA00000); var red:BitmapData = new BitmapData(64, 32, true, 0); red.draw(spr); //Делаем массив из этих 2-х битмапдат planes = [yellow, red]; } } } DemiColor 1.0.1 Как вы заметили, классы PlanesContainer и World (а в будущем - много чего ещё) наследуются от SpriteBase (папка base), вот и он: Код ActionScript: [ скрытый текст ] Цитирую (ActionScript) package base { import flash.display.BitmapData; import flash.display.Sprite; /** * ... * @author qwe */ public class SpriteBase extends Sprite { public function SpriteBase() { } public function setBitmapData(bmpd:BitmapData):void { graphics.clear();//Чистим то, что уже было (вдруг) graphics.beginBitmapFill(bmpd);//Рисуем то, что надо сейчас graphics.drawRect(0, 0, bmpd.width, bmpd.height); } public function removeFromParent():void { if (parent) { parent.removeChild(this); } } public function dispose():void { removeFromParent(); } //Переопределение геттеров и сеттеров x- и y-координат, запрет на установку нецелочисленных значений //Нецелочисленные значения не всегда корректно отображаются //Однако, с целочисленными значениями могут возникнуть проблемы //при использовании очень медленных движений твинерами (TweenNano, например) //У нас, однако, таких не будет public override function get x():Number { return int(super.x); } public override function set x(v:Number):void { super.x = int(v); } public override function get y():Number { return int(super.y); } public override function set y(v:Number):void { super.y = int(v); } } } DemiColor 1.0.1 Ну а создавать карту мы будем в классе MapCreator (в папке map папки utils) Код ActionScript: [ скрытый текст ] Цитирую (ActionScript) package utils.map { import flash.geom.Point; import flash.utils.getTimer; import gameObjects.Plane; import gameObjects.World; import utils.IsoUtils; /** * ... * @author qwe */ public class MapCreator { private static var x:int; private static var z:int; private static var xSize:int; private static var zSize:int; private static var minIso:Point; private static var maxIso:Point; public static function create(xSize:int, zSize:int):void { MapCreator.xSize = xSize;//Запоминаем размеры MapCreator.zSize = zSize; const world:World = GV.world = new World();//Создаём мир world.init(Math.max(xSize, zSize));//Инициализируем его максимальной стороной карты (лишнее потом всё равно удалится) GV.stage.addChild(world); //Следующие 2 команды важно не поменять местами, иначе теряется смысл в немедленной отрисовке того, что сейчас на экране GV.camGoto(new Point(xSize - 1, zSize - 1));//Перемещаем камеру в нужное нам место (в данном случае - на последнюю клетку) createPlanesOnScreen();//Немедленное создание плиток, находящихся в зоне видимости x = 0;//Начнём с начала GV.onUpdate.push(planesCreating);//Добавление всех оставшихся плиток оставляем на потом } private static function createPlanesOnScreen():void { const stageWidth:int = GV.stage.stageWidth; const stageHeight:int = GV.stage.stageHeight; //Координаты пикселей мира (World) относительно экрана const p1:Point = GV.world.globalToLocal(new Point(0, 0)); const p2:Point = GV.world.globalToLocal(new Point(0, stageHeight)); const p3:Point = GV.world.globalToLocal(new Point(stageWidth, 0)); const p4:Point = GV.world.globalToLocal(new Point(stageWidth, stageHeight)); //Переводим их в изометрию const p1Iso:Point = IsoUtils.flashToIso(p1.x, p1.y); const p2Iso:Point = IsoUtils.flashToIso(p2.x, p2.y); const p3Iso:Point = IsoUtils.flashToIso(p3.x, p3.y); const p4Iso:Point = IsoUtils.flashToIso(p4.x, p4.y); //Создаём из них 2 точки, расположенных в углах ромба, //который полностью заполняет экран (и частично выходит за его пределы) minIso = new Point(p1Iso.x, p2Iso.y); maxIso = new Point(p4Iso.x, p3Iso.y); //Следим за тем, что бы не допускать невозможных координат if (minIso.x < 0) minIso.x = 0; if (minIso.x > xSize) minIso.x = xSize; if (minIso.y < 0) minIso.y = 0; if (minIso.y > zSize) minIso.y = zSize; if (maxIso.x < 0) maxIso.x = 0; if (maxIso.x > xSize) maxIso.x = xSize; if (maxIso.y < 0) maxIso.y = 0; if (maxIso.y > zSize) maxIso.y = zSize; //Сразу же создаём те поверхности, которые находятся в пределах экрана for (var x:int = minIso.x; x < maxIso.x; ++x) { for (var z:int = minIso.y; z < maxIso.y; ++z) { const plane:Plane = new Plane(x * GV.SCALE, z * GV.SCALE, (x + z) % 2); GV.world.addPlane(plane); } } } private static function planesCreating(timeToUpdate:int):void { const startTime:int = getTimer(); //В начале нет x = 0, чтобы продолжить с того места, где закончили в прошлый раз for (; x < xSize; ++x) { if (getTimer() - startTime >= timeToUpdate) return;//Выйти из функции, если не успеваем for (z = 0; z < zSize; ++z) { if (minIso.x <= x && x <= maxIso.x && minIso.y <= z && z <= maxIso.y) continue;//Эти уже были созданы (сразу же) const plane:Plane = new Plane(x * GV.SCALE, z * GV.SCALE, (x + z) % 2); GV.world.addPlane(plane); } } //Если все поверхности созданы: const index:int = GV.onUpdate.indexOf(planesCreating); GV.onUpdate.splice(index, 1);//Эту функцию больше вызывать не требуется GV.world.planesContainer.canClear = true;//Разрешаем удалить пустые части } } } DemiColor 1.0.1 И наконец, класс Main: Код ActionScript: [ скрытый текст ] Цитирую (ActionScript) package { import flash.display.Sprite; import flash.events.Event; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; import flash.utils.getTimer; import utils.map.MapCreator; /** * ... * @author qwe */ [SWF (width = 640, height = 480, frameRate = 60)] public class Main extends Sprite { private var dTimes:Array = []; private var fpsTf:TextField; 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); RunUp.init(stage);//Инициализируем сцену и используемые ресурсы MapCreator.create(320, 320);//Создаём карту размеров 320х320 //Очевидно, текстовое поле для отображения FPS fpsTf = new TextField(); fpsTf.defaultTextFormat = new TextFormat(null, 30, 0xFF0000, true); fpsTf.background = true; fpsTf.autoSize = TextFieldAutoSize.LEFT; stage.addChild(fpsTf); addEventListener(Event.ENTER_FRAME, update);//Слушаем событие обновления } private function update(e:Event):void { const startTime:int = getTimer();//Время начала обработки кадра const countUpdateFunctions:int = GV.onUpdate.length;//Количество функций, которое надо вызвать //Время, которое можно затратить каждой функции var timeToEachUpdate:Number = 1000 / GV.stage.frameRate / countUpdateFunctions; for (var i:int = 0; i < countUpdateFunctions; ++i) { const funcStartTime:int = getTimer(); GV.onUpdate[i](timeToEachUpdate);//Вызов функции c доступным ей временем в качестве параметра const funcEndTime:int = getTimer(); //Корректировка времени, которое смогут затратить следующие функции timeToEachUpdate += (timeToEachUpdate - (funcEndTime - funcStartTime)) / (countUpdateFunctions - i - 1); } //Обновление значения FPS - операция очень дешёвая, //поэтому её не следует засовывать в общий массив функций обновления //К тому же, в качестве аргумента она принимает время начала обработки кадра updateFPS(startTime); } private function updateFPS(startTime:int):void { const dTime:int = getTimer() - startTime;//Разница во времени между началом и концом обработки кадра //Храним определённое количество (равное FPS, к которому стремимся) последних dTime dTimes.push(dTime); if (dTimes.length > stage.frameRate) { dTimes.shift(); } //Считаем среднее арифметическое у массива dTimes var middleDTime:int = 0; for (var i:int = 0; i < dTimes.length; ++i) { middleDTime += dTimes[i]; } middleDTime /= dTimes.length; //По этому среднему значению определяем среднее FPS var fps:int = 60; if (middleDTime) {//Смотрим на значение, а то поделить на 0 - не самая лучшая идея fps = 1000 / middleDTime; } if (fps > 60) {//Во Flash FPS не может быть больше 60 fps = 60; } //Вывод fpsTf.text = 'FPS: ' + fps; } } } DemiColor 1.0.1 Результат всего этого был показан в начале урока Исходник: [attachmentid=1862854] Следующие уроки планирую выпускать в таком порядке: 2. Загрузка графики 3. Создание объектов (деревья, дома...) 4. Создание персонажа (объект с анимацией) 5. Создание ботов (персонаж под управлением компьютера) Надеюсь, руки дойдут... Игра в изометрии, часть 1 [ скрытый текст ] Цитирую (ActionScript) package gameObjects { import base.SpriteBase; import flash.display.BitmapData; import flash.display.DisplayObject; import flash.geom.Matrix; import flash.geom.Point; import flash.utils.getTimer; import utils.IsoUtils; /** * ... * @author qwe */ public class PlanesContainer extends SpriteBase { private const minPartSize:int = 1 << 6;//Минимальный размер части контейнера - 2^6 = 64 (должна быть >= GV.SCALE) private const maxPartSize:int = 1 << 9;//Максимальный - 2^9 = 512 public var canClear:Boolean = false; private var partSize:int; private var generalSize:int; private var halfCount:int; private var count:int; private var toRedraw:Array; private var planes:Array; private var bitmapDatas:Array; private var parts:Array; private var needUpdatePart:Array; private var partIsEmpty:Array; private var worldSize:int; private const m:Matrix = new Matrix(); public function PlanesContainer() { } public function onClick():void { const isoPos:Point = IsoUtils.flashToIso(mouseX, mouseY);//Точка, по которой кликнули в изометрических координатах if (isoPos.x < 0 || isoPos.x >= worldSize || isoPos.y < 0 || isoPos.y >= worldSize) {//Если она явно вне карты return;//Делаем вид, что клика не было } GV.camGoto(isoPos);//Перемещаем камеру в эту точку } public function init(size:int):void { worldSize = size; var t:int = minPartSize;//Минимальная степень 2, >= generalSize const minCurrentPartSize:int = size * (2 * GV.SCALE) / 5;//Минимальный размер части для текущего размера карты //size - размер карты в клетках //2 * GV.SCALE - ширина каждой клетки //т. е. minCurrentCount - 1/5 от ширины карты в пикселях, чтобы по-горзонтали у нас помещалось 5 частей //Почему 5? На мой взгляд, это оптимальное число. Если по-горизонтали будет 5 частей, то по-вертикали скорее всего 4 //4х5 = 20, а 20 частей не влияют (почти) на производительность //Ищем степень 2, которая >= t //Почему степень 2? Чтобы не сильно переделывать код при "портировании" на движок типа Starling или Genome2D //(у GPU-движков свои заморочки (в том числе и максимальный размер текстур 2^11 = 2048), но сейчас мы не о них говорим) while (t < minCurrentPartSize) { t *= 2; } partSize = t; if (partSize > maxPartSize) {//Смотрим, не превысили ли мы максимальное значение partSize = maxPartSize; } generalSize = size * (2 * GV.SCALE);//Ширина карты в пикселях count = Math.ceil(generalSize / partSize / 2) * 2;//Количество частей, из которых будет состоять наш контейнер //У нас по-горизонтали и по-вертикали будет одно и тоже число квадратных частей //По-хорошему бы, конечно, надо делать их прямоугольными, но и этого вполне достаточно halfCount = count / 2;//Вычисляем половину от count заранее, что бы не делать этого потом тысячи раз initParts();//Части (спрайты) initBitmapDatas();//Битмапдаты initChildrens();//Массивы для поверхностей } private function initChildrens():void { toRedraw = [];//Неотрисованные поверхности planes = [];//Все поверхности } private function initParts():void { var i:int; var part:SpriteBase; if (parts) {//Если части уже были созданы for (i = 0; i < parts.length; ++i) { part = parts[i]; if (part) { part.dispose();//Удалить все, что есть } } } parts = [];//Массив частей partIsEmpty = [];//Массив из значений, по которым можно узнать, есть ли в определённой части какие-то поверхности needUpdatePart = [];//Массив из значений, по которым можно узнать, надо ли обновлять определённую часть parts.length = partIsEmpty.length = needUpdatePart.length = count * count; for (i = 0; i < count; ++i) { for (var j:int = 0; j < count; ++j) { part = new SpriteBase(); //Отключаем мышь у частей, её обрабатывать будем не здесь, здесь будет неудобно да и лагать будет part.mouseChildren = false; part.x = j * partSize; part.y = (i - halfCount) * partSize;//-halfCount - из-за того, что пол-карты в отрицательных координатах part.visible = false;//Сначала делаем невидимой, что б в случае чего потом со сцены не удалять //Ну и не добавляем её на сцену, разумеется parts[i * count + j] = part;//Оставляем ссылку на эту часть в массиве частей } } for (i = 0; i < needUpdatePart.length; ++i) { partIsEmpty[i] = true;//В начале все части пусты needUpdatePart[i] = false;//И не требуют обновления } } private function initBitmapDatas():void { var i:int; var bitmapData:BitmapData if (bitmapDatas) {//Если инициализируем контейнер не в первый раз (например, после перехода с одной карты на другую) for (i = 0; i < bitmapDatas.length; ++i) { bitmapData = bitmapDatas[i]; if (bitmapData) { bitmapData.dispose();//Удаляем все битмапдаты } } } //И инициализируем массив для битмапдат (и сами битмапдаты) заново bitmapDatas = []; bitmapDatas.length = count * count; for (i = 0; i < count; ++i) { for (var j:int = 0; j < count; ++j) { //Замените 0 на 0xFF000000 + 0xFFFFFF * Math.random(), если хотите увидеть границы частей bitmapData = new BitmapData(partSize, partSize, true, 0); bitmapDatas[i * count + j] = bitmapData; } } } public override function addChild(child:DisplayObject):DisplayObject { throw "Use addPlane";//Что б не забыться и не вызвать этот метод return child; } public function addPlane(plane:Plane):void { toRedraw.push(plane);//Надо перерисовать при следующем обновлении planes.push(plane);//Добавляем в массив поверхностей } private function redrawChild(plane:Plane, checkOutScreen:Boolean = true):Boolean { const x:int = plane.x; const y:int = plane.y; const w:int = plane.width; const h:int = plane.height; const bitmapData:BitmapData = plane.bitmapData; //Записваем координаты текущей поверхности в матрицу отрисовки в битмапдаты m.tx = x; m.ty = y; //"Координаты" части контейнера, в которой надо нарисовать данную поверхность const i:int = Math.floor(y / partSize); const j:int = Math.floor(x / partSize); //Выходит ли поверхность за пределы начальной части и по каким координатам const outX:Boolean = Math.floor((x + w - 1) / partSize) > j; const outY:Boolean = Math.floor((y + h - 1) / partSize) > i; //Отрисовываем поверхность во всех 1-4 частях var drew:Boolean = drawPlaneOnPart(i, j, bitmapData, checkOutScreen); var t:Boolean; if (outX) { //Не пытаться делать сразу drew &&= draw..(...), иначе, если drew сейчас = false, то вызова функции не будет t = drawPlaneOnPart(i, j + 1, bitmapData, checkOutScreen); drew &&= t; } if (outY) { t = drawPlaneOnPart(i + 1, j, bitmapData, checkOutScreen); drew &&= t; } if (outX && outY) { t = drawPlaneOnPart(i + 1, j + 1, bitmapData, checkOutScreen); drew &&= t; } return drew; } private function drawPlaneOnPart(i:int, j:int, planeBitmapData:BitmapData, checkOutScreen:Boolean):Boolean { //Номер части, куда будет рисоваться поверхность в этот раз const partNum:int = (i + halfCount) * count + j;//+halfCount - из-за того, что пол-карты в отрицательных y-коорд. partIsEmpty[partNum] = false;//Если пытались нарисовать, значит эта часть точно не пустая if (checkOutScreen) {//Если сейчас не надо рисовать за пределами экрана //Если часть, на которой надо рисовать, находится за пределами экрана, то ничего не рисуем if (!parts[partNum] || !parts[partNum].visible) return false; } //Корректируем координаты относительно конкретно этой части m.tx -= j * partSize; m.ty -= i * partSize; const bitmapData:BitmapData = bitmapDatas[partNum]; bitmapData.draw(planeBitmapData, m);//Рисуем needUpdatePart[partNum] = true;//Запоминаем, что надо обновить эту часть //Возвращаем назад m.tx += j * partSize; m.ty += i * partSize; return true; } private function updateVisible():void { const stageWidth:int = GV.stage.stageWidth; const stageHeight:int = GV.stage.stageHeight; for (var i:int = 0; i < parts.length; ++i) {//Проходимся по всем частям const obj:SpriteBase = parts[i]; if (obj) { const prevVisible:Boolean = obj.visible;//Была ли эта часть видна в момент вызова функции? const objGlobalX:int = obj.x + GV.world.x; const objGlobalY:int = obj.y + GV.world.y; //По положению части делаем её видимой или скрываем //Если станет невидимой, а до этого она была видима, то удаляем её со сцены //Или добавляем, если сейчас видима, а раньше была невидима //Обычно в этом смысла не много, но иногда сильно помогает, //Если размеры части в начале были определены очень неоптимально if (objGlobalX > stageWidth || objGlobalX + partSize < 0 || objGlobalY > stageHeight || objGlobalY + partSize < 0) { obj.visible = false; }else { obj.visible = true; } if (!obj.visible && prevVisible) { removeChild(obj); }else if (obj.visible && !prevVisible){ super.addChild(obj); } } } } public function drawOutScreen(timeToUpdate:int):void { const startTime:int = getTimer(); for (var i:int = 0; i < toRedraw.length; ++i) {//Отрисовываем всё, что надо отрисовать //Если рисуем вне экрана уже больше необходимого, то пора прекращать if (getTimer() - startTime >= timeToUpdate) break; const plane:Plane = toRedraw[i]; redrawChild(plane, false); } toRedraw = toRedraw.slice(i);//Обрезаем массив, оставляя в нём только то, что не успели нарисовать updateParts(); } public function update():Boolean { updateVisible();//Проверяем части на видимость в пределах экрана const notDrew:Array = []; notDrew.length = toRedraw.length; var countNotDrew:int = 0; for (var i:int = 0; i < toRedraw.length; ++i) {//Пробовать отрисовать всё, что надо отрисовать const plane:Plane = toRedraw[i]; const drew:Boolean = redrawChild(plane); if (!drew) {//Если не вышло notDrew[countNotDrew++] = plane;//Запомнить и попытаться позже } } notDrew.length = countNotDrew; toRedraw = notDrew; if (canClear) { canClear = false; clearEmpty(); } updateParts();//Выводим на экран return true; } private function updateParts():void { for (var i:int = 0; i < count; ++i) { for (var j:int = 0; j < count; ++j) { const part:SpriteBase = parts[i * count + j];//Отрисовываемая часть const partVisible:Boolean = part && part.visible;//В пределах ли экрана она? const needUpdate:Boolean = partVisible && needUpdatePart[i * count + j];//Надо ли её обновить? if (needUpdate) {//Если надо обновить //Отмечаем, что эта часть обновлена и в следующий раз делать это ещё раз без надобности не надо needUpdatePart[i * count + j] = false; //Отрисовка битмапдаты в спрайт const spriteBase:SpriteBase = parts[i * count + j]; spriteBase.setBitmapData(bitmapDatas[i * count + j]); } } } } private function clearEmpty():void { for (var i:int = 0; i < count; ++i) {//Проходим по всем частям for (var j:int = 0; j < count; ++j) { if (partIsEmpty[i * count + j]) {//Если текущая часть пуста const spriteBase:SpriteBase = parts[i * count + j]; if (spriteBase) { spriteBase.dispose();//Удаляем её parts[i * count + j] = null; } const bitmapData:BitmapData = bitmapDatas[i * count + j]; if (bitmapData) { bitmapData.dispose();//И её битмапдату bitmapDatas[i * count + j] = null; } } } } } private function find3d(x3d:int, z3d:int):Plane { for (var i:int = 0; i < planes.length; ++i) {//Ну тут всё просто вроде const plane:Plane = planes[i]; if (plane.x3d == x3d && plane.z3d == z3d) { return plane; } } return null; } public override function dispose():void { var i:int; for (i = 0; i < bitmapDatas.length; ++i) {//Удалим все битмапдаты const bitmapData:BitmapData = bitmapDatas[i]; if (bitmapData) { bitmapData.dispose(); } } bitmapDatas.length = 0; for (i = 0; i < parts.length; ++i) {//И все части const p:SpriteBase = parts[i]; if (p) { p.dispose(); } } parts.length = 0; super.dispose(); } } } DemiColor 1.0.1 Шаг за шагом (наброски, випы) с целью обучения Игра в изометрии, часть 1 [ скрытый текст ] При размерах 200х200 - не ниже 50 100х100 - вообще всегда 60 -Через несколько секунд, когда всё уже отрисовано, FPS стабильно держится на 60 -Перемещение по карте не влияет (или почти не влияет) на FPS Начнём урок Я работаю в FlashDevelop, компилировать буду для FlashPlayer (далее FP) версии 9.0 (зачем такая старая версия? Для не усложнения кода) Создавать всё с нуля не будем, мы не первые, кто добрался до изометрии Для начала советую ознакомиться с этим уроком: тема «Изометрическая проекция» Но всё же кое-что совсем-совсем простенькое сделаем и мы, лишь для проверки формул Для начала в папке проекта src создаём 2 папки: utils и gameObjects В папке utils создаём класс IsoUtils следующего содержания (откуда взялись все эти формулы - смотреть по ссылке выше): Код ActionScript: [ скрытый текст ] Цитирую (ActionScript) package utils { import flash.geom.Point; /** * ... * @author qwe */ public class IsoUtils { public static function isoToFlash(x3D:int, y3D:int, z3D:int):Point { return new Point( x3D + z3D, (x3D - z3D) / 2 - y3D ); } public static function flashToIso(flashX:int, flashY:int):Point { return new Point( int((flashX / 2 + (flashY - GV.SCALE / 2)) / GV.SCALE), int((flashX / 2 - (flashY - GV.SCALE / 2)) / GV.SCALE) ); } } } DemiColor 1.0.1 В папке gameObjects - класс Plane: Код ActionScript: [ скрытый текст ] Цитирую (ActionScript) package gameObjects { import flash.display.BitmapData; import flash.display.Sprite; import flash.geom.Point; import utils.IsoUtils; /** * ... * @author qwe */ public class Plane extends Sprite { public function Plane(x3d:int, z3d:int) { const bitmapData:BitmapData = Resources.getSimplePlaneBD();//Берём битмапдату и рисуем её в этом спрайте graphics.beginBitmapFill(bitmapData); graphics.drawRect(0, 0, bitmapData.width, bitmapData.height); moveTo(x3d, z3d);//Перемещаем в нужное место } private function moveTo(x3d:int, z3d:int):void { const t:Point = IsoUtils.isoToFlash(x3d, 0, z3d); x = t.x; y = t.y; } } } DemiColor 1.0.1 В самой папке src (далее именно она будет подразумеваться, если не указана конкретная папка) тоже создаём 2 класса: GV (для глобальных переменных и констант): Код ActionScript: [ скрытый текст ] Цитирую (ActionScript) package { /** * ... * @author qwe */ public class GV { public static const SCALE:int = 32;//Множитель координат, равен высоте картинки поверхности } } DemiColor 1.0.1 И Resources (который будет загружать ресурсы и отдавать): Код ActionScript: [ скрытый текст ] Цитирую (ActionScript) package { import flash.display.BitmapData; import flash.display.Sprite; /** * ... * @author qwe */ public class Resources { private static var bd:BitmapData; public static function init():void { var spr:Sprite = new Sprite();//Рисуем ромб spr.graphics.beginFill(0x00FF00);//С зелёным фоном spr.graphics.lineStyle(2, 0x000000);//И чёрной границей в 2 пикселя spr.graphics.moveTo(0, 16); spr.graphics.lineTo(32, 0); spr.graphics.lineTo(64, 16); spr.graphics.lineTo(32, 32); spr.graphics.lineTo(0, 16); bd = new BitmapData(64, 32, true, 0);//Сохраняем его в битмапдату с прозрачным фоном bd.draw(spr); } public static function getSimplePlaneBD():BitmapData { return bd;//Отдаём битмапдату с ромбом } } } DemiColor 1.0.1 А сам Main изменяем до такого состояния: Код ActionScript: [ скрытый текст ] Цитирую (ActionScript) package { import flash.display.Sprite; import flash.events.Event; import gameObjects.Plane; /** * ... * @author qwe */ 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); Resources.init();//Инициилизируем битмапдату, которую будем рисовать в качестве поверхностей //Делаем прямоугольник размером 7x3 из плиток for (var x:int = 0; x < 7; ++x) { for (var z:int = 0; z < 3; ++z) { const plane:Plane = new Plane(x * GV.SCALE, z * GV.SCALE); addChild(plane); } } } } } DemiColor 1.0.1 Результат: [attachmentid=1862850] Так, в правильности формул мы убедились Идём дальше Думаю, вполне очевидно, что 2 главные проблемы в изометрии во флэше: 1. Сортировка объектов и 2. Слишком большое их количество Одна из простейших оптимизаций насчёт сортировки подсказана ещё в уроке об изометрии, который дан в начале: Разобьём все объекты на (пока) 2 группы: поверхности (плитки) и неповерхности (дома, люди и т. д.) Поместим все плитки в какой-либо контейнер и забудем о них, т. к. сортировать их вместе с объектами не надо - они всегда самые дальние Но мы попытаемся при этом решить ещё и вторую проблему: у нас слишком много объектов. Карта имеет размеры 100х100? Будем создавать 10 000 плиток и добавлять их на сцену? Нет, флэш такое не потянет, надо другое решение, и вот оно: Создаём класс PlanesContainer, в который будем добавлять все плитки, но не в список отображения, а в массив Потом вызываем функцию отрисовки всех этих поверхностей Она отрисовывает все эти плитки в нужных (обычно в 1, но иногда и в 2 или 4 соседних), заранее подготовленных спрайтах таким образом, что у нас получается экономия в количестве спрайтов в сотни раз. Это очень неплохо сказывается на производительности Кроме того, если задуматься: создание 10 000 элементов и тем более их отрисовка - очень затратная операция Если сделать это в лоб, то приложение зависнет на несколько (десятков) секунд Попытаемся этого избежать: Сначала создадим и нарисуем только те элементы, что находятся в пределах экрана А потом, потихоньку, будем отрисовывать все остальные, стараясь не сбивать FPS [ скрытый текст ] Вообще, уже после того, как я это реализовал, у меня появилось смутное подозрение того, что я сделал велосипед, а именно - воркеры По идее, если использовать их сразу, а не реализовывать всё самому, то код, возможно, будет проще + воркеры используют многопоточность, а потому должны работать быстрее моего однопоточного велосипеда, но что сделано - то сделано Тем более воркеры появились только в версии FP 11.4 (насколько я помню), а у меня Linux, на который Adobe благополучно забила после версии 11.2, так что, думаю, я всё-таки не зря изобрёл велосипед для FP 9.0+ Про воркеры, кстати, тоже урок есть, если кто будет переделывать - понадобится Поехали Для начала, думаю, надо понимать, что на спрайтах мы далеко не уедем Поэтому переделываем класс Plane Код ActionScript: [ скрытый текст ] Цитирую (ActionScript) package gameObjects { import flash.display.BitmapData; import flash.geom.Point; import utils.IsoUtils; /** * ... * @author qwe */ public class Plane { private var _x3d:int; private var _z3d:int; private var _x:int; private var _y:int; public var index:int; public var bitmapData:BitmapData; public function Plane(x3d:int, z3d:int, index:int) { moveTo(x3d, z3d);//Перемещаем setIndex(index);//Устанавливаем картинку } private function moveTo(x3d:int, z3d:int):void { _x3d = x3d;//Запоминаем изометрические координаты _z3d = z3d; //Вычисляем координаты на экране и запоминаем и их const t:Point = IsoUtils.isoToFlash(x3d, 0, z3d); _x = t.x; _y = t.y; } public function setIndex(index:int):void { this.index = index;//Запоминание индекса bitmapData = Resources.planes[index];//Установка битмапдаты для этой поверхности } //Геттеры и сеттеры для стандартных свойств public function get x():int { return _x; } public function get y():int { return _y; } public function get x3d():int { return _x3d; } public function get z3d():int { return _z3d; } public function get width():int { return bitmapData.width; } public function get height():int { return bitmapData.height; } } } DemiColor 1.0.1 Выделение прозрачной ткани на зелёном фоне Мэйл-Арт ПОСТКРОССИНГ "Осеннее послание" Черно-белые антропоморфные иллюстрации Д.Альвареса Деми-приметы и прочие арт-суеверия Гараж №27 (Арт-кафе) Деми-приметы и прочие арт-суеверия ALEXSVIRID "Числовое" фото "Половинка и половинка". Фотомарафон. Наши цветы 8 Цитирую (Palыч) Всегда любым цветам рад, они мне просто нравятся И Вам Цитирую (tanita_m) Найду какую-нить незнакомую былинку galv6, Люблю эти синенькие! КРасивые!!! И пионы чудесные! [attachmentid=1862827] [attachmentid=1862828] Необычные скульптуры из стекла Кива Форда персы и другое "Проекты на FL.ru (Фри-ланс)" [ наверх ] Архитектура и дизайн Заполнение товара включая перевод с английского (авто тема) "Digg Top Stories" [ наверх ] How Mafia Pizzeria Busts Created One Of The Most Complex Criminal Trials Ever "Заказы для фрилансеров | Weblancer.net" [ наверх ] Настроить Redis - сделать его недоступным из вне. "Портал Бабр-Сибирь" [ наверх ] Клиенты турфирмы становились должниками банка "Каталог программиста" [ наверх ] Где можно скачать учебники по C... Защита формы паролем реализованная в Delphi Операция Антиспам "TOP-100 программ на freeSOFT.ru" [ наверх ] Kaspersky FREE 16.0.1.445 для Windows "AG.ru // Последние обновления сайта" [ наверх ] News: В For Honor будет одиночная кампания | |||
|
30 янв. 2016 г.
Свежие новости ленты "Почтовая" от 2016-01-30 06:02:37
Подписаться на:
Комментарии к сообщению (Atom)
Оповещение Google – JavaScript
JavaScript Ежедневное обновление ⋅ 19 января 2025 г. НОВОСТИ Google начинает требовать Jav...
-
Новости ленты " Почтовая" Свежие новости выписываемой вами ленты "Почтовая". Для ...
-
Новости ленты " Почтовая" Свежие новости выписываемой вами ленты "Почтовая". Для ...
Комментариев нет:
Отправить комментарий