TowerDefence #9. Кэширование объектов

В сегодняшнем уроке мы разберем кэширование игровых объектов и их повторное использование в игре.

В любой программе или игре при создании классов, объектов, переменных и т.п. под их использование выделяется оперативная память. Размер выделяемой оперативной памяти в основном зависит от сложности создаваемого класса и количества используемых им ресурсов. Например, для работы с простыми текстовыми данными не требуется большого объема памяти, а в случае с растровыми изображениями или звуками требуется много памяти. Что же будет происходить если постоянно удалять уже отработанные игровые классы и каждый раз создавать новые?

В Action Script 3 нет функции удаления не нужных классов, поэтому все созданные нами классы находятся в памяти до тех пор, пока на них существуют указатели из других классов. Но даже если вы обнулите все указатели на ненужный вам класс, он все равно никуда просто так из памяти не денется до тех пор, пока его не удалит сборщик мусора.

Сборщик мусора — это такой программный механизм встроенный в Flash Player, который через определенный интервал времени отслеживает все взаимосвязи классов и удаляет из памяти потерянные классы на которые нет указателей. То есть, чтобы удалить из памяти какой-либо класс или объект, достаточно всем переменным-указателям на него присвоить null.

Переменная — простые данные типа String, Number, int и т.п. либо указатели на классы;
Класс — набор переменных и методов, реализующих некий функционал;
Объект — под объектами я подразумеваю в основном классы, которые имеют визуальное представление на экране.

На создание и удаление новых классов и тем более объектов требуются некоторые ресурсозатраты. Любой бывалый программист с уверенностью скажет вам, что в процессе работы игрового приложения постоянно создавать новые объекты и удалять старые — это дурной тон, потому что это будет брать на себя слишком много процессорного времени. К тому же, флешовый сборщик мусора имеет не очень хорошую репутацию и умеет «сходить с ума». Но не принимайте это близко к сердцу. Я много раз читал об неадекватном поведении сборщика мусора, но сам с этим еще ни разу не сталкивался.

Судя по описанным случаям, неадекватное поведение сборщика мусора заключается в неправильной работе и постоянном перераспределении памяти, что приводит к жутким тормозам в игре. Основной причиной не адекватного поведения обычно служит слишком частое создание и удаление ресурсов во время игрового процесса.

Чтобы сэкономить процессорное время на создании и удалении игровых ресурсов, а так же в случае с Flash избежать в будущем проблем со сборщиком мусора — давным давно были придуманы простые механизмы кэширования данных. Cache в переводе с англ. означает «тайник, тайный запас», что с точки программирования примерно им и является.

Принцип работы тайного запаса

Принцип работы кэша достаточно простой: в процессе инициализации игры создается базовый набор игровых объектов и помещается в кэш, как в некую коробочку, откуда потом созданные экземпляры по мере необходимости извлекаются и используются в игровом процессе, а когда вновь становятся не нужны, то помещаются обратно в коробочку (в кэш), пока вновь не потребуются в игре.

Существует множество разнообразных алгоритмов работы кэша. Например, в некоторых реализациях не создается изначальный запас объектов и объекты создаются по ходу выполнения программы. ? когда они отрабатывают свои функции, то не удаляются полностью из программы и находятся в запасе до востребования. А когда требуется новый объект, то в первую очередь проверяется запасник на наличие свободных объектов, и если запас не пустой, то свободный объект быстро извлекается от туда для использования, а если в запасе ничего нет, то создается новый экземпляр.

Некоторые же вовсе не реализовывают никаких кэшев, устраивая структуру таким образом, что не нужные/умершие объекты находятся в структуре игры, но не обрабатываются и не рендерятся до тех пор пока они вновь не «воскреснут». Но давайте не будем зацикливаться на теории и перейдем к практике.

Профайлер

Профайлер — это утилитный визуальный класс, который наглядно отображает основные важные параметры игры, обычно это: кол-во кадров в сек. (fps), занимаемая память и в некоторых случаях время в миллесекундах затрачиваемое на рассчеты и рендер.

Поскольку мы тут все начинающие программисты, то мы не будем изобретать велосипед и писать свой профайлер (хоть это на самом деле не так уж и сложно). Для нашей игры в рамках урока мы возьмем уже готовый отлаженный профайлер с приятным оформлением и наглядным отображением информации: SWFProfiler. Встроить этот профайлер в игру достаточно просто:

  1. Скачиваем архив →
  2. Распаковываем, находим файл SWFProfiler.as и переносим его в нашу папку с игрой в пакет com.framework.*;
  3. Открываем SWFProfiler.as и изменяем первую строчку на «package com.framework»;
  4. Сохраняем изменения в SWFProfiler.as и закрываем его.
  5. Открываем наш класс App.as и в методе init() самой первой вставляем строчку: SWFProfiler.init(stage, this);
  6. Добавляем import com.framework.SWFProfiler в заголовке класса App.as;
  7. Компилируем игру и кликаем правой кнопкой мыши в флешку, выбираем пункт меню «Show Profiler» чтобы увидеть профайлер, а чтобы скрыть профайлер, выполняем те же самые действия.

Экспериментируем

Теперь давайте проведем пару экспериментов, чтобы увидеть наглядно как наши объекты занимают память и как происходит работа сборщика мусора. Запустите игру, откройте профайлер, нажмите и долго удерживайте пробел, чтобы выбежало как можно больше врагов. По мере появления врагов количество занимаемой приложением памяти будет неуклонно расти, это будет хорошо заметно на синем графике. А теперь поставьте башни, преимущественно вокруг финишных точек для врагов куда они бегут. По мере уничтожения и появления новых врагов и пуль синий график постоянно будет колебаться, словно волны на море.

Каждая кочка на синем графике — это максимальное занятое приложением количество памяти до того, как сборщик мусора выполнил свою работу.

Как можно видеть из эксперимента, график постоянно отражает борьбу, фактически двух стихий. Мы постоянно плодим мусор, удаляя ненужных врагов и пуль, а сборщик мусора их постоянно чистит :) Обратите так же внимание и на зеленый график, где отображается изменение количества кадров в секунду. ?, если внимательно присмотреться, то на момент работы сборщика мусора приходятся провалы fps. То есть, вывод один: чем меньше сборщику мусора работы, тем стабильнее будет наш fps и плавнее игра. Что же, давайте реализуем кэширование и посмотрим что из этого получится.

SimpleCache.as

Наша простая реализация кэша будет базироваться на примере патронной обоймы. Я надеюсь все знают, что такое обойма пистолета и видели как она работает. Наш простой кэш будет иметь аналогичный функционал: мы заряжаем нашу коробочку однотипными объектами и на готове держим последний добавленный объект. Когда объект улетает из нашей обоймы по нуждам игры, мы держим на готове следующий свободный объект, и так до тех пор, пока все объекты в обойме не закончатся. Так же когда объект возвращается в обойму, мы помещаем его в свободную ячейку и держим его на готове в числе первых. Еще нам нужно предусмотреть и тот факт, что обойма может быть совсем опустошена или переполнена. В первом случае нам необходимо будет экстренно создать новый объект и отдавать его как свободный, а во втором случае просто расширять вместимость обоймы.

Приступаем к реализации. Создаем новый класс в пакете com.framework с именем SimpleCache.as. В нем у нас будет три переменных:

protected var _targetClass:Class;
protected var _currentIndex:int;
protected var _instances:Array;

Далее пишем конструктор:

public function SimpleCache(targetClass:Class, initialCapacity:uint)
{
  _targetClass = targetClass; // Базовый класс всех объектов
  _currentIndex = initialCapacity - 1; // ?ндекс текущего свободного объекта
  _instances = []; // Список всех объектов
        
  // Заполняем обойму
  for (var i:int = 0; i < initialCapacity; i++)
  {
    _instances[i] = getNewInstance();
  }
}

В конструкторе кэша мы сохраняем тип класса хранящихся в нем объектов, инициализируем список объектов в кэше и заполняем его новыми объектами с помощью метода getNewInstance():

protected function getNewInstance():Object
{
  return new _targetClass();
}

Для извлечения объектов из кэша мы будем использовать метод get():

public function get():Object
{
  if (_currentIndex >= 0)
  {
    // Возвращаем свободный объект из кэша
    _currentIndex--;
    return _instances[_currentIndex + 1];
  }
  else
  {
    // Если обойма пуста, то экстренно создаем новый объект
    return getNewInstance();
  }
}

А возвращать освободившиеся классы в кэш мы будем через метод set():

public function set(instance:Object):void
{
  _currentIndex++;
  // Если обойма переполнена
  if (_currentIndex == _instances.length)
  {
    // То помещаем в конец массива, как новый элемент
    _instances[_instances.length] = instance;
  }
  else
  {
    // Помещаем в свободную ячеку массива
    _instances[_currentIndex] = instance;
  }
};

Вот собственно и весь класс простого кэша. Теперь реализуем его использование в нашей игре.

Реализация кэша в виде обоймы была подсмотренна на форуме flashgamedev.ru из примера кэша от scmorr.

Реализация кэша в игре

На данный момент в игре у нас два вида часто удаляемых и создаваемых объектов, для которых следует использовать кэш — это пули обычных пушек и обычные враги солдаты. Так же следует сделать кэширование и башен, они хоть и не так часто создаются и удаляются, но лишним это не будет.

Добавляем в класс Universe.as три новых публичных переменных:

public var cacheEnemySoldier:SimpleCache;
public var cacheGunTower:SimpleCache;
public var cacheGunBullet:SimpleCache;

Обратите внимание, для каждого типа объектов мы используем свой индивидуальный кэш. Теперь инициализируем кэши объектов в конструкторе Universe.as:

cacheEnemySoldier = new SimpleCache(EnemySoldier, 50);
cacheGunTower = new SimpleCache(GunTower, 20);
cacheGunBullet = new SimpleCache(GunBullet, 50);

Переходим к методу newEnemy() класса Universe.as и заменяем строчку создания нового врага на:

var soldier:EnemySoldier = cacheEnemySoldier.get() as EnemySoldier;

Далее переходим к методу buildTower() класса Universe.as и делаем тоже самое:

var tower:GunTower = cacheGunTower.get() as GunTower;

Теперь открываем класс GunTower.as и в методе shoot() заменяем строчку создания пули следующим образом:

var bullet:GunBullet = _universe.cacheGunBullet.get() as GunBullet;

Сейчас можно протестировать приложение и если все сделано правильно, то никаких изменений в работе игры вы не должны заметить :) Теперь у нас все башни, враги и пули берутся из кэша до тех пор, пока в нем что-то есть, а когда содержимое кэша заканчивается, то создаются новые экземпляры объектов «на лету». Сейчас нам нужно сделать так, чтобы использованные объекты возвращались обратно в кэш, начнем с пуль от башен.

Открываем класс GunBullet.as и переносим строчку создания графического спрайта из метода init() в конструктор класса:

_sprite = new GunBullet_mc();

Это нужно для того, чтобы спрайт создавался только один раз при заполнении кэша и находился в памяти. Далее перекрываем метод free() базового класса пули и изменяем его следующим образом:

override public function free():void
{
  if (!_isFree)
  {
    // Возвращаем пулю в кэш
    _universe.cacheGunBullet.set(this);
    super.free();
    _isFree = true;
  }
}

Теперь откроем класс BulletBase.as и добавим новую переменную:

protected var _isFree:Boolean = true;

Эта переменная будет отвечать у нас за текущее состояние объекта. Если объект не используется в игре, то значение равно true, иначе false. Далее в методе init() класса BulletBase.as добавьте строку:

_isFree = false;

Таким образом, если вдруг в наш объект попадут сразу 2-3 пули с небольшим интервалом, то он не попадет в кэш во множественном экземпляре и лишний раз не будет выполняться освобождение ресурсов.

Я надеюсь вы сделали домашнее задание из прошлого урока? Если нет, то тогда вам прийдется заглянуть в исходники к текущему уроку и доработать свой класс пуль, чтобы пули не улетали в бесконечные дали и при достижении пределов экрана удалялись из игрового мира и возвращались в кэш.

Теперь открываем класс EnemySoldier.as и переносим строчку создания графического спрайта юнита из метода init() в конструктор класса. Потом перекрываем аналогичным способом метод free():

override public function free():void
{
  if (!_isFree)
  {
    // Вовращаем юнита в кэш
    _universe.cacheEnemySoldier.set(this);
    super.free();
    _isFree = true;
  }
}

После этого не забудьте в классе EnemyBase.as добавить переменную _isFree:Boolean и в методе init() добавить несколько строчек:

isDead = false; // Воскрешаем юнита
_isFree = false; // Юнит используется
_speed.set(0, 0); // Обнуляем скорость движения

Все тоже самое теперь проделываем с башнями: модифицируйте класс GunTower.as, перекрыв в нем базовый метод free(), и добавьте работу с переменной _isFree в классе TowerBase.as. После того, как выполните изменения в башнях, можете протестировать приложение и если все сделано правильно, то визуально вы опять же не заметите никаких изменений :) На самом деле, для текущего состояния игры данные изменения почти никак не скажутся на производительности игры, но в будущем, когда игра станет более масштабной стратегией, у нас не возникнет вышеописанных проблем с производительностью.

