Найти тему
Сделай игру

Игра: проблема синхронизации действий

Оглавление

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

Одной из важнейших проблем - становится вопрос синхронизации действий и изображения на экране. Итак, у нас есть персонаж, который должен действовать и действия, которые должны выполняться. Но довольно быстро возникает вопрос: а как всё это синхронизировать.

Синхронизация, которую мы заслужили
Синхронизация, которую мы заслужили

Первичный подход

Он не требует синхронизации как таковой: наш персонаж моментально реагирует на управление и не снабжён никакими анимациями, либо его анимации никак не связаны с управлением. Проще говоря, управление персонажем попросту меняет изображение внешнего вида: идёт направо - отображается правый спрайт, налево - левый.

Подход продвинутый

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

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

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

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

Подход совершенный

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

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

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

Реализация

И вот, мы переходим к самому актуальному: а как всё это сделать. Собственно, данную задачу я решаю в настоящий момент (ну как "решаю" - почти решил, скорее оформляю результаты, попутно фиксируя неточности).

Есть список правил, которые надо усвоить:

  1. Состояние персонажа не должно быть логического вида, вроде, да или нет: очень часто, оно должно разделяться на несколько составляющих (например: инициация атаки, замах (0..100%), удар; однако это совсем не обязательно);
  2. Есть состояния, которые могут быть прерваны, а есть, которые нет (например, если герой просто движется, то пропущенный удар отталкивает его, прерывая движение, а если сам атакует и пропускает удар - то состояние не может быть сброшено);
  3. Переходы состояния всегда привязываются к некоторому внутреннему времени игры (например, состояние прогрессирует каждые 50 миллисекунд до тех пор, пока не будет полностью выполнено; после завершения переходит в режим - ожидание);
  4. Пока состояние не перетекло из режима "ожидание" в режим "выполнено", факт действия не считается применённым.

Зачем все эти сложности

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

Приведу простой пример: пошаговая стратегия: за героя играет пользователь, за злодея - компьютер. Герой стреляет во врага ракетой, которая летит 1 секунду (красивая анимация) и должна попасть точно в него. Но, после выстрела, управление передаётся злодею, который смещается со своей позиции на другую и ракета не попадает. Однако, если поменяться местами, герой не сможет сдвинуться с места, пока анимация полёта ракеты не закончится.

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

Выводы

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