среда, 2 января 2013 г.

Заканчиваем исследование партикл-систем.

Думаю, это последний пост про партикл-системы. Я нашел несколько интересных штук в движке и хотел бы ими с вами поделиться. В посте 3 основных вопроса:
1. Как улучшить, сделать красивее свою систему.
2. Что такое BatchedSpriteParticleSystem и стоит ли ее использовать.
3. Какие бывают эмиттеры.

Бонусом выкладываю вам для всеобщего использования файлик с частицами, которые я использую. Он не мой - он из движка HGE. Первая картинка - просто для удобства - она Jpeg. Качайте вторую себе - она PNG. Я везде пока использую третью слева в третьей строчке.




Ну во-первых оказалось, что системе частиц можно добавлять несколько однотипных модификаторов, если в этих модификаторах есть время начала и конца модификации. Проще пояснить на примере.
mPS.addParticleModifier(new ColorParticleModifier(0.0f, 0.1f, 1.0f, 1.0f, 1.0f, 0.7f, 1.0f, 0.0f));
mPS.addParticleModifier(new ColorParticleModifier(0.1f, 0.4f, 1.0f, 1.0f, 0.7f, 0.0f, 0.0f, 0.0f));
Опять прошу прощения за последнюю строчку - это глюк блога.
Вот мы видим 2 одинаковых модификатора. Первый параметр - время начала действия, второй - окончания. То есть второй модификатор начинает действовать в тот же момент, в который первый модификатор заканчивает свое действие.

Вот полный список модификаторов для партикл-системы двигателя корабля.
mPS.addParticleInitializer(new VelocityParticleInitializer(-20.0f, 20.0f, -10.0f, 300.0f));
mPS.addParticleInitializer(new ExpireParticleInitializer(0.3f, 0.8f));
mPS.addParticleInitializer(new BlendFunctionParticleInitializer(GLES20.GL_SRC_ALPHA,GLES20.GL_ONE));
mPS.addParticleModifier(new ColorParticleModifier(0.0f, 0.1f, 1.0f, 1.0f, 1.0f, 0.7f, 1.0f, 0.0f));
mPS.addParticleModifier(new ColorParticleModifier(0.1f, 0.4f, 1.0f, 1.0f, 0.7f, 0.0f, 0.0f, 0.0f));
mPS.addParticleModifier(new OffCameraExpireParticleModifier(this.sapCamera));
mPS.addParticleModifier(new ScaleParticleModifier(0.0f, 0.1f, 0.3f, 1.0f, 0.5f, 1.0f));
mPS.addParticleModifier(new ScaleParticleModifier(0.1f, 0.4f, 1.0f, 0.1f, 1.0f, 1.0f));
mPS.addParticleModifier(new AlphaParticleModifier(0.3f, 0.8f, 1.0f, 0.0f));
А вот так выглядит обновленная партикл-система по сравнению со старым вариантом:
ТО есть в стартовой точке системы появилось яркое белое ядро. Цвет сначала меняется от белого до желтого, а затем уже от желтого до красного.
Однако стоит помнить - чем больше модификаторов, тем больше будет проседать ФПС. Не стоит злоупотреблять модификаторами :)

Дальше я бы хотел рассказать о своем исследовании BatchedSpriteParticleSystem
Нашел я эту штуку в движке и сразу подумал, что она сделана для улучшения производительности. Ведь SpriteBatch вроде бы для этого и служит (хотя я его еще подробно не исследовал). При исследовании выяснилось следующее. Во-первых, такая партикл-система совсем по-другому обрабатывает наложение спрайтов. Вот вам картинка для примера. (огонь не мой, а вот кораблик сверху мой - в плавильную систему частиц добавлено много частиц для наглядности).
Как видите, результат абсолютно разный. Я бы не сказал, что он хуже. В некоторых ситуациях может и понадобится батч-система. И это только во-первых.
Во-вторых, на практике никакого прибавления в производительности замечено не было. И ДАЖЕ, по-моему, производительность слегка просела. Хотя я не проводил тщательный тест производительности, а пользовался лишь парочкой относительных результатов. Если кто-то пойдет по моим стопам и захочет исследовать этот вопрос более тщательно - буду очень благодарен за информацию.