Теперь вы можете провести эксперименты как мы делали вначале урока, и обратить внимание на ощутимую разницу на графике в профайлере. Созданные объекты занимают память и находятся в ней все время по ходу выполнения игры, позволяя использовать себя многократно. Таким образом, потерянных ресурсов меньше, и благодаря этому сборщик мусора реже выполняет свою работу и меньше тратит на это процессорного времени. Тут хочется отметить, что не нужно чересчур зацикливаться на показаниях графика. Сделать идеальную прямую на синем графике у вас едва ли получится, так как в любом случае во время работы программы все равно будет появляться некий мусор, который сборщик мусора будет регулярно убирать. Наша же задача уменьшить количество этого мусора в критически важных местах со своей стороны программы.

Утечки памяти

Раз уж мы заговорили о кэшировании, профайлерах и связанных с этим проблемах, то затроним быстренько и тему утечек памяти, что это такое и почему это случается.

Утечки памяти — это когда программа все больше и больше занимает оперативной памяти для своих нужд, хотя на самом деле ей она не нужна. Утечки памяти могут приводить к критическим ситуациям в тот момент, когда заканчивается отведенная для Flash Player оперативная память. Чаще всего это заканчивается зависанием Flash Player вместе с браузером пользователя.

Причина утечек памяти в основном заключается в неправильном или в неполном освобождении ресурсов программы. То есть, если вы постоянно создаете новые игровые объекты и при их освобождении не до конца обнуляете на них указатели, то сборщик мусора считает их используемыми и в конце концов, создавая при этом новые объекты, приложение занимает все больше памяти, что в итоге приводит к локальной катастрофе. Кэширование объектов в некоторой степени предотвращает утечки за счет того, что мы почти не создаем и не удаляем объекты во время работы приложения.

Ну и в заключение сегодняшнего урока мы еще разберем проблему из прошлого урока.

Решение проблемы с потерей пути юнитами

В прошлом уроке читатели заметили важную проблему, из-за которой юниты сбивались с курса и убегали за пределы карты. Сегодня я решил еще вернуться к этому вопросу и объяснить причины этой аномалии. А причин тут целых две:

  1. Основная причина — это использование динамического deltaTime. Как вы помните, все наши перемещения и расчеты для врагов хоть и привязываются к Event.ENTER_FRAME, но расчитываются исходя из прошедшего времени между кадрами. Таким образом, если вдруг возникает «тормоз» длинной в 2-3 секунды, то соотвественно за это время должно пройти 70-105 кадров, но из-за «тормоза» не проходит ни одного кадра, а тик времни уже засечен. Соотвественно, время идет, а кадры «замерзли» и при наступлении следущего кадра в расчете deltaTime считается, как будто уже прошли эти самые 70-105 кадров, таким образом все объекты словно телепортируются на новые места без учета расчетов и проверок.
  2. В методе update() для врагов во время движения не выполняется перерасчет угла и векторной скорости, таким образом, если вдруг юнит пробежал свою контрольную точку и проверка на её достижение не сработала (а проверка как раз не срабатывает из-за «телепортации»), то юнит продолжает свое движение заданным курсом в неизведанную бесконечность.

? так, решений тут тоже может быть два:

Первое. Если вдруг пользователь переключил вкладку браузера или вовсе свернул окно с Flash приложением в панель задач — то соответсвенно Flash «замерзнет» и времени между кадрами может пройти бесконечно много, и когда пользователь вдруг решит вновь вернуться в игру, то он с ужасом обнаружит, что все куда-то исчезли. Решение тут достаточно простое — сделать макисмально допустимый deltaTime :) Сделать это не сложно, открываем класс Universe.as и добавляем новую переменную:

private var _maxDeltaTime:Number = 0.04;

Теперь перейдем в метод enterFrameHandler() и добавим проверку на макисмальный deltaTime после его рассчета:

_deltaTime = (_deltaTime > _maxDeltaTime) ? _maxDeltaTime : _deltaTime;

В целом этим решением можно закрыть проблему потери курса, но для желающих сделать все надежно, можно еще добавить перерасчет угла и векторной скорости юнита. Для этого открываем класс EnemySoldier.as и добавляем новую переменную:

private var _calcDelay:uint = 0;

Теперь добавим следующий код в метод update() непосредственно перед кодом смещения юнита:

// Обновление угловой скорости
if (_calcDelay > 10)
{
  // Обновление угловой скорости
  var angle:Number = Amath.getAngle(x, y, _targetPos.x, _targetPos.y);
  _speed.asSpeed(_defSpeed, angle);
  _calcDelay = 0;
}
_calcDelay++;

Теперь каждые 10 кадров будет обновлятся направление движения юнита, и если он вдруг по какой-то причине собъется с курса, то он непременно вернется назад к текущей контрольной точки пути.

Домашнее задание

В рамках этого урока я решил ограничиться 3 видами башен и 3 видами врагов, зато мы их сделаем разнообразными. Я предлагаю такие разновидности врагов:

  1. Солдатик (уже реализован);
  2. Джип;
  3. Танк;

Разновидности башен:

  1. Пушка (уже реализована);
  2. Ракетница (с самонаводящимися ракетами);
  3. Энерго пушка (с электро разрядами);

Ваша задача подумать над их внешним видом и подготовить заготовки их классов и графики, чтобы в ближайшие 1-2 урока мы уже смогли бы оперативно разработать интерфейс.

Заключение

Сегодня мы рассмотрели принцип кэширования игровых объектов и реализовали их кэширование в игре, что в будущем поможет нам избежать некоторых проблем с производительностью и возможных утечек памяти.

В будущем я решил уменьшить объем каждого урока, рассматривая не более одной новой темы за раз. Так уроки будут меньше, мне их будет проще готовить и на мой взгляд они получатся более понятными, и вам будет легче их разбирать. Кроме этого каждый из уроков с конкретной темой может быть для кого-то полезным отдельно от всей серии уроков, да и мне будет проще определиться со следующей темой ;)

В следующем уроке мы разберем отображение жизни для врагов.

Ссылка на исходники — CS4, *.zip, 159кб.

Результат

В левом верхнем углу отображается заполненность каждого из кэшей. Чтобы открыть профайлер, кликните правой кнопкой мыши и выберите пункт «Show Profiler».

Содержание

  1. Вступление
  2. Структура игры
  3. Карта проходимости
  4. Первый враг
  5. Готовимся к поиску пути
  6. Поиск пути
  7. Редактор уровней
  8. Движение врагов
  9. Первая башня
  10. Кэширование объектов
  11. Полоса жизни
  12. Вражеские волны
  13. Загрузка вражеских волн
  14. Продолжение следует...

 

 

Ну старик, какая красота.

torboot
27 Апреля 2011
— 01:39
#

Спасибо!) Всё очень круто :)

Sckavo
27 Апреля 2011
— 01:51
#

Спс за труд.

Глюк с призраками остался. Теперь его можно словить и не переключаясь, а достаточно просто перегородить дорогу юнитам. По крайней мере это у меня очень хорошо получалось с теми которые выходяи из правого нижнего угла - некоторые либо просто стоят на месте либо "идут напролом"
http://i041.radikal.ru/1104/11/b1ffcaea701c.gif

з.ы. "В правом верхнем углу отображается..." - в левом.

NoICE
27 Апреля 2011
— 05:50
#

Спасибо за статью. Нужно еще добавить возможность контролировать объем кеша иначе он распухает при пиковом потреблении игровых объектов и продолжает занимать большое кол-во памяти после их удаления со сцены. Что и есть утечка памяти. У меня в Вашем примере размер кеша врагов дошел почти до двух сотен экземпляров

icewind
27 Апреля 2011
— 09:47
#

Как обычно на высоте и урок очень в тему. До сегодня не совсем понимал алгоритм работы кеширования, тогда как проблемма уже появилась и ее надо решать.

Спасибо Антон, что продолжаешь серию уроков!

jarofed
27 Апреля 2011
— 11:01
#

@NoICE, глюк с движением юнитов на пролом или их зависании на одном месте уже заключается в другом — они не могут найти путь до целевой клетки поскольку путь перегорожен. Эту проблему мы устраним немного позже запретив строить башни таким образом чтобы они полностью перекрывали дорогу юнитам.

Ant.Karlov
27 Апреля 2011
— 11:25
#

@icewind, контролировать объем кеша нет необходимости, потому что его переполнение напрямую зависит от того как программист написал код. То есть, поскольку в финальной игре будут использоваться вражеские волны с миксом разнотипных врагов, на количество которых игрок ни коем образом не сможет повлиять, то возможное переполнение кэша будет зависеть от того как мы сбалансируем вражеские волны/игровые уровни.

Респавн врагов на пробел в данный момент используется лишь просто для быстрой отладки алгоритмов игры и создания крэш-тестов :)

Ant.Karlov
27 Апреля 2011
— 11:30
#

Спасибо Anton.
Sorry for my english but its better then my russian;)
Your lessons are really helpfull.
Going throug your tutorial is pleasure at end of the day(if i find enough time)
I wish I will be able to repay you somehow in the future.

Junak
27 Апреля 2011
— 12:29
#

Спасибо за урок) Неожиданный, кстати, момент. Не думал, что кэширование может быть так реализовано.

А почему в Профайлере показаны данные фпс, отличающиеся от расчетинных в вашем коде?

Андрей
27 Апреля 2011
— 12:29
#

@Ant.Karlov, в данном приложении действительно нет :) Я это упустил. Просто подумал как сделать класс более универсальным

icewind
27 Апреля 2011
— 12:35
#

? в моей игре, фпс расчитанный фпс оказался отличным от показаний профайлера. Странно.

Андрей
27 Апреля 2011
— 13:18
#

Антон, СПАС?Б?ЩЕ, пост в тему!

В данном примере разобран кеш графических объектов, а как быть если в игре используется физика (Box2D или Nape)?

Вадим М.
27 Апреля 2011
— 13:47
#

@Андрей, если рассчитанный вами fps отличается от fps в профайлере, то это значит что скорее всего вы его не правильно рассчитываете.

Ant.Karlov
27 Апреля 2011
— 15:08
#

@Вадим М., работая над новой игрой я думал о том как можно было бы сделать кэширование физических объектов. Но пришел к выводу, что из этого ничего хорошего не получится. Например, в Box2D для создания физических объектов используются классы помошники на основе параметров которых создаются физические тела. То есть, если удалить тело из физического мира, то вернуть его обратно уже нельзя, можно создать только новое. Поэтому я решил кешировать непосредственно свои игровые объекты, а их физические тела пересоздаются по новой после извлечения объекта из кэша в момент воскрешения.

Ant.Karlov
27 Апреля 2011
— 15:17
#

В данный момент именно так и делаю, перед удалении объекта из физ.мира копирую его свойства во временный объект и потом создаю новый, такой же объект. Графику при этом не кеширую, надо будет попробовать твой способ и посмотреть результат...

Антон, как считаешь какое оптимальное потребление оперативной памяти для игры.

Например моя игра в данный момент потребляет согласно SWFProfiler: 411-418 Мб, много это или мало?

Сколько потребляет памяти твоя новая игра?

Вадим М.
27 Апреля 2011
— 16:29
#

@Вадим М., оптимальное потребление памяти считается не более 80мб. Во всяком случае так считают те гики-разработчики с которыми мне довелось поработать. Mining Truck 2 потреблял от 150 до 200мб, но в случае с MT сделать меньшее потребление памяти не удавалось из-за больших уровней. Новая игра на данный момент потребляет в среднем 120мб (от 110 до 150). В новой игре уровни меньше почти в 3 раза, но зато намного больше разных анимированных объектов в разных состояниях.

На мой взгляд потребление памяти до 200+ мб — это еще терпимо, так как современные компьютеры в среднем имеют 2гб памяти. Но более 300мб — это конечно уже перебор и с этим надо что-то делать.

Ant.Karlov
27 Апреля 2011
— 17:50
#

@Вадим М., еще важный момент: потребление памяти в отладочном Flash Player в почти в два раза больше чем в обычном плеере (скорее всего это связанно с тем что в отладочном плеере используется больше ресурсов для программы чтобы в случае ошибки дать разработчику полный отчет). Поэтому чтобы наверняка знать сколько ваша игра будет занимать оперативной памяти у игроков, следует запускать её в обычном Flash Player (не дебаг версия).

Ant.Karlov
27 Апреля 2011
— 17:55
#

В релизной версии игры, объем потребляемой памяти составляет 350-370 мб... Да ужжжжжж, УЖАЗзззззз!!!

В самом начале игры я использую классы кеширования Анимации от "тачмайпиксель" о которых уже много раз говорилось. Я так понял, что кеширование анимации происходит в оперативную память, а не на жесткий диск пользователя. Во время кеширования анимации я могу наглядно это наблюдать в SWFProfiler, он прямо на моих глазах разрастается и остается на том же уровне в течении всей сессии игры.

Как считаешь, может в этом и есть основная проблема? Какие варианты предложишь для её решения?

Вадим М.
27 Апреля 2011
— 18:54
#

Поменьше мелких классов создавайте, один заголовок класса весит около 56 байт, а то и больше (для сравнения переменная типа int весит 4 байта). При наличии тысячи простеньких классов (хранят всего пару переменных) перерасход на хранение заголовка просто сумасшедший.

Кеширование тоже стоит применять только к таким объектам как пули и другим не долго живущим спецэффектам. В остальных случаях перерасход не окупится.

? еще, вы наверное втупую растеризуете анимации из вектора в битмапы. Советую посмотреть сколько полезной площади битмапа используется под хранение изображения. Наверняка более 50% это полностью прозрачные области. Решение - растеризовать заранее и разбивать на несколько изображений с подгонкой размеров для оптимального использования пространства. ? не забывайте, что все размеры текстур всегда кратны степени двойки (требование аппаратной части), так что делать узенький но длинный страйп весьма весьма необдуманно.

