(бухай): программируй
148 subscribers
465 photos
96 videos
3 files
65 links
Мысли, дегустация и код
Download Telegram
Накидал "немного" планов на будущий год )
Я вообще не могу представить, сколько еще займёт разработка.
Начал я все это делать в конце ноября, и первый месяц тупо раздумывал, как лучше сделать графический движок.

Сейчас это позади, но теперь самые сложные вещи - это объекты на уровнях, ролевая система и звуки.

Надеюсь, что за следующие полгода я решу большинство этих проблем
1
С Новым Годом, ребята:)
🔥4🍾1
Сегодня я добавил спрайты и как же я с ними намучился=)

Оказалось, что зона в памяти с $1000 по $1fff занята зеркалом Character ROM, и видеочип не может считать спрайты из этой области. А это было идеальное место под них.
Видеочип смотрит всего на 16 Кб памяти одновременно, и под спрайты места не остается.

Поэтому сперва я принял решение изменить область памяти (VIC Bank), на которую смотрит видеочип (перенести туда, где Character ROM не зеркалится). Это слабо помогло, потому что пришлось бы учесть еще кучу возможных параметров: создать новые символы, перенести область, откуда берутся цвета для hires bitmap и при каждом прерывании менять не только параметры экрана, но и VIC Bank. В общем, куча лишних действий, да и всю программу пришлось бы перелопатить.

Поэтому я поступил иначе:
Теперь все спрайты лежат вообще в другом месте памяти, но подгружаются в область видимости видеочипа в процессе. Оказалось, что это довольно быстро и совсем не заметно (с учетом, что игра у меня совсем не динамическая)
4
This media is not supported in your browser
VIEW IN TELEGRAM
А еще для каждого спрайта можно менять цвета.
Один программный спрайт состоит из четырех аппаратных, поэтому два цвета на весь спрайт будут всегда одинаковыми. Но третий цвет можно менять для каждого из 4-х аппаратных спрайтов.

А так как на карте может быть много объектов одного спрайта, а на экране будет отображаться только 1 программный спрайт (в ближайшем от игрока блоке), то и цвета для всех объектов спрайта могут быть разными (что задается в данных карты)
2
Ну и немного того, как все в очередной раз ломалось из-за смены VIC Bank. Здесь например цвета для экрана берутся из части той-же зоны, где находится сам экран.
Получается небольшая рекурсия
1
Назрел вопрос на будущее.

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

Второй вопрос - делать игру линейной или с небольшим открытым миром, чтобы можно было переходить между уровнями в любом порядке?

И пока что я вижу такие варианты:
1. Самый сложный. Так как каждый новый уровень будет подгружаться с дискеты, то его состояние всегда будет одинаковым. Придется где-то хранить все возможные состояния триггеров и самого уровня. Это, как по мне, сделать ну очень сложно. Поэтому такой вариант пока что можно отбросить.

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

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

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

Есть возможность сохранять данные на дискету. То есть я могу теоретически записать основные параметры и считывать их при загрузке игры.
Как это будет работать на практике, пока что непонятно.

Второй вариант - использовать пароли прямо как в старых играх на денди, да:)
1
Ну что ж, я починил артефакт между графической и текстовой зонами.
Раньше в прерывании я считывал значения регистров и командами AND и ORA включал или выключал нужные биты.

lda $d011
ora #%00100000
sta $d011
lda $d016
ora #%00010000
sta $d016
lda $d018
ora #%00001000
sta $d018

Теперь я просто беру нужное значение и сразу кладу его в регистр:
        lda     #$3b
sta $d011
lda #$d8
sta $d016
lda #$1d
sta $d018

Занимает куда меньше циклов, и видимо из-за этого процессор успевает отработать прерывание до выхода за пределы линии. Докинул недостающие циклы NOP-ами, и все заработало
2👍2
Я тут подумал, что можно использовать вкладки для текстовой зоны!
Это позволит запихнуть больше информации на экран.

