5 способов быстро улучшить игру на LibGDX


Звук, свет, Box2D, партиклы и скелетная анимация — простые инструменты, которые могут заметно улучшить визуал и ощущения от игры.
В предыдущей статье я писал про [5 вещей, которые не следует делать в LibGDX].
Поэтому логично продолжить тему и поговорить о вещах, которые, наоборот, стоит использовать при разработке 2D-игры на LibGDX.
Сразу оговорюсь: это не статья про архитектуру, паттерны проектирования, ECS, оптимизацию кода или правильную организацию проекта.
Здесь всё гораздо практичнее.
Я хочу поговорить о нескольких технических инструментах, которые при относительно небольших затратах времени способны заметно улучшить визуальное восприятие игры:
звук;
освещение;
физика;
партиклы;
скелетная анимация.
А в конце будет небольшой бонусный пункт про шейдеры.
Это именно мой практический опыт разработки 2D-игр на LibGDX, а не попытка составить универсальный список технологий для всех возможных игр.
Часть описанных здесь подходов прекрасно работает и в 3D, и в других движках и фреймворках. Но я делаю именно 2D-игры и именно на LibGDX, поэтому дальше буду рассматривать всё с этой точки зрения.
При этом я бы назвал эти инструменты одним из самых дешёвых способов улучшить игру.
Не в смысле денег.
В смысле соотношения:
сколько времени потратил → насколько лучше стала выглядеть и ощущаться игра.
Многие из описанных ниже вещей можно добавить буквально за несколько часов, а полученный эффект может быть заметнее, чем результат нескольких дней работы над какими-нибудь мелкими деталями интерфейса.
Но есть важное условие.
Если игра сама по себе скучная и в неё не хочется играть, то ни свет, ни партиклы, ни суперсовременная анимация её не спасут.
А вот если у вас уже есть нормальный геймплей и хочется сделать игру более живой, атмосферной и приятной — тогда совсем другое дело.
1. Звук
Начнём с самого очевидного.
Добавляйте звук.
Вообще, я не очень понимаю игры, в которых игрок что-то делает, а в ответ — тишина.
Нажал кнопку — тишина.
Ударил врага — тишина.
Подобрал предмет — тишина.
Открыл сундук — снова тишина.
При этом визуально всё может выглядеть вполне нормально.
Звук очень сильно влияет на восприятие происходящего на экране. Причём иногда игрок даже не осознаёт, насколько сильно.
Например, можно сделать две абсолютно одинаковые анимации удара.
В первой будет только анимация.
Во второй в нужный момент появится:
звук удара;
небольшой экранный эффект;
частицы;
возможно, вспышка.
И вторая версия почти наверняка будет ощущаться значительно «тяжелее».
Именно поэтому звук — один из самых дешёвых способов сделать действие в игре более убедительным.
Sound и Music
В LibGDX есть два основных класса для воспроизведения аудио:
Sound и Music.
И это не просто два разных названия одного и того же.
Sound
Sound предназначен для коротких звуковых эффектов:
удар;
выстрел;
прыжок;
шаг;
звук кнопки;
подбор предмета;
взрыв;
разбитое стекло.
Причём один и тот же Sound можно воспроизводить несколько раз одновременно.
Например:
Sound shot = Gdx.audio.newSound(Gdx.files.internal("shot.wav"));
shot.play();
Можно получить ID воспроизведения:
long id = shot.play();
и затем управлять конкретным экземпляром:
shot.setVolume(id, 0.5f);
shot.setPitch(id, 1.2f);
shot.setPan(id, -1.0f);
setPitch
Изменяет высоту тона и скорость воспроизведения.
shot.setPitch(id, 1.2f);1.0f— обычное звучание1.2f— звук выше и быстрее0.8f— ниже и медленнее2.0f— примерно в 2 раза выше/быстрее
Это очень полезно для игровых эффектов.
Например, вместо одного и того же звука удара:
long id = hitSound.play();
hitSound.setPitch(id, 0.9f + MathUtils.random(0.2f));Получишь случайное значение примерно 0.9–1.1, поэтому каждый удар будет звучать немного по-разному.
Это удобно, например, если у вас одновременно стреляют несколько NPC.
setPan
Это стереопозиция звука — левее или правее.
shot.setPan(id, -1.0f);Диапазон:
-1.0 0.0 +1.0
| | |
ЛЕВО ЦЕНТР ПРАВОНапример:
shot.setPan(id, -1.0f); // только/почти только слева
shot.setPan(id, 0.0f); // по центру
shot.setPan(id, 1.0f); // справаДля top-down игры это особенно интересно: можно рассчитывать pan относительно позиции игрока и источника звука.
Например, источник слева от игрока:
float dx = soundX - playerX;
float pan = MathUtils.clamp(dx / 500f, -1f, 1f);
long id = shot.play();
shot.setPan(id, pan);Тогда звук будет постепенно перемещаться:
источник далеко слева pan = -1
↓
источник слева pan = -0.5
↓
источник напротив pan = 0
↓
источник справа pan = 0.5
↓
источник далеко справа pan = 1Music
Music предназначен для длинных аудиотреков:
музыка уровня;
музыка меню;
фоновая музыка;
длинные ambient-звуки.
Например:
Music music = Gdx.audio.newMusic(
Gdx.files.internal("music.mp3")
);
music.setLooping(true);
music.setVolume(0.5f);
music.play();
И здесь есть важный практический момент.
Не стоит использовать Sound для длинной музыки.
Для коротких эффектов Sound отлично подходит, а длинные композиции следует воспроизводить через Music.
Это не просто вопрос архитектурной красоты: разные типы аудио в LibGDX рассчитаны на разные сценарии использования и имеют разные модели загрузки/воспроизведения.
Поэтому если у вас вдруг длинная композиция ведёт себя странно, обрывается или работает не так, как ожидается, первое, что стоит проверить, — правильно ли выбран тип аудио.
Не делайте звук раздражающим
Есть ещё одна проблема.
Если вы добавили звук на каждое действие, это ещё не значит, что игра стала лучше.
Если NPC делает:
шаг → шаг → шаг → шаг → шаг → шаг → шаг
и каждый шаг звучит одинаково и идеально синхронно, через пять минут игрок будет мечтать выключить звук.
Поэтому полезны:
несколько вариантов одного звука;
небольшое изменение pitch;
изменение громкости;
пространственное позиционирование;
разные звуки для разных материалов.
Например, удар по дереву и удар по металлу должны звучать по-разному.
То же самое касается оружия, шагов, UI и различных игровых событий.
А что с WebGL?
Здесь у LibGDX есть отдельный класс проблем.
При сборке под WebGL работа с аудио зависит уже не только от LibGDX, но и от возможностей браузера и используемого backend.
В частности, могут возникать ситуации, когда большое количество одновременно воспроизводимых звуков работает не так, как на desktop.
Например, часть параллельных эффектов может не проигрываться или система начинает агрессивно ограничивать количество одновременно звучащих источников.
Поэтому если игра должна работать в WebGL, я бы обязательно тестировал звук именно в браузере, а не только на desktop.
Иногда приходится:
уменьшать количество одновременно воспроизводимых эффектов;
объединять некоторые эффекты;
использовать более простой микшер;
ограничивать количество одновременно играющих
Sound;отдельно тестировать конкретный браузер.
Это особенно актуально для игр, где одновременно происходит много событий.
Вывод
Звук в игре должен быть.
Но он должен быть:
качественным;
уместным;
разнообразным;
не раздражающим.
И обязательно тестируйте его на каждой целевой платформе.
2. Свет
Вот это уже моя любимая тема.
Свет способен практически мгновенно изменить внешний вид 2D-игры.
Причём иногда вам даже не нужно добавлять новую графику.
Вы можете взять ту же самую сцену, добавить несколько источников света — и она начнёт выглядеть совершенно иначе.
Например:
тёмный подвал;
факел;
костёр;
фонарь;
фонарик;
фары автомобиля;
выстрел;
вспышка взрыва.
И всё это можно сделать без огромного количества новых текстур.
Для LibGDX существует несколько подходов к освещению. Один из известных вариантов для 2D — связка LibGDX с box2d и box2dlights.
И здесь появляются очень интересные возможности.
Точечный источник света
Самый очевидный вариант — свет, распространяющийся вокруг определённой точки.
Например:
факел;
костёр;
горящая бочка;
лампа;
фонарь на стене;
взрыв.
Условно:
\ | /
-- * --
/ | \
Где * — источник света.
Причём источник можно двигать вместе с объектом.
Например, персонаж несёт факел — источник света следует за персонажем.
Конус света
А вот здесь становится ещё интереснее.
Можно сделать источник света, ограниченный углом.
Получается примерно:
*
/ \
/ \
/ \
/ \
Это прекрасно подходит для:
фонарика;
фар автомобиля;
прожектора;
оружия;
полицейской мигалки;
света от направленного источника.
Причём такой конус можно вращать.
Например, персонаж поворачивает фонарик — и конус света поворачивается вместе с ним.
Для top-down игры это особенно эффектно.
Тени
А теперь самое интересное.
Свет без теней уже выглядит неплохо.
Но свет с динамическими тенями — совсем другой уровень.
Например, персонаж проходит рядом с факелом.
Источник света находится здесь:
*
|
|
PLAYER
|
|
А препятствие между источником и объектом перекрывает свет.
В результате появляется тень.
Именно здесь очень удобно использовать физическую геометрию.
Если вы используете box2d в качестве физического мира, геометрию препятствий можно использовать системой освещения для определения того, что блокирует свет.
Но важно понимать:
Box2D сам по себе не является системой освещения.
Обычно речь идёт о связке физического мира с библиотекой освещения, которая умеет использовать его геометрию для построения теней.
И это очень удобное сочетание.
Свет + Box2D + Particles
А теперь можно начать комбинировать всё вместе.
Например, горящая бочка:
Box2D — физическая геометрия;
свет — освещает окружающее пространство;
частицы — огонь;
частицы — дым;
звук — потрескивание огня.
И всё это уже начинает создавать ощущение живого мира.
Причём сама бочка может быть относительно простой картинкой.
Именно поэтому мне нравится свет.
Он способен компенсировать недостаток сложной графики.
Не заменить её полностью, конечно.
Но если у вас не AAA-графика, грамотное освещение способно очень сильно улучшить восприятие сцены.
Интересный пример — оружие
Допустим, у персонажа автомат.
Можно сделать вспышку при выстреле.
Можно добавить маленький источник света.
Можно сделать небольшой световой след.
А для пулемёта можно использовать очень узкий направленный источник или другой световой эффект, который быстро перемещается вместе с пулей.
Получается своеобразный эффект трассера.
А теперь добавим:
звук;
вспышку;
частицы;
дым;
отдачу;
свет.
И обычный выстрел превращается в достаточно впечатляющее действие.
Вывод
Свет — это вишенка на торте.
Он не сделает скучную игру интересной.
Но если игра уже хорошая, свет способен очень сильно поднять её визуальное качество.
Особенно хорошо он работает вместе с:
физикой;
частицами;
погодными эффектами;
анимацией;
звуком.
Если в вашей игре есть факелы, фонари, костры, взрывы, фары или оружие — я бы почти всегда рассматривал возможность добавить динамический свет.
3. Box2D
Теперь немного неожиданное место.
Box2D.
Даже если ваша игра вообще не похожа на физический симулятор, Box2D всё равно может оказаться полезным.
Для очевидных жанров всё понятно.
Например:
Платформер
столкновения;
гравитация;
прыжки;
ящики;
падающие объекты.
Гонки
столкновения автомобилей;
физические объекты;
динамика движения;
препятствия.
Angry Birds-подобные игры
Тут Box2D вообще становится практически центральной частью игры.
Но интереснее случаи, когда физика не является частью геймплея.
Box2D как геометрия мира
Представим top-down игру.
У вас есть:
стены;
здания;
столы;
ящики;
двери;
препятствия.
Визуально это просто картинки.
Но физическому миру нужно знать:
где находится препятствие?
Для этого мы создаём физические тела и fixtures.
И теперь у нас появляется удобная геометрия мира.
Она может использоваться не только для столкновений.
Например, системой динамического освещения.
Если объект должен блокировать свет, его физическая геометрия может использоваться для построения теней.
И вот здесь Box2D становится полезен даже в игре, где игрок никогда не увидит «физику» как таковую.
Ещё одна причина использовать Box2D
Иногда разработчик думает:
«Да зачем мне Box2D? Я сам проверю пересечение двух объектов».
И действительно.
Для простой игры можно написать:
if (circle.overlaps(otherCircle)) {
...
}
И этого будет достаточно.
Но как только появляется:
много объектов;
сложная геометрия;
стены;
полигоны;
движущиеся объекты;
физические импульсы;
сенсоры;
разные типы столкновений;
самописная физика начинает быстро разрастаться.
В какой-то момент вы уже фактически пишете свой собственный Box2D.
И возникает вопрос:
а зачем?
Если вам действительно нужна физика — лучше использовать готовое решение.
Но Box2D не нужно использовать везде
Это тоже важно.
Если у вас RPG, где персонаж просто ходит по карте, вам вовсе не обязательно превращать каждого NPC в полноценное физическое тело.
Иногда обычных математических проверок вполне достаточно.
Box2D — это инструмент.
Не религия.
Используйте его там, где он действительно упрощает задачу.
Вывод
Если у вас:
платформер;
гонки;
физические головоломки;
большое количество физических объектов;
Box2D практически напрашивается сам.
Но даже в обычной 2D-игре он может быть полезен как удобное описание физической геометрии мира, особенно если вы планируете использовать динамическое освещение и тени.
4. Партиклы
А вот теперь мой любимый пункт.
Партиклы.
Если вы никогда не работали с системой частиц, обязательно попробуйте.
Партикл — это небольшая частица, которая живёт некоторое время и имеет определённые свойства:
позицию;
скорость;
направление;
размер;
прозрачность;
время жизни;
вращение;
цвет;
масштаб.
Из сотен или тысяч таких частиц можно собрать визуальный эффект.
Например:
огонь;
дым;
искры;
снег;
дождь;
пыль;
кровь;
магию;
взрыв;
туман;
пепел;
след от пули.
И именно здесь партиклы становятся невероятно мощным инструментом.
Почему партиклы мне нравятся больше покадровой анимации
Допустим, вам нужен огонь.
Можно сделать анимацию:
fire1.png
fire2.png
fire3.png
fire4.png
fire5.png
...
И последовательно переключать кадры.
Работает.
Но у такого подхода есть проблема.
Анимация всегда будет одинаковой.
Один и тот же огонь:
1 → 2 → 3 → 4 → 5
Партиклы работают совершенно иначе.
Каждая частица может иметь немного:
другую скорость;
другое положение;
другой размер;
другое время жизни;
другой угол;
другую прозрачность.
Поэтому даже при использовании одной и той же текстуры можно получить эффект, который выглядит гораздо более живым.
Что я делал с помощью партиклов
Практически всё.
Например:
Огнемёт
Поток огня можно собрать из большого количества частиц.
Горящий меч босса
На меч можно повесить систему частиц.
Получается:
/\
/ \ ← огонь
/____\
|
|
рука
И при движении персонажа огонь продолжает двигаться вместе с мечом.
Бочки
На бочку можно одновременно добавить:
огонь;
дым;
искры.
Взрыв
Несколько систем частиц:
вспышка;
огонь;
дым;
искры;
осколки.
Попадание
Можно добавить:
искры;
кровь;
пыль;
небольшую вспышку.
Погода
А вообще партиклы отлично подходят для:
дождя;
снега;
пепла;
пыли;
листьев.
И тут особенно хорошо видно преимущество частиц.
Не нужно рисовать тысячу кадров анимации дождя.
Вы создаёте частицы, которые падают сверху вниз.
Важный момент: текстуры и TextureAtlas
А теперь то, на чём я однажды сам споткнулся.
Если в вашей игре используется TextureAtlas, не стоит бездумно держать отдельные текстуры для частиц и постоянно переключаться между атласами.
Почему?
Потому что частиц может быть очень много.
Допустим, на экране одновременно находятся:
300 частиц огня;
200 частиц дыма;
100 искр.
Если рендер организован плохо, можно получить большое количество переключений текстур и соответствующих draw calls.
А это уже может начать влиять на производительность.
Поэтому имеет смысл организовать текстуры эффектов так, чтобы система частиц могла работать с текстурами из общего TextureAtlas.
Например, вместо того чтобы держать отдельные текстуры:
game.png
characters.png
effects.png
particles.png
...
и постоянно переключаться между ними во время отрисовки, можно добавить текстуры частиц в тот же атлас, который используется для остальных игровых объектов.
Это особенно важно, если на экране одновременно находятся сотни частиц. Каждая частица сама по себе очень простая, но большое количество частиц означает большое количество операций отрисовки. Лишние переключения текстур могут заметно увеличить нагрузку на GPU и особенно чувствоваться на мобильных устройствах и в WebGL.
Поэтому если вы активно используете частицы, имеет смысл заранее продумать, как они будут организованы в TextureAtlas, и по возможности не заставлять рендер постоянно переключаться между разными текстурами.
Особенно это важно на мобильных устройствах и WebGL.
Ещё одна проблема — blending
С партиклами есть ещё один интересный момент — blending, то есть способ смешивания текстуры частицы с уже нарисованным изображением.
Например, обычный дым обычно использует альфа-смешивание.
А искры или магическое свечение могут хорошо смотреться с additive blending.
Условно:
batch.setBlendFunction(
GL20.GL_SRC_ALPHA,
GL20.GL_ONE
);
Тогда светлая частица не просто перекрывает фон, а как бы добавляет к нему свет.
И именно поэтому можно получить красивое свечение.
Но тут легко перестараться.
Если оставить неправильный режим blending, частицы могут:
выглядеть слишком яркими;
иметь чёрный квадрат вокруг текстуры;
неправильно смешиваться;
давать странные края;
ломать внешний вид других объектов.
Поэтому если партиклы внезапно выглядят как набор светящихся квадратов — первым делом стоит посмотреть на blending и альфа-канал текстур.
У партиклов есть ещё один враг — overdraw.
Частица может быть маленькой, но если её текстура представляет собой большой прозрачный квадрат, GPU всё равно приходится обрабатывать этот квадрат. А когда таких частиц сотни или тысячи, стоимость начинает складываться. Поэтому для частиц желательно использовать небольшие текстуры и не оставлять вокруг эффекта огромные прозрачные области.
И снова WebGL
WebGL здесь тоже может внести свои коррективы.
На desktop вы можете совершенно спокойно вывести огромное количество частиц и сказать:
«Работает же».
А потом открыть WebGL на телефоне.
И получить:
«Почему 60 FPS превратились в 20?»
Поэтому количество частиц, размер текстур, количество draw calls и режимы blending всё равно нужно контролировать.
Партиклы очень дешёвые по сравнению с полноценными анимациями во многих сценариях, но это не означает, что количество частиц бесконечно.
Вывод
Мой совет здесь очень простой:
Если эффект можно сделать партиклами — попробуйте сделать его партиклами.
Огонь?
Партиклы.
Дым?
Партиклы.
Искры?
Дождь?
Взрыв?
Магия?
Всё партиклы.
Конечно, не буквально всегда.
Но это один из тех инструментов, которые дают очень много визуального результата за относительно небольшое количество работы.
5. Скелетная анимация
И вот здесь я, пожалуй, могу сформулировать своё мнение наиболее категорично:
После знакомства со скелетной анимацией мне сложно даже думать о покадровой. Зачем покадровая, если есть скелетная.
Я использовал Spine и именно на нём получил свой основной опыт работы со скелетной анимацией.
Поэтому дальше я буду говорить именно о Spine.
Но сам принцип относится не только к нему.
Как работает покадровая анимация
В классическом варианте у нас есть последовательность изображений:
player_01.png
player_02.png
player_03.png
player_04.png
player_05.png
...
И мы просто показываем их последовательно.
Это работает.
И иногда это именно то, что нужно.
Но у такого подхода есть очевидный недостаток:
каждое новое движение — это новые изображения.
Хотите добавить новую атаку?
Нужно нарисовать кадры.
Хотите новый вариант ходьбы?
Опять кадры.
Хотите новый костюм?
В зависимости от реализации снова потребуется значительный объём графики.
Скелетная анимация
В скелетной анимации персонаж состоит из:
костей;
слотов;
изображений;
анимаций.
Условно:
голова
|
тело
/ \
рука рука
| |
оружие кисть
Анимируется не вся картинка целиком, а положение костей.
И это открывает огромное количество возможностей.
Размер
Не обязательно хранить десятки больших полноценных изображений персонажа на каждый кадр.
Вместо этого можно использовать набор частей персонажа и хранить данные о движении костей.
В результате размер ресурсов может быть значительно меньше, особенно если персонаж имеет большое количество анимаций.
Конечно, конкретный выигрыш зависит от персонажа и способа подготовки ресурсов, поэтому говорить, что скелетная анимация всегда меньше покадровой, было бы неправильно.
Но для персонажей с большим количеством анимаций это очень интересный вариант.
Новые анимации
Очень удобно то, что графика персонажа и его движения разделены.
Например, у нас есть:
idle;
walk;
run;
attack;
hit;
death.
А затем мы добавляем:
другой костюм;
другой цвет;
другой меч;
другой шлем.
Не обязательно рисовать весь набор кадров заново.
Можно использовать те же анимации с другим набором визуальных элементов.
Скины
Это одна из моих любимых возможностей.
Один и тот же скелет может использовать разные скины.
Например:
Warrior
Mage
Knight
Skeleton
Zombie
При этом структура анимации может оставаться общей.
Для RPG это особенно удобно.
У вас может быть одна система:
idle
walk
run
attack
hit
death
а визуально персонажи могут выглядеть совершенно по-разному.
События анимации
А вот здесь начинается самое интересное.
Допустим, персонаж замахивается мечом.
В определённый момент меч должен попасть по врагу.
Можно, конечно, писать:
if (animationTime > 0.37f) {
attack();
}
А потом менять длительность анимации.
И снова менять число.
И снова проверять.
И снова ловить рассинхронизацию.
В Spine можно использовать события анимации.
Например:
0.00 ───── начало атаки
0.25 ───── замах
0.42 ───── HIT
0.70 ───── конец атаки
На событие HIT код может отреагировать:
if (event.getData().getName().equals("HIT")) {
attackTarget();
}
И в этот же момент можно:
нанести урон;
проиграть звук;
создать частицы;
показать вспышку;
создать эффект попадания.
То есть сама анимация становится источником игровых событий.
И это очень удобно.
Эффект на кости
А теперь ещё интереснее.
Представим меч.
Меч — это часть персонажа, которая связана с определённой костью.
На эту же кость можно ориентировать или привязать дополнительный визуальный эффект.
Например:
огонь;
свечение;
искры;
дым;
магический след.
Персонаж замахивается.
Меч движется.
И эффект автоматически движется вместе с мечом.
Не нужно вручную каждый кадр вычислять:
effect.x = ...
effect.y = ...
effect.rotation = ...
Сама анимационная система уже знает, где находится нужная кость.
Для оружия это особенно удобно.
А что кроме Spine?
Здесь я специально не хочу говорить:
«Spine — единственный нормальный вариант».
Конечно, нет.
Есть и другие инструменты для скелетной анимации.
Просто я использовал Spine, поэтому могу говорить именно о своём опыте с ним.
Для меня главное преимущество самой скелетной анимации перед покадровой заключается в том, что она лучше подходит для персонажей, которых нужно много раз переиспользовать и комбинировать:
разные анимации;
разные скины;
оборудование;
оружие;
события;
эффекты.
Вывод
Если у вас игра, где есть персонажи с большим количеством движений, я бы обязательно посмотрел в сторону скелетной анимации.
Особенно если это:
RPG;
action;
roguelike;
стратегия с персонажами;
MMO;
файтинг.
Для простого маленького персонажа с двумя анимациями она может быть избыточной.
Но чем сложнее становится персонаж — тем интереснее становятся преимущества этого подхода.
6. Бонус — шейдеры
А вот здесь я должен признаться.
Я не использовал шейдеры в своих LibGDX-проектах.
Не потому что они плохие.
Просто до этого мне не требовалось.
Поэтому я не буду делать вид, что сейчас расскажу вам, почему шейдеры — величайшая технология в истории компьютерной графики.
Мне самому пока интереснее спросить об этом у вас.
:)
Я понимаю в общих чертах, что шейдеры позволяют выполнять собственные операции непосредственно на GPU и дают гораздо больше контроля над тем, как именно отрисовывается изображение.
Но вот вопрос:
в каких ситуациях в 2D-игре на LibGDX шейдер действительно лучше партикла, анимации или обычного эффекта?
Например:
вода;
искажение изображения;
heat distortion;
glow;
outline;
grayscale;
damage effect;
dissolve;
освещение;
постобработка.
Где проходит граница?
Когда проще сделать эффект партиклами?
Когда лучше использовать шейдер?
Когда шейдер даёт существенный выигрыш по производительности?
Вот это мне самому было бы интересно узнать от людей, которые реально используют их в своих проектах.
Поэтому считайте это моим домашним заданием.
Если вы хорошо разбираетесь в шейдерах LibGDX — расскажите в комментариях, какие эффекты вы используете и почему сделали их именно шейдерами.
Мне действительно интересно.
Заодно проверим, кто вообще дочитал статью до этого пункта. :)
А теперь самое интересное — комбинируем всё вместе
На самом деле сила всех этих технологий проявляется не тогда, когда мы используем их по отдельности.
А когда начинаем их комбинировать.
Возьмём обычную машину в top-down игре.
Без эффектов:
машина
Неплохо.
Теперь добавим звук двигателя.
Добавим фары, теперь машина освещает дорогу.
Добавим дым из выхлопной трубы, добавим частицы при столкновении.
Добавим физические столкновения:
Box2D + свет + частицы + звук
И внезапно та же самая машина начинает восприниматься совершенно иначе.
Причём мы практически не добавили новой игровой механики.
Мы просто сделали визуальную и звуковую обратную связь.
То же самое с RPG
Допустим, у нас средневековая RPG.
Персонаж подходит к факелу.
Что можно сделать?
Только графика
🧍 🔥
Добавляем свет
Факел освещает персонажа и окружающую стену.
Добавляем частицы
Появляется анимация огня и искр.
Добавляем звук
Появляется звук горения.
Добавляем скелетную анимацию
Персонаж естественно проходит рядом с факелом, а оружие и оборудование двигаются вместе с ним.
И вот уже простая картинка превращается в сцену, которая ощущается гораздо живее.
Но есть важное «но»
Все эти технологии не делают игру хорошей автоматически.
Можно сделать:
великолепный свет;
тысячу частиц;
идеальную скелетную анимацию;
пространственный звук;
физику;
шейдеры.
И получить очень красивую игру, в которую никто не хочет играть.
Поэтому я бы разделял две вещи:
Геймплей
Почему игроку интересно играть?
Presentation
Почему происходящее приятно видеть и слышать?
Эта статья исключительно про второе.
Если у вас уже есть интересный игровой процесс, тогда эти технологии могут дать очень большой прирост качества.
Если же геймплея нет — не нужно тратить неделю на настройку красивого дыма из трубы.
Если делать гонки — делайте фары, дым и звук
Допустим, вы делаете гонки.
И думаете:
«Нужны ли мне вообще фары?»
Да.
Если игра происходит ночью — это естественная часть визуального языка игры.
Нужен ли дым?
Да.
Из выхлопной трубы.
При пробуксовке.
После повреждения.
При столкновении.
Нужны ли частицы?
Да.
Искры при столкновениях.
Осколки.
Пыль.
Дым.
Нужен ли звук?
Конечно.
Двигатель.
Тормоза.
Столкновения.
Шины.
Переключение передач.
Если делать шутер
Можно сделать фонарик.
И свет от него.
И тени.
И вспышку выстрела.
И дым.
И искры.
И звук.
И небольшой эффект отдачи.
Каждый из этих элементов сам по себе не является чем-то невероятным.
Но вместе они создают совершенно другое ощущение от выстрела.
Если делать средневековую RPG
Раздайте всем факелы.
:)
Серьёзно.
Огонь — один из тех эффектов, который очень хорошо подходит для средневекового сеттинга.
Факел:
источник света;
тень;
частицы огня;
дым;
звук.
И один объект сразу использует несколько технологий из этой статьи.
И самое главное — это недорого по времени
Именно поэтому мне нравятся все эти инструменты.
В большинстве случаев вам не нужно писать огромную систему.
Можно начать с очень простого варианта.
Например:
Свет
Поставили один источник → посмотрели → настроили.
Партиклы
Сделали огонь → поменяли размер → скорость → lifetime → готово.
Звук
Подключили Sound → проиграли → настроили громкость.
Скелетная анимация
Подключили персонажа → проиграли готовую анимацию → добавили событие.
То есть это не обязательно месяцы работы.
Иногда несколько часов дают достаточно заметный результат.
И именно поэтому я считаю такие технологии очень хорошим вложением времени для небольшого инди-проекта.
Итог
Если у вас уже есть игра, в которую интересно играть, я бы рекомендовал хотя бы попробовать:
Звук — чтобы действия ощущались.
Свет — чтобы создать атмосферу.
Box2D — когда нужна физика или удобная физическая геометрия.
Партиклы — чтобы оживить эффекты.
Скелетную анимацию — если у вас много персонажей и анимаций.
Шейдеры — если вы понимаете, зачем они вам нужны. :)
Причём самое интересное начинается тогда, когда эти технологии используются вместе.
Например:
удар мечом → событие скелетной анимации → звук → вспышка света → частицы искр → физическое столкновение.
И всё это может происходить за долю секунды.
Игрок не думает:
«О, автор использовал Box2D, ParticleEffect и Spine».
Он просто чувствует:
«О, этот удар ощущается хорошо».
И, наверное, именно это и является главной целью всех этих технологий.
Не показать игроку, сколько технологий вы использовали.
А сделать так, чтобы игра лучше ощущалась.
Если вы планируете делать игру и у вас уже есть интересный геймплей — попробуйте добавить хотя бы несколько описанных здесь вещей.
Особенно если они естественно подходят вашему жанру.
Гонки → фары, звук, дым, частицы.
Шутер → свет, тени, звук, вспышки, частицы.
RPG → скелетная анимация, свет, огонь, частицы, звук.
Платформер → физика, частицы, звук.
И так далее.
Не обязательно делать всё сразу.
Просто каждый раз, когда возникает вопрос:
«А стоит ли добавлять сюда ещё один эффект?»
Попробуйте добавить.
Иногда одна маленькая вспышка, звук или несколько частиц дают больше визуального результата, чем несколько часов работы над новой графикой.
Делайте хорошие игры. Используйте хорошие технологии. И не забывайте, что технология — это всё-таки инструмент, а не сама игра.
P.S.
А теперь мне действительно интересно узнать мнение читателей.
Про что написать следующую статью?
Можно пойти глубже в одну конкретную тему.
Например:
подробно разобрать освещение в LibGDX;
показать подключение динамического света;
разобраться с тенями;
сделать фонарик;
сделать фары автомобиля;
разобрать ParticleEffect;
показать оптимизацию большого количества частиц;
подробно разобрать Spine + LibGDX;
поговорить про WebGL и ограничения LibGDX;
или вообще не уходить глубоко в технические детали и продолжить рассказывать про практический опыт разработки.
Напишите в комментариях, что вам было бы интереснее.
Если эта публикация вас вдохновила и вы хотите поддержать автора — не стесняйтесь нажать на кнопку
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.