BuxomBerry
27 Апреля 2011
— 19:15
#

Ant.Karlov давай уже скорее с процессом рисования персонажа из Zombotron, неделю жду...

TRaY
27 Апреля 2011
— 19:56
#

@Вадим М., флеш не имеет доступа к жесткому диску пользователя без разрешения (не считая SharedObject который ограничивается несколькими килобайтами), соответственно все данные хранятся в оперативной памяти.

Безусловно если ты всю графику игры кэшируешь на этапе загрузки игры, то это одна из основных причин куда уходит столько памяти. Как вариант: можно кэшировать ресурсы игры по мере необходимости. Например, если у тебя есть объекты которые начинают использоваться только в середине игры, то именно в этот момент их и следует кэшировать. При этом удалять из кэша те объекты которые в ближайшее время не будут использоваться в игре. То есть тут следует разделить объекты на регулярно используемые и на редко используемые и кэшировать редко используемые объекты непосредственно перед их использованием, например на этапе создания уровня.

Ant.Karlov
27 Апреля 2011
— 23:20
#

@BuxomBerry, когда размер используемой оперативной памяти идет на сотни мегабайт, то на классах экономить уже как то странно :) В любом случае отказываться от классов, то есть жертвовать удобством разработки приложения следует в последнюю очередь.

Если под «растеризировать заранее» подразумевается ручное сохранение векторных клипов в растр с последующей сборкой спрайтов из кусочков — то это тоже сомнительный совет. В первую очередь размер *.swf быстро разбухнет, а во вторых этой рутиной заниматься жутко не удобно, долго и утомительно. А размер текстур в степени двойки наверное станет актуальным только после выхода Flash Player 11.

Ant.Karlov
27 Апреля 2011
— 23:28
#

@TRaY, поспешу вас расстроить, но я вам не чем не обязан и ничего не обещал, так что скорее всего ждать вам прийдется еще не одну и не две недели. Это авторский блог и пишу я здесь о том, что считаю нужным и исключительно под свое настроение. Запасайтесь терпением.

Ant.Karlov
27 Апреля 2011
— 23:32
#

Ant.Karlov, большое спасибо за то, что делишься своими знаниями. Очень интересно, каждый новый урок, как праздник :)

dada
27 Апреля 2011
— 23:49
#

@Ant-Karlov:
Вот тебе ситуация. Делаешь мудреный тайловый движок. Каждый тайл может быть и анимированным и трансформируемым, вобщем не просто номер квадрата тайла в текстуре (т.е. требуется определенно больший набор параметров). Очевидно решение - сразу написать класс Tile с удобной структурой и богатым функционалом на будущее. Причем это нормальная практика в ООП оформлять даже простейшие структуры в виде отдельного класса.
По логике ничего страшного, классы не наследуют никаких DisplayObject, просто структуры для удобного хранения и обработки данных.

А теперь, на выходе требуется сделать сцену 1000х1000 тайлов. Это 1 млн. образцов классов. Пускай заголовок класса требует 56 байт, независимо от его структуры.
Объем занимаемой памяти под заголовки:
(56*1000*1000) / 1048576 = 53 Мб.
Неплохо?
А если не использовать классы, а просто организовать сплошной массив параметров для этих 1 млн. тайлов? Плюс все функциональные возможности по обработке тайлов легко закладываются в менеджера-обработчика. Думаю 53 мегабайта окажутся не лишними.
Кстати добавь к ситуации выше необходимость делать несколько слоев тайлов, а ведь это практически в любой игре нужно.

Окей, можно и не растеризовать заранее, но тогда хотя бы правильно анимации в векторных клипах организовывать. Не рисовать целый мультик внутри одного клипа, где персонаж размеров в 64х64 пикселя машет хлыстом в радиусе 200-300 пикселей. Этот хлыст можно и отдельно вынести и тогда растеризованное изображение не будет таким огромным.

А насчет разбухания SWF, конечно если персонаж в векторе это просто цветное пятно с глазками, то хранить его в растре накладно. Но если персонаж рисуется с кучей деталей, то хранение в векторном виде намного накладнее чем в PNG картинке. Можешь сам проверить, переведи в вектор растровую картинку с кучей цветов (ту же фотку) и сравни с ее PNG вариантом. Пиксель это всего 4 байта (причем всегда), а векторный примитив тот же треугольник - это 6 байт для хранения координат вершин, а если это не треугольник а что-то более сложное? Причем примитив может реально на изображении быть даже меньше пикселя. Откуда уверенность что векторная графика всегда занимает меньше памяти?

А размер текстур в степени двойки это суровая реальность. Flash не более чем программная оболочка и все свои текстуры он так или иначе вынужден передавать видеокарте. А подготовка специальных вариантов текстур убила бы производительность, поэтому он передает те самые Bitmap с которыми ты работаешь, без изменений размера. Размеры этих битмапов автоматически округляются видеокартой под размер степени двойки, по другому она не умеет. Значит твое веселенькое изображение в 386х270 пикселей (ты думал оно не слишком большое, ага...) будет натужно рисоваться видеокартой по текстуре размером 512х512 пикселей (О_о боже мой как же так). А значит в какой-то момент видеопамять может просто переполнится и твоя прога встанет в очередь, а пользователь узрит ТОРМОЗААААА.... :)

BuxomBerry
28 Апреля 2011
— 09:41
#

@BuxomBerry, конечно классы следует использовать с умом. В случае с тайловым движком подход к его реализации должен быть совсем иным, так как Flash едвали потянет обработку миллиона тайлов. Я придерживаюсь мнения что экономить на классах оперативную память — это не верный подход, так как при больших потреблениях памяти проблему надо искать в другом месте.

Безусловно в случае очень детализированного векторного изображения оно может занимать больше места чем обычный растр. Но не забываем что вектор сжимается в *.swf лучше чем растр. Во всяком случае я не могу найти другого объяснения тому, как все то безумство которое я рисую умещается в месте со всеми ресурсами игры в 4-5мб.

К сожалению меньше пикселя ничего не может быть. Да, есть еще дробные числа, но в этом случае примитив будет рисоваться полупрозрачным пикселем. Если я не ошибаюсь, то наши мониторы и видеокарты работают исключительно с пикселями и половину пикселя они в реале никак нарисовать не могут.

> А размер текстур в степени двойки это суровая реальность.

Флеш изначально создавался для работы с векторной графикой которая может масштабироваться в реальном времени и без потери качества. Как это все работает и в какой последовательности — мне плохо известно. Но поскольку у флеша нет аппаратной поддержки, предполагаю, что в видео карту отправляются уже «отрендеренные» векторные или растровые объекты в правильной нарезке. ? в текущей версии флеша нет понятия текстур как таковых, поэтому считаю что за их степенью двойки следить как минимум бесполезно до тех пор пока мы не получим аппаратную поддержку и не начнем работать напрямую с аппаратной частью.

Ant.Karlov
28 Апреля 2011
— 11:34
#

@Ant-Karlov:
>Flash едвали потянет обработку миллиона тайлов
Еще как тянет и намного больше. ?з них конечно отрисовываются только те, что видны на экране. Но общее количество этих тайлов вообще на производительность не влияет, только на объем занимаемой памяти.

В случае флеша вектор и растр сжимаются ничуть не лучше текста или звуковых данных. Все дело в ZLIB, именно он родимый сжимает ваши SWF и он работает только с массивами байтов.
Зато в случае с растром можно использовать еще и JPEG сжатие и флеш при этом хранит альфа-канал отдельно. А уж JPEG сжатие позволяет сжать растр намного сильнее чем ZLIB сжимает векторы в байтовом представлении. Только качество теряется.

BuxomBerry
28 Апреля 2011
— 12:16
#

Новичкам бы посоветовал почитать вот это...

Stepan
28 Апреля 2011
— 12:51
#

Ant.Karlov ладно жду!
а пока попрактикуюсь сам)))

TRaY
28 Апреля 2011
— 15:47
#

Антон, спасибо за данную статью именно в ней я узнал, что такое профайлер и как им правильно пользоваться.

Мои эксперименты увенчались успехом. Переделав некоторые мувики, количество потребляемой оперативЫ снизилось с 411 - 418 до 50 - 70 Мб. Основная ошибка была в том, что я кешировал не только игровые объекты, которые участвуют в игре, но и объекты анимационных кнопок, менюшек и даже экранов, больших размеров.

Теперь моя игра - это симбиоз вектора и растра). Кнопки, игровые экраны - вектор, игровые объекты - растр. В дальнейшем буду думать, как всё перевести в растр, т.к. мне кажется, что флеш плеер с растром дружит лучше, да и для дальнейшего портирования игры - это только на пользу.

Я в шоке, что классы по кешированию анимации могут, как помочь, так и навредить игре!!! Будьте аккуратны с "тачмайпиксель"

Вадим М.
28 Апреля 2011
— 16:16
#

Наверное, стоит добавить в класс кэширования восстановления начальных состояний объектов :) Ну там x,y, alpha, scaleX, scaleY, visible, rotation.

Platon
29 Апреля 2011
— 09:57
#

@Вадим М., да флеш с растром работает намного быстрее. Но поскольку на Flash создаются все-таки «недоигры» которые являются частью сайта и работают непосредственно в браузере, то приходится балансировать на гране между растром и вектором с целью получить оптимальную производительность и небольшое потребление оперативной памяти.

Я в своих играх так же сочетаю растр и вектор. Все игровые меню, кнопки, тексты — это по возможности вектор. Если игровые меню состоят из сложного вектора, то приходится его растеризировать на лету. А вся игровая графика это в основном растерезированный заранее вектор.

Ant.Karlov
29 Апреля 2011
— 11:40
#

@Platon, добавления сброса параметров непосредственно в класс кэша — это уже на ваше усмотрение. Но я бы этого делать не стал, потому что в кэше могут хранится классы не только унаследованные от DisplayObject. Да и отслеживание состояния объектов находящихся в кэше не входит в обязанности кэша. Лучше сделать у объектов что-то вроде метода init() или reset() в котором бы выполнялся сброс всех параметров объекта на стандартные и вызывать этот метод перед использованием объекта в игре.

Ant.Karlov
29 Апреля 2011
— 11:45
#

Антон, разобрался с фпс, ничего страшного там не было, просто округлялся некорректно.

Но у меня другая проблема выявилась с Профайлером. Моя простенькая игра начинает с 15 мб, и очень медленно но неуклонно набирает память. Через пол часа уже становится 30 мб и по чуть чуть продолжает прибавлятся. Хотя в игре ничего не происходит, персонажи случайно двигаются и обсчитывают свое поведение, а в каждом кадре перерисовывается [холст BitmapData.

Это утечка памяти, следует искать в моих структурах данных или это так флеш и работает?

Андрей
29 Апреля 2011
— 15:06
#

Дополню описание ситуации:

?спользую рендер в BitmapData.
Мувайклипы использую только для хранения кадров, при инициализации игры копирую методом draw в массив битмапов.
?спользую Box2D.
Оставляю игру на пол часа, в этот момент новые объекты не создаются, идет непрерывный обсчет имеющихся.

Такая утечка памяти как у меня (с 15 мб до 30 за пол часа + процесс этот медленно, но стабильно идет дальше) это точно косяк у меня или это какая-то особенность работы флеша?

Андрей
29 Апреля 2011
— 16:11
#

@Андрей:
?щите места где указатели на объекты, созданные с помощью new, не обнуляются, даже если эти объекты вы считали удаленными - это не так, пока активен хотя бы один указатель.

Вот вам дурной пример:

for (var i:int = 0; i< 1000; i++)
player.SetPos(new Point(x,y));

BuxomBerry
29 Апреля 2011
— 20:00
#

@Андрей, да, описанная проблема похожа на утечки. Если не создается никаких новых ресурсов, а занимаемая память постоянно растет, значит нужно пробежаться рефакторингом по коду. В первую очередь проверить рендер, потом игровые объекты и т.п. Если в коде для расчетов постоянно создаются новые переменные/классы, то их следует создать однажды и использовать повторно.

Ant.Karlov
30 Апреля 2011
— 12:34
#

@Андрей. В плеере 10.1 был баг с утечкой памяти в экземплярах BitmapData, временное решение есть в комментариях к треду по ссылке ниже.
https://bugs.adobe.com/jira/browse/FP-4812

@BuxomBerry Приведенный пример не иллюстрирует утечки памяти, если внутри метода SetPos "указатели" на экземпляры Point не сохраняются, то ГК их нормально соберет.

Claymore
30 Апреля 2011
— 13:01
#

@BuxomBerry, Ant.Karlov, спасибо вам, буду проверять.
@Claymore, спасибо за информацию, попробую изучить моя ли это ситуация.

Андрей
30 Апреля 2011
— 13:39
#

Антон,спасибо большое вам за столь интересные уроки.Можно целыми днями просиживать в интернете и при этом больше нигде не найти столько "полезностей",сколько их тут.

knight of honor
1 Мая 2011
— 10:09
#

Спасибо большое за статью, но возник вопрос.
Как можно предотвратить блокирование спавна башнями игрока?Чтобы пусть всегда находился, и последнюю "дырку" игрок своей башней застроить не смог?Бьюсь над этим уже второй день...

John
3 Мая 2011
— 13:06
#

@John, скоро я об этом расскажу в одном из уроков подробно. Но если в двух словах, то все до безобразия просто: нужно установить башню во временную маску и найти пути по этой маске из всех стартовых точек в финишные. Если вдруг путь от одной до другой точки найден не будет, значит башня перекрыла дорогу и нам остается только сообщить игроку, что башню тут строить нельзя ;)

