Вы можете использовать так называемый "кольцевой буфер", если знаете максимальную длину змейки.
«Массивы» в ассемблере — это просто непрерывная область памяти, структура которой определяется вашим кодом.
Допустим, у вас будет змейка длиной не более 5 звездочек, тогда я бы использовал буфер из 6 позиций [x, y], чтобы определить, что 5 позиций — это звездочки от головы до хвоста, 6-я позиция — предыдущая позиция хвоста, т. е. где пробел печатается, чтобы «стереть» хвост.
Я бы на самом деле немного изменил это в коде, имея такой буфер с позициями длиной в степени двойки (следующая степень двойки после 6 равна 8, если ваша змея будет расти, чтобы заполнить экран, тогда вам нужно 80 * 25 = не более 2000 позиций, а следующая степень двойки — 2048). И я бы оставил индекс для элемента head (затем просто выбираю направление к следующим частям тела, как вы хотите, я, вероятно, поместил бы head последним во время инициализации, поэтому буфер [0] будет содержать хвост "пробел", буфер [1] последний звездочка "хвост", ... буфер[5] будет звездочкой "голова", и я бы придерживался этого headIndex=5 (или, скорее, непосредственно смещение памяти как headOffset = 10
, так как каждый элемент будет состоять из 2 байтов ([x,y]
) и 5* 2 = 10).
Итак, у вас есть некоторый «кольцевой буфер» для позиций частей тела в разделе данных:
BUFFER_SIZE EQU 2*8 ; or 2*2048 for growing snake
; everything will be "2*", because each position is 2 bytes big
snakeLength:
dw ?
headOffset:
dw ?
positions:
db BUFFER_SIZE DUP (?)
И некоторый код, который инициализирует длину змеи до 5, затем первые 6 пар байтов в positions
, где начинается змейка (включая хвост «пробел» в первой позиции!), и смещение головы до 10
(5-я позиция байт-пара в буфере).
Теперь, чтобы напечатать змею, вы загрузите headOffset
и snakeLength
и много раз выполните "snakeLength":
- получить два байта из
positions + offset
, как [x,y]
- напечатать звездочку там
- уменьшить смещение на два
and <register with offset>,(BUFFER_SIZE-2)
, что заставит его указывать на последний элемент памяти в случае, если смещение уже равно 0 (вот причина, почему степень двойки, чтобы позволить мне обернуть «кольцевой буфер» одной инструкцией and
и операцией маскирования битов) .
И, наконец, еще одна итерация, но на этот раз печать пробела, а не звездочки (для последнего элемента).
Вот о том, как нарисовать змею, определенную в таком массиве.
Как "переместить" его в каком-то направлении, скажем (-1, 0) (влево):
- прочитать
headOffset
значение
- прочитать текущее положение головы из
[positions + headOffset]
- приращение смещения на 2
and <register with offset>,(BUFFER_SIZE-2)
указывает на следующий элемент в буфере
- добавить (-1, 0) к текущему положению головы
- сохранить эту обновленную позицию в
[positions + offset]
- сохранить смещение как новое
headOffset
(что сделает последнее положение звездочки хвоста теперь новым хвостом «пробел», а предыдущий хвост «пробел» недосягаем с той же змеиной длиной).
После того, как вы нарисуете это новое состояние данных, змейка «переместится» влево на один шаг.
Как вырастить змейку: то же самое, что и перемещение, но вы также обновляете snakeLength
на +1, поэтому последние два элемента буфера по-прежнему используются как предыдущий хвост звездочки + пробел, сохраняя хвост в том же положении, в то время как голова перемещается вперед. (с помощью этого метода вы можете вырастить змею только на +1 на каждом этапе, так как вы не добавляете новые части тела к хвосту... но именно так работают старые игры со змеями, когда вы едите более крупную пищу, которая делает вашу змейка растет на N частей, это делается за N следующих шагов, а не мгновенно).
Итак, у вас есть массив из 8 позиций, из которых используются только 6. Какие 6 используются, определяется headOffset
, который идет по кругу (от смещения 0 движение «вниз» возвращается к смещению 14 (8 * 2-2), а от смещения 14 движение вверх означает посадку на смещение 0, поэтому "кольцевой буфер" крутится, как "кольцо", без начала и конца).
Альтернативой является наличие массива из 6 позиций, и когда вы перемещаете змейку, вы буквально перемещаете данные в массиве из позиции i в позицию i-1, а затем записываете новая позиция головы к последнему элементу массива. Это, вероятно, проще понять с первого раза, но решение с «кольцевым буфером» лучше, потому что вам не нужно перемещать блок памяти каждый раз, когда змея движется, вы просто добавляете новую голову и корректируете данные смещения/длины вместо перемещения позиции в памяти, вы перемещаете смещение, по которому вы получаете доступ к данным. (со змейкой длиной 1500 частей это означает экономию почти 3000 байтов memmove
только за счет настройки одного нового элемента и обновления смещения). Платой за это является введение логики деформации смещения (переход от смещения 14 к 0 и от 0 к 14) каждый раз, когда вы переходите от одного элемента к другому, но это делается с помощью одной инструкции and
, так что это все равно будет намного сложнее. быстрее, чем копирование памяти буфера.
emu8086 уступает, и ваш лектор должен обновить инструменты и курс (как насчет NASM + dosbox + какой-нибудь аккуратный отладчик, в большинстве университетов, скорее всего, все еще есть студенческие лицензии на турбо-отладчик примерно с эпохи 199x).
person
Ped7g
schedule
18.08.2017
int 21h,2
печатает его в следующей позиции по умолчанию (в STDOUT), а не вверх/вниз /вправо/влево нажатием какой-либо клавиши. Кроме того, поскольку это DOS, почему бы вам не писать напрямую в VRAM, поскольку вы работаете над игрой, в эпоху 8086 PC XT, если вы хотели написать игру, вам приходилось отрезать всех посредников, включая очень медленные вызовы BIOS/DOS. Кстати, вы хорошо понимаете C/C++, поэтому я могу использовать примеры C в ответе? - person Ped7g   schedule 18.08.2017emu8086
, и он молча компилировалmov [di+bx*2],ax
какmov [di+bx],ax
без каких-либо предупреждений или ошибок. Я не хочу поддерживать такой плохой инструмент (у меня не в первый раз возникают какие-то странные проблемы с emu8086). - person Ped7g   schedule 18.08.2017int 10h
/int 21h
. Возможно, вам следует прочитать об организации видеопамяти VGA в текстовом режиме под DOS и проверить этот пример, чтобы увидеть, как это работает. Хотя для чтения состояния клавиатуры BIOSint 16h
подходит для простой игры в змейку (я просто надеюсь, что правильно помню эти прерывания), чтение клавиатуры непосредственно из порта 60h (или 61h?) приведет к конфликту с обработчиком BIOS, и вам это не нужно. для змеиной игры. - person Ped7g   schedule 18.08.2017