Вражеские пули попадают в игрока на C64.

Я просматриваю Интернет и старые книги по C64 в поисках вопроса, но не нахожу ответа, поэтому в конце концов мне просто пришлось опубликовать его здесь. Мне нравятся старые добрые времена кодирования C64, и хотя я сейчас не программирую игры для этой платформы, я хотел бы знать, как в то время преодолевались некоторые аппаратные ограничения.

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

bullet_velocity = (player.position - bullet.position).normalize();

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

Пожалуйста, ответьте, у меня тысячи таких сомнений, но, отвечая на этот вопрос, может быть, я смогу найти ответ на остальные! :-)

РЕДАКТИРОВАТЬ: Просто примечание: примерами игр C64 с пулями, нацеленными на игроков, являются Silkworm и Cybernoid.


person 1Garrett2010    schedule 27.10.2015    source источник
comment
Это навевает воспоминания... Насколько я помню, в большинстве игр для C64 эта проблема решалась просто тем, что все действия происходили строго в горизонтальном или вертикальном направлении, что делало весь этот вопрос спорным.   -  person Sam Varshavchik    schedule 27.10.2015
comment
@1Garrett2010: Не могли бы вы привести пример конкретной игры/уровня? Потому что разные игры, вероятно, делали что-то по-разному. Я думаю, что многие старые игры позволяли врагам стрелять только по прямой горизонтальной/вертикальной линии с фиксированной скоростью.   -  person Michael    schedule 27.10.2015
comment
Кроме того, мишени не точечного размера, поэтому некоторая неточность в ракурсе все равно даст вам попадание, да и разрешение экрана не такое уж большое. Математика с фиксированной точкой должна быть выполнима с небольшой таблицей поиска. Но это только мои догадки :)   -  person Jester    schedule 27.10.2015
comment
@Michael: Привет, игра Silkworm, боссы 1-го уровня (и не только они) стреляют в игрока, а также в некоторых врагов в Cybernoid. Но этот список игр, я думаю, мог бы быть намного шире.   -  person 1Garrett2010    schedule 27.10.2015
comment
Я не знаю наверняка, но они определенно могли использовать алгоритм Брезенхема или основные подпрограммы с плавающей запятой в ПЗУ.   -  person Nick Westgate    schedule 27.10.2015


Ответы (2)


Предположим, вас устраивает достаточно небольшое количество выходных направлений, проще всего это сделать с помощью таблицы поиска. Например. для 64 выходных направлений возьмите вектор (x, y) от источника к месту назначения, и, если оба положительны, сдвиньте оба влево, пока одно из них не заполнит знаковый бит, затем сформируйте четырехбитный индекс таблицы из двух верхних битов каждого и посмотрите выходной вектор.

Предполагая, что у вас 160x200, я думаю, вам нужно отбросить немного точности, прежде чем войти.

Отражайте соответствующим образом, чтобы иметь дело с другими квадрантами. Предполагая фиксированную точку 8,8 для местоположений объектов и скорость пули 1, тогда это 32-байтовая таблица поиска.

Итак, наивно, что-то вроде:

xPosYPos:

    ; assuming ($00) has the positive x difference
    ; and ($01) has the positive y difference

    lda $00
    ora $01

shiftLoop:
    asl $00
    asl $01
    asl a
    bpl shiftLoop

    ; load A with table index
    lda #$00
    rol $00
    rol a
    rol $00
    rol a
    rol $01
    rol a
    rol $01
    rol a

    ; look up vector
    tax
    lda xVec, x
    ; ... put somewhere ...
    lda yVec, x
    ; ... put somewhere ...

... с более разумным решением, вероятно, включающим что-то вроде:

    lda $00
    ora $01

    asl a
    bmi shift1
    asl a
    bmi shift2
    ... etc ...

shift1:

... etc, but you can shift directly to form
the table index rather than going to all the work
of shifting the vector then piecing together from
the top bits ...

Или вы можете создать 256-байтовую таблицу поиска для поиска подпрограммного адреса на основе x|y (который всегда будет не более 127, потому что они оба положительны) и перейти непосредственно к сдвигу без подсчета битов.


Учебник по расположению объектов и фиксированной точке:

Предполагая, что вы находитесь в режиме 160x200, вы можете сохранить каждый компонент местоположения объекта в виде одного байта. Итак, один байт для x, один байт для y. Вместо этого многие игры сохраняют каждое местоположение в виде двух байтов. Всего четыре байта для x и y.

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

Обычно это называется арифметикой с фиксированной точкой. В отличие от числа с плавающей запятой, место в целом числе, где находится десятичная точка, фиксировано. В описанной здесь схеме всегда восемь бит.

Так, например. чтобы добавить смещение только с количеством байтов:

clc
lda xPosition
adc xVelocity
sta xPosition

sta SomeHardwareRegisterForSpritePosition

Чтобы добавить смещение со схемой с фиксированной точкой:

clc

lda xFractionalPosition
adc xFractionalVelocity
sta xFractionalPosition

lda xPosition
adc xVelocity
sta xPosition

sta SomeHardwareRegisterForSpritePosition

Преимущество в том, что ваш вектор скорости теперь может быть всего 1/256 пикселя в любом направлении. Так, например. вы можете сохранить скорость, которая говорит о том, что в каждом кадре ваша пуля будет перемещаться на один пиксель влево и на 32/256 пикселя вниз. И все, что нужно для перемещения этой пули с субпиксельной точностью, — это дополнительная пара байтов памяти на вектор и дополнительная пара АЦП.

С приведенным выше предложением вы получите вектор от источника к месту назначения, вычитая один байт одного из одного байта другого. Результатом будут два одиночных байта, оба из которых будут дробными частями вывода. Так, например. вы можете решить выстрелить пулей с вектором (87/256, 239/256), то есть под углом 20 градусов.

person Tommy    schedule 28.10.2015
comment
Спасибо, у меня есть некоторые сомнения: 1) для захвата вектора (x, y) от источника к месту назначения вы намереваетесь взять векторы начальной и конечной позиции пули? Если вектор должен быть рассчитан от источника к месту назначения, задействована плавающая математика, и я не вижу преимуществ! 2) В источниках C64 обычно я вижу таблицы поиска (например, таблицы синусов) с уже рассчитанными позициями (x, y), в которые можно перемещать объект/спрайт. Здесь вы намереваетесь хранить одно число с плавающей запятой (x, y) для добавления на каждом шаге движения к спрайту пули? Итак, некоторые вычисления с плавающей запятой разрешены при программировании игр C64? - person 1Garrett2010; 28.10.2015
comment
Нет, поплавки не нужны. Если позиции являются целыми числами, то вектор между ними также является целым числом. Но для движения, которое не просто влево, вправо, вверх, вниз или по диагонали, вы обычно используете фиксированную точку — например. один байт для целой части x, другой байт для дробной части. Пусть несут поток от одного к другому. Было бы полезно расширить мой ответ, чтобы охватить фиксированную точку? Трудно влезть в комментарий. - person Tommy; 28.10.2015
comment
Расширение ответа, охватывающего фиксированную точку, было бы абсолютно полезным. Спасибо! - person 1Garrett2010; 28.10.2015

Вы также можете (ab) использовать алгоритм рисования линии Брезенхэма, чтобы нацелить свои пули, при этом начальные координаты «линии» будут координатами врага, а конечные координаты — координатами вашего корабля. Однако вместо добавления пикселей к линии вы просто перерисовываете маркер в текущей позиции на каждой итерации.

person tiny tim    schedule 15.01.2016