Ant.Karlov
3 Мая 2011
— 13:34
#

Прочитал и сижу думаю... А чем Вам методы push и pop класса Array не угодили, что нужно так заумно все делать?) Там же даже если не хотите стек, а магазин сделать, то есть методы shift и unshift... ?ли может это не круто?) Просто спрашиваю ради интереса, потому что от недавнего времени стал часто пользоваться этими методами :)

pSi_X
10 Мая 2011
— 01:53
#

? ещё, простите мне за грубость, спасибо за прекрасную статью, начал подумывать на счет реализации кэша :)
Кстати есть один маленький вопрос, не знаю уместен ли он. Как вы думаете, если создать класс, а в нем массив и поместить в него несколько объектов таким способом
arr.push({bitmapData:bitmapData});
Потом создать другой класс и использовать эту битмапу для визуального отображения. Если отредактировать объект этого второго класса, изменится ли сама битмапа в массиве первого класса?
Ещё раз прошу прощения, если не в тему :)

pSi_X
10 Мая 2011
— 02:02
#

@pSi_X, методы push и pop класса Array более медленные по сравнению с приведенными способами работы с массивами.

Ant.Karlov
10 Мая 2011
— 11:38
#

@pSi_X, а с какой целью помещать один bitmapData в массив обернутым в Object?

> Если отредактировать объект этого второго класса, изменится ли сама битмапа в массиве первого класса?

Да, изменится. При обычном присвоении класса другому классу, как правило передается указатель на класс оригинал. В случае с битмапами для явного копирования следует использовать copyPixels().

Ant.Karlov
10 Мая 2011
— 11:43
#

А почему они могут быть медленней? Просто я считаю что в adobe не дураки сидят, проще чем они мне кажется не сделать эти методы, или Вы знаете того чего не знаю я?))
Если Вы правы, то почему бы не реализовать самим класс с такими реализациями методов push и pop как у вас? ?ли это будет не оптимизация, а наоборот добавление лишнего кода?

>а с какой целью помещать один bitmapData в массив обернутым в Object?

ну это как пример, в объекте, чтобы там id добавить, комментарий, параметров :)

>Да, изменится. При обычном присвоении класса другому классу, как правило передается указатель на класс оригинал. В случае с битмапами для явного копирования следует использовать copyPixels().

Тогда да, я был прав и кеш почти единственный вариант спасения.

pSi_X
10 Мая 2011
— 16:40
#

@pSi_X:
Дело не в ребятах из Adobe, они то свое дело знают. Методы push и pop предназначены НЕ для просмотра элементов, а для их извлечения/помещения (гуглите стек). Это как минимум потребует выделение памяти под размещаемые элементы и процесса очистки для извлекаемых, что разумеется требует больше времени чем простой доступ к данным.

BuxomBerry
10 Мая 2011
— 17:30
#

@BuxomBerry:
А действительно, я чего-то сразу не досмотрел. Что-то мне сразу показалось, что при доставании из кэша отдается именно сам объекта а не ссылка на него. Тогда да, я не прав, прошу прощения за свою невнимательность >_<

pSi_X
10 Мая 2011
— 18:10
#

Кстати, всяческие маньяки-оптимизаторы много где писали, что вставка по индексу в разы быстрее чем push. Например вот эту презентацию можно полистать. Не знаю кто там в Адоби сидит, ну уж точно не оптимизаторы.

Claymore
11 Мая 2011
— 16:14
#

Ant.Karlov ну что вы снимаете процесс рисования зомби?

TRaY
11 Мая 2011
— 16:58
#

Предлагаю забанить товарища TRaY в интернете.

google bot
11 Мая 2011
— 19:45
#

@Claymore:
Спасибо за презентацию :)

pSi_X
11 Мая 2011
— 21:07
#

TRaY, посмотри урок про рисование дерева и после этого ты сможешь нарисовать себя.

Андрей
12 Мая 2011
— 00:11
#

@pSi_X:
Держи тогда еще блог маньяка-оптимизатора - куча всяческих тестов производительности , есть даже примеры с разбором генерируемого байт-кода.

Но помни первую заповедь оптимизатора: "Premature optimization is the root of all evil" ;)

Claymore
12 Мая 2011
— 03:23
#

Андрей всё уже не надо, начинаю осваивать сам! Просто я больше веб-дизайном занимаюсь, а вектор что-то не успел освоить)

TRaY
12 Мая 2011
— 05:32
#

P.S не нужно меня банить, я больше не буду!
потому что сам уже почти научился)

TRaY
12 Мая 2011
— 14:28
#

Антон,
хотел поинтересоваться, как дела у приложения "Грузовичок" в контакте?
база пользователей немаленькая, а сколько там активных? ? если не секрет, какой профит?

Platon
12 Мая 2011
— 18:24
#

@Claymore:
Пасибки)) Правда жаль что английский, но нам программистам не привыкать :)

Ну а на счет оптимизации оно то конечно да, но такие банальные вещи как вместо new Array() писать [] по-моему не навредит, а потому лучше знать побольше таких штук ;)

pSi_X
12 Мая 2011
— 21:58
#

@Platon, если честно, то я что-то давно уже не интересовался результатами Грузовичка в Вконтакте. Надо спросить ребят. Но судя по последнему нашему с ним общению, они жаловались что все достаточно плохо из-за затянутых Вконтактом гаеек.

Ant.Karlov
14 Мая 2011
— 02:39
#

Хочу посоветовать книгу "Flash Game Development by Examle" от E. Feronato, довольно известного флэш-гуру. Полностью на английском, но море полезной информации. [link]http://www.emanueleferonato.com/2011/03/24/flash-game-development-by-example-my-book-is-on-the-shelves/[link]

Kpanic
18 Мая 2011
— 15:54
#

Антон, извините, что вопрос не по теме. А как в ваших проектах реализованы цифры и текст?Например, цифры таймера или подсчета каких либо очков. Это специально подобранный шрифт или каждая цифра отрисована отдельной картинкой, а число разбивается на разряды и выводится соответствующий этому разряду спрайт?

Алексей
24 Мая 2011
— 05:33
#

@Алексей, в своих играх я использую обычные шрифты которые устанавливаются в систему. Единственное что все эти шрифты изначально разработанны как пиксельные поэтому при увеличении они сохраняют свои неровные квадратные формы. В самих играх для отображения текстов использую обычный TextField, а шрифт, пропорции и т.п. обычно настраиваю через панель настройки шрифтов для текстового поля, там же включаю опцию embed для шрифтов. В общем ничего особенного нет :)

Ant.Karlov
24 Мая 2011
— 10:31
#

Спасибо за ответ! Значит проще найти стилизованные шрифты.

Алексей
24 Мая 2011
— 12:09
#