Нарисовал то, что я примерно хочу видеть в итоге:
1. Слева будет компас, который показывает направление движения (вполне себе замена для мини-карты). Правда, мне не очень нравится, как он выглядит, буду перерисовывать.
2. Справа - поле Player с количеством здоровья, брони и ключами, которые есть у игрока.
3. Посередине будут меняться 4 вкладки, при этом вкладка Fight будет появляться, если игрок вступил в бой.

Конечно, для этих вкладок надо будет выделять память:
64 байта для всех позиций компаса (4*4*4)
520 байт для всех четырех вкладок (26*5*4)
Многовато, но зато экран будет информативным
🔥32
Ну, теперь это уже стало больше похоже на игру.
Правда зачем нам ролевая система и игровые механики, если теперь буковки рисуются=)
2
This media is not supported in your browser
VIEW IN TELEGRAM
Ну и видео.
Стрелка правильно показывает направление (что можно использовать в подсказках), а под конец можно увидеть, как появляется ключ, когда я беру его из шкафа.
Там же можно увидеть, что вывод текста пока что не работает, как собственно и переключение вкладок. Но уже что-то
2👍2🤯1
Media is too big
VIEW IN TELEGRAM
Видеоотчет за последние несколько недель:)
2
И как-то так теперь выглядит карта в Tiled. То есть можно прям в программе забить нужные позиции триггеров и даже назвать из как полагается.

А Tiled хорош тем, что сохраняет все карты в формат JSON или XML. Мне JSON больше по душе, его легче читать.
И довольно простым питоновским скриптом все это дело можно перенести в формат ассемблера.
2👍1
This media is not supported in your browser
VIEW IN TELEGRAM
Сегодня сделал не так много:

