Эта пост является продолжением статьи “ Отложенные вычисления ” и есть в некотором роде пример применения данного метода в реальном игровом проекте. 

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

Основной метод update

  • Обработка освещения
  • Обработка состояния объектов
  • Все остальные обработки

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

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

  1. обработка состояния внутреннего времени
  2. вычисление положения солнца
  3. расчет карты теней
  4. расчет карты освещения на основе зрения игрока
  5. выполнение событий, связанных с игровой картой (рост травы, разлив рек и т.д.)
  6. поиск пути А* алгоритмом для существ
  7. перемещение существ
  8. выполнение игровых эффектов (полет стрелы, взрыв и т.д.)
  9. перемещение игрока и выполнение связанных с ним событий

Возможно, это и не весь список, но это “скелет” игры. Давайте разберемся, какие из этих событий требуют мгновенного выполнения, а какие могут быть отложены на некоторое время?

Игровая модель требует обрабатывать состояние внутриигрового таймера одновременно с инкрементированием его состояния(1). Одновременно вычисляется положение солнца (2), как связанное событие, и рассчитывается новая карта теней (3), т.к. эти данные необходимы для немедленного вывода на экран (даже задержка в 40мс становится заметна для глаза и неприятна, если видится). По этим же соображениям требуются данные освещения игрока(4).

С другой стороны, обновление состояния объектов игровой карты (5) может занять слишком много времени, чтобы вызвать задержку. Шутка ли, если нужно выполнять события для 262144 (карта 512*512 тайлов) объектов в один момент времени? Именно в этом случае разработчик может “схитрить”, можно:

  • выполнять события только для тех тайлов, которые видны игроку, а про остальные забыть
  • или выполнять сейчас события для тех тайлов, которые видны игроку, а остальные выполнять при последующих вызовах метода Update (когда процессор будет свободен)
  • или выполнять обновление всех тайлов, когда процессор будет свободен (т.к. игрок точно не заметит, что тайл “пророс травой” на 40мс позже, чем он выполнил какое-то действие)

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

Выполнение игровых эффектов (8) и перемещение игрока (9) так же должны выполняться мгновенно, но все их последствия могут вычисляться с задержкой.