Антон, хочу сказать спасибо за прекрасную серию уроков. Благодаря вам я сделал первые шаги в разработке флеш-игр (чему очень рад :) и выпустил свою первую игру.

Кстати, интересно узнать ваше мнение о ней.
?гра получила от редакторов fgl семерку, не знаю, смогу ли я ее продать с такой оценкой.
Если вы не против, то я могу оставить здесь ссылку на нее.

ZloyMedved
27 Мая 2011
— 21:36
#

Спасибо за урок, проглотил с большим удовольствием, как всегда все на высоте )

Marik
2 Июля 2011
— 13:16
#

Добрый день всем, заметил такую штуку, если оставить конструктор класса Level1 пустым то при нажатии на кнопки "Game" или "Editor" появляются ошибки. Так и должно быть или я чтото намудрил не то ?

Marik
5 Июля 2011
— 12:25
#

А раз уж кэш напрямую связан с ОбжектКонтроллер, то можно их объединить в один класс? Например какой-нить GameArray. Поучится массив с реализованым внутри него кэшем.
Правда получится что разные виды врагов будут лежать в разных массивах, каждый тип юнита в своем GameArray. Хотя это можно поправить указав нем ссылку на некий общий массив куда этот GameArray должен выделять или извлекать объекты . А уже объекты этого общего массива с пушками, врагами и прочим обновлять в событии кадра.

Massagames
18 Августа 2011
— 11:19
#

Функция set в simpleCache:
public function set(instance:Object):void
{
_currentIndex++;
if (_currentIndex == _instances.length)
{
_instances[_instances.length] = instance;
} else {
_instances[_currentIndex] = instance;
}
}


Не понимаю зачем сдесь условие? Ведь _currentIndex == _instances.length , а значит если условие верно, то без разницы как написать:
_instances[_instances.length] = instance; или
_instances[[_currentIndex] = instance; .т.е. в обоих случаях произойдет обращение к одному и томуже элементу
?ли это я заглючил и не вижу чего-то под носом ...

Massagames
18 Августа 2011
— 11:45
#

@Massagames, вы наверное не поняли принцип работы ObjectController и SimpleCache. Первый обрабатывает все живые объекты в игре, второй является хранилищем свободных и не используемых объектов в текущий момент времни. Объединить их можно, но это усложнит вам жизнь. ООП за то чтобы разделять сложные объекты на отдельные составляющие для упрощения, понимания и поддержки кода в будущем.

? да, вы скорее всего правы, условие на проверку текущего индекса с общим количеством объектов в кэше наверное не обязательно.

Ant.Karlov
19 Августа 2011
— 11:36
#

Тем не менее я ради эксперимента объединил их, создал класс GArray. В качестве параметра он принимает класс содержащихся в нем объектов, их стартовое количество и массив живых объектов,куда из кэша нужно выделять объекты objarray.
var cacheTower:GArray = new GArray(Tower, 10, objarray);
и теперь создание нового объекта выглядит так:
var tower:Object = cacheTower.add();
tower.init(...);

а удаление в кэш, так:

cacheTower.remove(tower);

Может по принципам ООП это и не верный подход, но тоже как вариант. Правда обрабатывать массив всеравно как-то нужно, поэтому может и зря я упрознил objectController :) он ведь как раз занимался update`ом.
Вобщим эксперимент удался, но я особо ничего не выгадал кроме небольшой краткости кода создания кеша и выделения из него объектов.

Massagames
22 Августа 2011
— 12:11
#

А также проверил функцию set в simpleCache, я убрал условие и все работало как и прежде, поэтому думаю я был прав и можно смело удалить его.

Massagames
22 Августа 2011
— 12:14
#

@Massagames, самое важное что вы потеряли при объеденении класса SimpleCache и ObjectController — это гибкость которая теперь вам не позволит использовать класс кэширования объектов в других ваших играх без его предварительной адаптации. Ведь принцип ООП как раз заключается в том, что каждая деталь должна быть не зависимой и легко заменяемой в игре. Поэтому важно отделять мух от котлет чтобы при использовании котлеты в новом вашем проекте с ней не тащились и все мухи прилипшие к ней ;)

Ant.Karlov
22 Августа 2011
— 13:34
#

Универсальность пропала.

У меня, например, прежде чем удалиться некоторые объекты переходят в разные контроллеры (например, для отрисовки в разных слоях). А уже потом возвращаются в кэш.

Алексашка
22 Августа 2011
— 13:34
#

Антон, когда будет следующий урок по ТД?
Уже соскучились...

Ivo_Bobul
24 Августа 2011
— 21:23
#

@ Ivo_Bobul, новый урок по ТД будет следующей записью в блоге. Скорее всего на выходных.

Ant.Karlov
24 Августа 2011
— 21:46
#

Антон, блог классный, я в диком восторге. Но пришла в голову идея. А не хотите ли вы издать "бумажную" версию блога?! Книгу! С иллюстрациями, некоторый такой "как делать игры"? Думаю, книга бы разошлась как пирожки.

CJay
27 Сентября 2011
— 00:51
#

@CJay:
Материала будет маловато для книги, лучше букварь с картинками школоте подарите.

BuxomBerry
27 Сентября 2011
— 10:13
#

А то! Многие бы купили красочный глянцевый "Букварь разработчика игр" или "Букварь FLash-разработчика".
? ещё хотелось бы добавить... Кэширование объектов. Всё же кэширование - это использование чего-то заранее просчитанного, а в данном случае, наверное, стоит называть не кэширование, а пул объектов http://ru.wikipedia.org/wiki/Объектный_пул.
А букварь вышел бы классный!

CJay
27 Сентября 2011
— 23:32
#

Я тут почитал про оптимизацию AS, узнал что класс Vector с фиксированной длинной перебирается в два раза быстрее чем Array.

Т.е. _instances лучше делать вектором с фиксированной длинной.

Vector отличается от Arrray, тем, что у него все элементы одного типа, можно фиксировать длинну и не может быть промежуточных пустых(т.е. без значения или null) элементов.

Kickerua
12 Ноября 2011
— 11:47
#

Долго нажимал пробел,
в итоге противники начали двигаться не в нутри лабиринта, а сквозь него.

Что за дела?

Тилль
25 Января 2012
— 23:25
#

удивительное дело, еще ДО начала постройки график Profiler`a показывает прям таки горную гряду. Память постоянно резко варьируется от 6 до 16 мегабайт, после запуска горы становятся не такими крутыми, но выше) доходят до 25 мегов. А после пары минут игры, сотни запущенных и благополучно уничтоженных зомби неожиданно выравнивается.
Отчего так происходит, особенно в начале, когда никаких объектов и в помине нет?

Trololotron
30 Января 2012
— 15:07
#

Спасибо за уроки.

Кэш, по сути это объектный пул,
который осуществляет кэширование.

А обойма - это стек.

Fear_Factory
2 Апреля 2013
— 21:40
#