Русский
Русский
English
Статистика
Реклама

Перевод Дискретный арктангенс в процессоре NES


Геймплей моей игры Star Versus основан на поворотах. Объекты отслеживают направление, в котором они смотрят, и на каждом цикле движка двигаются в этом направлении. В такой ситуации требуется много тригонометрии, большую часть которой можно ради эффективности вычислить заранее, однако иногда это невозможно. В частности, паре геймплейных элементов нужно находить арктангенсы, и делать это быстро.


Определение арктангенса: в прямоугольном треугольнике arctan вычисляет один из непрямых углов, используя в качестве входных данных длину стороны, противоположной этому углу, разделённую на длину прилежащей стороны. В случае Star Versus сторонами треугольника являются расстояния X/Y между двумя объектами, например, снарядом и кораблём, а угол это направление, в котором должен двигаться первый, чтобы достичь второго.



Поначалу арктангенс нужен был для того, чтобы понять, когда один корабль ударяет по другому своим мечом. Мечи не имеют собственной информации о коллизиях, из-за необходимости анимаций они существуют исключительно как артефакты рендеринга. Поэтому они используют код, распознающий коллизии между двумя кораблями с увеличенным хитбоксом для учёта радиуса удара меча. Это позволяет движку экономить драгоценное время процессора, вычисляя дельты X/Y всего один раз. Однако для этого нужно гарантировать, что размахивающий мечом смотрит в нужном направлении, а не совершает удар в сторону от цели, и здесь нам пригождается арктангенс.

Реализация


Важным упрощением в Star Versus является то, что направление не непрерывно, а дискретно. Объекты могут двигаться только в 24 возможных направлениях. Перемещение вправо соответствует 0, вверх 6, влево 12, а вниз 18. Каждый инкремент направления представляет собой угол в / 24 радиан. ( это фундаментальная константа окружности, равная 6.2831853)

После выполнения кода распознавания коллизий у нас уже есть дельты позиций X/Y для функции arctan. Нам достаточно одних знаков, чтобы понять, в каком из четырёх квадрантов находится результат, поэтому остальная часть кода должна выяснить при помощи абсолютных значений дельт, какое из 6 направлений квадранта является правильным.

Процессор 6502 слишком медленный, чтобы вычислять arctan при помощи стандартных способов, например рядов Тейлора, но поскольку результат должен быть правильным в рамках / 24, мы можем сжульничать и использовать аппроксимацию. В целом план заключался в том, чтобы сначала найти соотношение X/Y, затем представить набор линий, разделяющих пространство в соответствии с возможными направлениями, затем найти наклон этих линий, и сравнить соотношение, чтобы понять, к какому из направлений угол ближе всего.


Нам нужно быть внимательными к тем углам, которые равномерно делят пространство на области, окружающие направления, которые должен возвращать arctan. Это /48, 3/48, 5/48, 11/48. Тангенс каждого из них равен:

tan( 1 * / 48) = 0.131652497587
tan( 3 * / 48) = 0.414213562373
tan( 5 * / 48) = 0.767326987979
tan( 7 * / 48) = 1.30322537284
tan( 9 * / 48) = 2.41421356237
tan(11 * / 48) = 7.59575411273

Поскольку у процессора 6502 нет команд умножения и деления, дробные значения нежелательны. Однако у него есть битовые сдвиги, с помощью которых можно малозатратно делить или умножать на 2. К счастью, три значения тангенсов выше диагонали довольно близки к 1.25, 2.5 и 7.5, а эти значения довольно легко найти при помощи битовых сдвигов [1]. Другие углы ниже диагонали просто являются их отражениями, поэтому мы можем найти их, поменяв местами X и Y.



Сравнивая соотношение X/Y с этими значениями, мы получим номер области от 0 и 3. Будет ли эта область находиться над диагональю, зависит от того, поменяли ли мы местами X и Y. Вот псевдокод алгоритма:

small, large = x, yif y < x:    small, large = y,xhalf = small / 2// compare to 2.5 slopeif small * 2 + half > large:    // compare to 1.25    quarter = half / 2    if small + quarter > large:        region = 1    else:        region = 0else:  // compare to 7.5  if small * 8 - half > large:    region = 3  else:    region = 2// Use region, whether X/Y were swapped, and quadrant in a lookup table.

Полный код на ассемблере выложен здесь вместе с юнит-тестами, использующими nes_unit_testing.

Применение


После реализации arctan он применяется во всех аспектах игры. Кроме коллизий мечей нужно также рассчитывать движение бомб, которые должны попадать в свои цели. Для этого они периодически проверяют направление в сторону движения к другому кораблю, и изменяя его, если отклонились от курса [2]. Почти так же arctan используется при стрейфе по кругу, поворачивая корабль в направлении другого корабля, пока он движется вбок, создавая круговое движение. Хотя реализация arctan неидеальна, на практике погрешность достаточно мала, поэтому самонаведение бомб и круговой стрейф работают хорошо.

При создании кампании на одного игрока arctan очень пригодился для создания вражеского ИИ. В базовом поведении врагов присутствует стрельба по игроку, и arctan определяет, как это нужно делать. У разных врагов есть различные паттерны разлёта пуль, но все они основаны на arctan.



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

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

Примечания


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

[2] Самонаводящиеся снаряды не меняют своё направление в каждом кадре и не меняют его, если слишком отклонились от цели. В противном случае они были бы слишком точными, что отрицательно сказалось бы на игровом балансе.

Источник: habr.com
К списку статей
Опубликовано: 06.11.2020 10:14:52
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Разработка игр

Nes

Nintendo entertainment system

Хаки

Оптимизация кода

Тригонометрия

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru