Механизм обратного времени в играх


10

Мне интересно, как обычно создаются механизмы манипуляции временем в играх. Я особенно заинтересован в обращении времени (вроде как в последнем SSX или Prince of Persia).

Игра представляет собой 2D шутер сверху вниз.

Механизм, который я пытаюсь разработать / реализовать, имеет следующие требования:

1) Действия сущностей, кроме персонажа игрока, являются полностью детерминированными.

  • Действие, которое предпринимает объект, основано на прогрессирующих кадрах с момента начала уровня и / или позиции игрока на экране.
  • Объекты создаются в установленное время на уровне.

2) Обратное время работает, возвращаясь обратно в реальном времени.

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

Например:

Кадры 0-50: в течение этого времени игрок перемещается на 20 единиц вперед в течение 20 кадра. Враг 1 перемещается влево на 10 единиц в течение кадра 30-40. Игрок стреляет пулей в кадре 45. Пуля движется на 5 вперед (45-50) и убивает врага 1 в кадр 50

Изменение этого положения приведет к воспроизведению в реальном времени: в течение этого времени игрок перемещается назад на 20 единиц. Враг 1 возрождается в кадре 50. Пуля возвращается в кадре 50. Пуля движется назад 5 и исчезает (50-45) Враг перемещается влево 10 (40-30) Враг удаляется в кадр 20.

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

void update()
{
    movement += new Vector(0,5);
}

Я бы сделал что-то вроде этого:

public interface movement()
{
    public void move(Vector v, Entity e);
}

public class advance() implements movement
{
    public void move(Vector v, Entity e)
    {
            e.location += v;
    }
}


public class reverse() implements movement
{
    public void move(Vector v, Entity e)
    { 
        e.location -= v;
    }
}

public void update()
{
    moveLogic.move(new vector(5,0));
}

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


1
Я не смотрел все это (шаткая видеокамера на YouTube, 1,5 часа) , но, возможно, есть некоторые идеи, как Джонатан Блоу работал над этим в своей игре Braid.
MichaelHouse

Возможный дубликат gamedev.stackexchange.com/questions/15251/...
Hackworth

Ответы:


9

Возможно, вы захотите взглянуть на шаблон команды .

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

Всякий раз, когда ваша сущность выполняет обратимое действие, вы сначала создаете соответствующий объект команды. Вы сохраняете его в стеке Undo, затем добавляете его в игровой движок и запускаете его. Когда вы хотите изменить время, вы выталкиваете действия из верхней части стека и вызываете их метод Undo (), который делает противоположное методу Execute (). Например, в случае прыжка из точки A в точку B вы выполняете переход с B на A.

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

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

У шаблона команд есть и другие преимущества: например, создание рекордера воспроизведения довольно просто, так как вам просто нужно сохранить все объекты из стеков в файл, а во время воспроизведения просто подать его в игровой движок один за другим. один.


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

@StevenStadnicki Возможно, но это определенно возможно. Сверх того, C & C Generals делает это таким образом. Он имеет многочасовые повторы до 8 игроков, в худшем случае с весами в несколько сотен килобайт, и, как мне кажется, большинство, если не все игры RTS делают их многопользовательскими: вы просто не можете передать полное состояние игры с потенциально сотнями единиц в каждом кадре, вы должны позволить двигателю делать обновления. Так что да, это определенно жизнеспособно.
Хакворт

3
Воспроизведение - это совсем не то, что перемотка, потому что операции, которые последовательно воспроизводимы вперед (например, поиск позиции в кадре n, x_n, начиная с x_0 = 0 и добавление дельты v_n для каждого шага), не обязательно воспроизводимы в обратном направлении. ; (x + v_n) -v_n не всегда равен x в математике с плавающей точкой. И легко сказать «обойти это», но вы говорите о потенциально полной перестройке, включая невозможность использования многих внешних библиотек.
Стивен Стадницкий

1
Для некоторых игр вашего подход может быть возможным, но AFAIK большинство игр , которые используют обращения времени механика использует что - то ближе к Memento подходу OriginalDaemon, где соответствующее состояние будет сохранено для каждого кадра.
Стивен Стадницкий

2
А как насчет перемотки путем пересчета шагов, но сохранения ключевого кадра каждые пару секунд? Ошибки с плавающей запятой вряд ли будут иметь существенное значение всего за несколько секунд (конечно, в зависимости от сложности). Также показано, что он работает в режиме сжатия видео: P
Tharwen

1

Вы могли бы взглянуть на Шаблон Memento; Его основное намерение - реализовать операции отмены / возврата путем отката состояния объекта, но для некоторых видов игр этого должно быть достаточно.

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


0

В вашем конкретном случае обработка отката путем перемотки движения должна работать нормально. Если вы используете какую-либо форму поиска пути с единицами ИИ, просто обязательно пересчитайте ее после отката, чтобы избежать перекрытия юнитов.

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


0

Пока это интересная идея. Я бы посоветовал против этого.

Воспроизведение форвардов игры работает нормально, потому что операция всегда будет одинаково влиять на состояние игры. Это не означает, что обратная операция дает вам исходное состояние. Например, оцените следующее выражение на любом языке программирования (отключите оптимизацию)

(1.1 + 3 - 3) == 1.1

По крайней мере, в C и C ++ он возвращает false. Хотя разница может быть небольшой, представьте, сколько ошибок может накапливаться при 60 кадрах в секунду за 10 секунд секунд. Будут случаи, когда игрок только что-то пропускает, но поражает его, пока игра переигрывается задом наперед.

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

Если ваша игра не слишком сложна, просто сохраняйте ключевые кадры состояния игры 30 раз в секунду и играйте в обратном направлении. Если бы у вас было 15 объектов с 2D-позицией, потребуется 1,5 минуты, чтобы получить МБ без сжатия. Компьютеры имеют гигабайты памяти.

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.