Во-первых, немного изменил принцип считывания клавиатуры. Из-за этого теперь не удастся зажать клавишу и бесконечно выполнять одно и тоже действие. Но за счет этого я избавился от таймера нажатия (вызывался при каждом нажатии и ждал +- 0.1 секунду, прежде чем можно было считать следующую клавишу.

Во-вторых - добавил все текстовые меню и переключения между ними.

И возникла одна идея. В левой пустой части экрана сделать индикаторы изменений в текстовых меню. Допустим, игрок нашел триггер, который выводит текстовое сообщение. Само сообщение отправляется в буфер, а в левой части экрана появляется конверт. Мол, иди смотри, что тебе там написали.
🔥1
А еще я долго размышлял, как лучше сделать переключения оружия.
Все оружие будет в левой части экрана в квадратиках.
Сперва я думал сделать это спрайтами, но у меня уже занято 4 спрайта. Пришлось бы их размножать, вовремя переключать цвета и т.д.
Потом я подумал, что можно было бы рисовывать оружие попиксельно. Но одна зона для каждого оружия занимает 72 байта, что в сумме дает 576 байт (если не учитывать, что должно быть несколько состояний: оружие доступно/оружие выбрано/оружие недоступно).

Поэтому самый оптимальным решением я вижу банальную смену цветов в знакоместах.
Например:
серый/серый - оружие недоступно;
игрок подбирает оружие - меняем на белый/белый - оружие доступно;
игрок выбирает оружие - меняем на синий/красный - оружие выбрано.

Это позволит моментально выбирать нужное оружие и переключать его отображение в HUD без изменения пикселей.
🔥3
Сегодня я написал генератор случайных чисел.
Нууу.. как написал. Скорее спер отсюда:

https://codebase64.org/doku.php?id=base:ax_tinyrand8

Единственное, что я поменял - это процесс генерации зерна. Вместо того, чтобы брать значение из регистра A (аккумулятор) для последующей генерации, я взял значения таймера.
Нижний байт таймера лежит в регистре $dc04, а верхний - в регистре $dc05. Банальным XOR этих двух чисел получаем нужное зерно.

А дальше сделал все по инструкции и проверил на 27 тысячах итераций. Паттернов как будто особо не видно, да и вероятностное распределение по полученным числам тоже в пределах нормы.

Есть и второй вариант, и он довольно специфический.
Дело в том, что в Commodore есть звуковой чип, и один из каналов этого чипа умеет генерировать шумы. А шумы - это ничто иное, как псевдослучайные числа. Но такой подход я использовать не хочу, все таки звуковой канал есть звуковой канал.
🔥4
Продолжаю описывать всякие полезные для будущей ролевой системы функции.

И сейчас написал функцию для конвертации бинарных значений в так называемое Binary Coded Decimal (BCD).
BCD - это довольно полезная штука, например, для вывода на экран числовых значений.

В стандартном виде числа хранятся в одном байте памяти в диапазоне $00 — $FF в шестнадцатеричной системе, то есть от 0 до 255 в десятичной. А BCD позволяет представить десятичные числа в шестнадцатеричном формате.

Для этого один байт надо разбить на две части по 4 бита: 0000 0000.
4 бита могут хранить значения от 0 до 15 (то есть от $00 до $0F). Но в десятичной системе нет цифр A,B,C,D,E,F, поэтому нужно ограничиться значениями от 0 до 9 на каждые 4 бита.

Например, при использовании BCD значение $87 будет непосредственно равно 87, а не 135, как в шестнадцатеричной системе.

И как удобно, что процессор 6502 поддерживает такой формат чисел на аппаратном уровне.
Для того, чтобы процессор считал в десятичном формате надо поставить флаг D (decimal) в регистре статусов. например, чтобы сложить два числа, надо сделать так:
sed ; выставляем флаг
clc
lda #$49
adc #$20
cld ; убираем флаг

На выходе в регистре A будет значение $69
👍3🔥1
И возникает закономерный вопрос: А почему бы не хранить в таком виде все значения?

Во-первых, в одном байте в таком случае можно хранить всего до 100 значений: от $00 до $99.
Во-вторых, процессор 6502 в таком режиме может только вычитать и складывать числа. Все другие операции будут производиться в обычном двоичном формате.
Например, для увеличения адреса на 1, я уже не могу сделать так:
inc $nnnn

Вместо этого придется делать что-то такое:
sed
clc
lda $nnnn
adc #$01
sta $nnnn
cld

Это не очень удобно.

Зато очень удобно выводить числа на экран. Числа в экранной памяти кодируются значениями от $30 до $39. Просто прибавляем BCD значение (левую и правую части байта) к $30 и выводим на экран нужную цифру.

Поэтому я и решил написать функцию, которая конвертирует бинарные числа в BCD.
Получилось что-то такое:
binary_to_bcd_converter
pha
lda #0
sta decimal_result
sta decimal_result+1

sed
ldx #8
@dec_loop
pla
dex
bmi @end
asl
pha
bcc @dec_loop

clc
lda decimal_result+1
adc decimal_tb_lo,x
sta decimal_result+1
lda decimal_result
adc decimal_tb_hi,x
sta decimal_result

jmp @dec_loop
@end
cld
rts

decimal_result
byte $00,$00
decimal_tb_lo
byte $01,$02,$04,$08,$16,$32,$64,$28
decimal_tb_hi
byte $00,$00,$00,$00,$00,$00,$00,$01


В аккумуляторе хранится двоичное число, например $1000 1001 (136 в десятичной)
Сдвигаем все биты влево в цикле. И каждый бит при этом попадает в carry (флаг переноса).
Если carry равен 1, то надо просто прибавить к результату соответствующий разряд:
0 бит — 1
1 бит — 2
2 бит — 4
3 бит — 8
4 бит — 16
5 бит — 32
6 бит — 64
7 бит — 128

В итоге, полученное число окажется в адресах результата:
decimal_result — $01
decimal_result+1 — $36

И эти значения можно уже без проблем вывести на экран
1🔥1