Ну и в конце я расскажу о найденным мною эмиттерах.
ИМХО, не очень удобно использовать конструкцию вроде этой:
class Hero extends BasicObject implements IParticleEmitter{
// ...some code...
  // ===========================================================
  // Methods for/from SuperClass/Interfaces
  // ===========================================================
   
  public void getPositionOffset(float[] pOffset) {
   pOffset[VERTEX_INDEX_X] = this.getX() + HERO_ENGINE_PARTICLES_OFFSET_X;
   pOffset[VERTEX_INDEX_Y] = this.getY() + HERO_ENGINE_PARTICLES_OFFSET_Y; 
  }
// ...some code...
}
Гораздо проще оказалось добавить некий уже готовый эмиттер, как поле, а не делать самого героя эмиттером. Вот примерно как это выглядит:
class Hero extends BasicObject {
// ...some code...
private PointParticleEmitter mPointParticleEmitter;
// ...some code...
Hero(/*...constructor parameters...*/){
// ...some code..     
 mPointParticleEmitter = new PointParticleEmitter
   (HERO_ENGINE_PARTICLES_OFFSET_X, HERO_ENGINE_PARTICLES_OFFSET_Y);
}  // ...some code...
Ну а дальше я пошел исследовать различные эмиттеры. По-сути, готовых к использованию оказалось 5 штук.
1. PointParticleEmitter создает все частицы в одной точке.
2. CircleParticleEmitter. Ему задается радиус по Х и по У, то есть он может быть эллиптическим. Частицы создаются по всей площади эллипса.
3. СircleOutlineParticleEmitter. Частицы создаются только на периметре эллипса. Думаю, такой эмиттер подойдет для всяких там гало и телепортов.
4. RectangleParticleEmitter. Частицы создаются по всей прямоугольной площади.
5. RectangleOutlineParticleEmitter. Частицы создаются по периметру прямоугольной формы.
Для наглядности вот скриншоты простого и круглых эмиттеров:
Вот, собственно, и все на сегодня.

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

7 комментариев:

  1. Ваши статьи супер ! Осваиваю движок и опираюсь на ваши статьи ! Продолжайте в том же духе !

    ОтветитьУдалить
    Ответы
    1. Спасибо. Приятно, блин :)
      Но появилось мнение, что БЛОГ не соответствует своему названию. Прочитайте первое сообщение и, возможно, оставьте комментарий :)

      Удалить
  2. А как избежать проседания FPS при добавлении Particle? Может как-то можно предзагрузить?

    ОтветитьУдалить
    Ответы
    1. Если честно, то хз. Пока не занимался этим вопросом.

      Один вариант решения предполагался такой - мониторить производительность и в зависимости от нее играться с количеством частиц прямо в коде. То есть на медленных девайсах будет автоматически выбираться минимум частиц. Чем сильнее девайс, тем больше частиц.

      Другой вариант мне напомнил один друг в скайпе. Вообще отказаться от частиц. Нарендерить спрайты взрывов в МАКСе или где там. И просто ставить AnimatedSprite со взрывом в нужном месте. Хотя такой подход требует тестирования. Потому как такие спрайты будут занимать много графического места. Предполагаю, что на 4-5 взрывов понадобится текстура 1024*1024 и это, опять же, просадит ФПС.

      Удалить
    2. И еще. Желательно минимизировать количество инициализаторов и модификаторов для каждой системы частиц. Можно делать мнеьше частиц, но каждую больше размером. А вообще крайне трудно поймать оптимальное соотношение между красотой и производительностью.

      Удалить
  3. День добрый. Буквально сегодня начал прочитывать ваш блог. До этого читал книгу по AndEngine на английском и , признаюсь, мозги сильно уставали. Хочу сказать вам большое спасибо за ваш труд. Со своими начальными знаниями в разработке игр, пока не столкнулся ни с какими проблема. Единственное , пока не знаю как сделать Background из повторяющихся текстур, был бы вам очень благодарен,если бы вы поделились.
    Спасибо за внимание

    ОтветитьУдалить
  4. Коли створюєш TextureAtlas потрібно використати як параметр TextureOptions.REPEATING_BILINEAR_PREMULTIPLYALPHA. Для прикладу :
    myTextureAtlas = new BitmapTextureAtlas(this.getTextureManager(), 32, 32, TextureOptions.REPEATING_BILINEAR_PREMULTIPLYALPHA);

    ОтветитьУдалить