Search
Write a publication
Pull to refresh
55
0
Алексей @Swamp_Dok

Программист, радиолюбитель.

Send message

Да. Надеюсь этим летом добить демку с плавной камерой. Но пришлось переписывать весь движок.

Можно купить новодельную денди и посмотреть топологию осовремененую под микроскопом. Но там не чистый 6502.

Только сейчас прочитал комментарий.

Вчера перед сном размышлял о движке и архитектуре таблиц. И забавно, что придумал примерно тоже самое, что ты описал в этом комментарии (код нормально тоже не смотрел ещё).

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

Только лучи около 0 и 90 градусов нужно отдельно обрабатывать.

Про 128 лучей согласен, логично. И хорошая идея про ориентацию по таблице из фиксированной стартовой позиции, но тут придётся вручную на асме писать. Сс65 туповат, он каждый раз будет считывать указатели в регистры. Но это мелочи.

И я примерно придумал как быстрое формирование стен сделать с дробными тайлами по краям. Если брать шаг тайла в 2 пикселя, там не так много вариантов высоты стен.

И со статус баром проблем нет. Можно выводить его спрайтами или использовать параллакс.

С полигонами тоже скорее всего многое заменится таблицами, ибо сцена всего 64х64 пикселя. Но там все надо доделать пока без оптимизации.

Код буду изучать, работа большая проделана, спасибо. Попробую его портировать. Прикрепи его на странице на итч.ио, так удобнее будет.

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

Библиотеку дробей я изначально писал, так как у СС65 они вообще никак не реализованы, только целые типы. Поэтому пришлось писать библиотеку. Там у меня псевдо дроби по факту, ибо они с фиксированной точкой, арифметика целочисленная.

Значит у тебя сейчас лучевая шестеренка на 128 зубьев? А как же рывки? Я наоборот перешел на таблицы с 1024 значений на 360 градусов, шаг 0.35 градуса. Значения в таблицах можно было сделать однобайтными, но проще знак хранить в таблице, чем генерировать его через свитч. Но вычисления, связанные с тангенсом, в однобайтные числа как закладывать? Тангенс около 90 градусов может быть +- 160.

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

Я тоже планировал заменять таблицами места, где число умножается на тангенс. Расстояние тоже можно было бы скорее всего загнать в чистые таблицы (на основе косинусов или корней не особо важно, суть одна).

Этот вариант тоже рассматривал, я рассчитываю угол наклона полигона, чтоб определить его видимость, можно привязать к нему и освещённость.

Но частоту кадров всё-таки хотелось бы побольше) Тем более выяснилось, что я могу перерисовать всю сцену за один кадр.

А можно скрин с рендером последней версии? Самому лень все собирать и возиться с ДосБоксом.

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

Или там вообще одна переменная с двумя тетрадами X и Y?

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

Речь о самих скалерах? Т. е. мы получили высоту колонки (относительно максимума в 16 или в 24 — время одинаковое ушло), но теперь её надо заполнить в дополнительном буфере (как я понял, этим мы весь кадр занимаемся, пока не наступило гашение луча на обратном ходу) — и вот тут как раз время сильно зависит от того, 16 или 24 тайла надо актуализировать?

Увеличится время на заполнение буфера для последующего его вывода в видеопамять. Работа с массивами через переменные-индексы очень плохо работает. Смещения желательно хардкодить (зато смещения могут быть 16-битными, ибо шина адреса 16 бит).

…и 256 стека, итого 512, верно?

Как бы вообще дофига, если честно. А как она такая быстрая вообще? Там что, SRAM, а не DRAM? Изначально-то понятно, это всё проектировалось под ROM и дюжину регистров под «прыг, скок и шмяк», но сейчас ведь там (по крайней мере в области спрайтов) оперативка и ещё к тому же маппер?

Стек хранится в общем адресном пространстве, его можно хранить в любых адресах и даже выбрать его размер можно, если не хватает оперативки. Обычно его делают 256 байт, но видел и 128 байт. Он нужен чисто для вызова функций, чтоб не потерять значения регистров. Для другого его использовать особо смысла нет. Скорость чтения одинаковая.

Чуть быстрее работают первые 256 байт оперативной памяти (из-за удобных адресов). Все остальная оперативка работает на 1-2 такта медленнее в зависимости от команды.

И, кстати, маппер переключает только часть области или всё вот это вот? Потому что при прямых руках можно вообще лихо сделать процессор-Джекил и процессор-Хайд, которые превращаются друг в друга переключением всей памяти, включая стек вызовов и переменные, и ничего не знают друг о друге :)

Мой маппер переключает первые 16 килобайт ROM. Вполне можно подключать и другие микросхемы ПЗУ, но счетчик команд будет указывать на последний адрес, а переключении всей ПЗУ он попадает неизвестно куда. Это нужно будет учитывать. Что такое я хочу сделать, когда буду картридж с ОС делать. Будет ПЗУ с загрузчиком (считывание дискет, магнитофона и т.д.). Когда загрузчик отработал и сложил программу в отдельную ОЗУ (которая будет работать как ROM), процессор переключится на подготовленную ОЗУ вместо загрузчика-ПЗУ. Самое простое делать это через RESET-прерывание, но можно спаять и аппаратное пользовательское прерывание в картридже (по-умолчанию оно не используется, висит в воздухе).

А это можно подержать в руках или оно только хозяина слушается? А как потом происходит эмуляция, чем оно всё проверяется? А то любопытство просто жесть уже %) игры ещё нет даже в общих чертах, а уже затягивает похлеще большинства головоломок %)

Да, там легко все собирается. Я как раз планирую сделать проект быстрого старта. Статью напишу и выложу на гитхаб все, но его пока готовлю. Ссылку на гитхаб постараюсь пораньше дать.

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

:: Запускать компиляцию из директории проекта такой командо:
:: start /b  %cc65bat% <имя_файла_без_разширения>
:: %cc65bat% - имя системной переменной, котороая хранить путь к этому батнику (можно назвать как удобно)
@echo off

set name=%~1

:: Переводит в .asm
:: -Oi - опитмизатор
cc65 -Oi %name%.c --add-source
:: Из .asm компилирует объектный файл
ca65 reset.s
ca65 %name%.s
ca65 asm4c.s
:: Линкует файлы
ld65 -C nes.cfg -o %name%.nes reset.o %name%.o asm4c.o nes.lib -m %name%.map
:: -C указывает использовать конфиг файл
:: ld65 -C nes.cfg -o %name%.nes reset.o %name%.o nes.lib

del *.o

start D:\Programs\Emulator\fceux-2.6.4\fceux.exe %name%.nes

А сколько у нас, кстати, всего памяти и как именно её маппер организует (к вопросу выше)? А то там, по ходу дела, карта даже 64×64 клетки не факт, что влезет.

16 страниц по 16 килобайт. Но там еще 100500 мапперов. Но страницу больше 32 килобайт сделать не получится. В видеопамяти страницы по 8 килобайт. Но в моем маппере там ОЗУ вместо ПЗУ.

Код аутентично лежит в ROM или часть можно в SRAM держать? А то там явно нужно перед началом каста править некоторые адреса и константы прямо в самом коде.

Там общее адресное пространство (если не считать видеопамять) для программ и данных (фон Нейман). Я думаю, что так можно сделать. Не вижу ограничений, но надо пробовать. Я раньше думал об этом, но так и не попробовал. Как попробую, то отпишусь.

И про текущее состояние моего движка.

Эксперименты с Брезенхемом я прекратил, так как точные точки пересечения с ним не получить, если шагать по целым клеткам. + мне не нравится необходимость находить конечные точки луча.

В итоге я решил вернуть к более стандартному подходу. Координаты теперь будут 16-битные (8 бит на целую часть и 8 бит на дробную часть (положение внутри клетки). Ты к этому же параллельно пришел :).

Карту я оставил 16 на 16, но буду помнить, что каждая клетка делится на 256х256 подклеток. Это дает быстрые вычисления, не сильно хуже, чем раньше.

Луч теперь будет стрелять через нахождение первого пересечения с соседней клеткой (это всего 1-2 умножения и пара сложений), а следующие пересечения находятся обычным сложением.

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

Длину луча буду считать через таблицу косинусов и синусов (это еще одно умножение).

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

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

Оценку по ФПС сейчас дать не могу, но кажется, что в 2 кадра я должен уложиться. А где 2 кадра, там и 1 кадр. 70 умножений должны успеть обработаться за 2 кадра. А заполнение видеобуфера можно будет сильно ускорить. Но пока надо просто заставить работать движок хоть как-то.

Можно сделать рамку вокруг полигонов и использовать разные цвета для соседних полигонов.

Про разработку игр для денди можно у меня в профиле почитать) Сейчас, правда, отвлёкся на разработку 3D- движка для неё же.

Я, видимо, неправильно сформулировал про 16х16. У меня не одна клетка 16х16, а я вся карта 16х16 (один блок карты 1х1). Поэтому обычное определение расстояния через корни особо ничего не даст. И отсюда и прыжки персонажа, а шаг камеры сейчас 256 значений на 90 градусов.

Еще я использую рабочую сцену 32х16 тайла (быстрее можно вывести и удобный размер для хранения и вывода). Можно сделать 32х24, столько байт я тоже должен успеть вывести за один кадр.

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

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

Еще я рассчитываю на уменьшение угла обзора до 60 градусов, это на треть увеличит горизонтальное разрешение сцены (сейчас 90 градусов).

Еще можно будет все-таки попробовать считать расстояние через корни (при текущем разрешении не сложно сделать таблицу). Но думаю, что это ничего не даст.

"Быстрая" память есть у 6502 , но там 1-2 такта разница у команд относительно других разделов памяти, но там всего 256 байт. Я их и так использую в алгоритмах. Проект я собираю с помощью батника, который запускает компилятор сс65.

Даже текущий алгоритм явно съедает несколько кадров (точные расчеты я пока не делал), при том, что каждый луч проходит максимум 8 шагов. Т.е. расстояние до стен у меня от 1 до 8. Увеличение дальности тут тоже ничего не даст, только увеличение разрешения карты. Но не хочется переходить к 16-битным индексам, это сразу замедлит любые обращения к карте раза в 3. А у нас и так с ФПС проблемы.

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

Добавил затемнение стен при удалении и простую карту (правда она не помещается по высоте полностью). Потом еще добавлю стрелку направления вместо собаки на карте.

Вот чуть улучшенный вариант. Я ещё придумал как сделать градиентное затемнение при удалении от игрока, но пока не реализовал.

Корней у меня вообще нет в коде. Расстояние я нахожу через количество итераций Брезенхема. Двух зайцев одним брезенхемом. Спасибо деду за алгоритм.

И в ходе экспериментов проявились проблемы с разрешением карты 16х16. Шаги игрока будут прыжками и всего по 8-ми направлениям.

И из-за малого разрешения карты диапазон высот стен очень маленький. Расстояние от 1 до 8 блоков. Итого стены от 2 до 16 тайлов.

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

Ещё возможно поможет уменьшение угла обзора до 60 градусов. Так будет больше горизонтальное разрешение. Сейчас 90.

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

Переписывание всего на ассемблере может дать ускорение в 5-10 раз. Но пока все отлаживаю на си.

Привет. Немного продвинул свой 2.5D-движок. Для трассировки и вычисления расстояния использую свой старый брезенхем (взял код из своей прошлой статьи). Он вроде бы подходит.

За один кадр пока я не успеваю все посчитать (Примерно 25к тактов на кадр доступно, но я вижу, что можно оптимизировать). Думаю, трассировку успею посчитать. Но ведь потом еще и логику считать, спрайты и музыку. Но если хотя бы пустой лабиринт заработает, уже будет успех.

Вот что удалось получить. Первый корректный рассчет столбцов (было много проблем из-за оптимизации и восьмибитности):

Про мышь я думал. Я планирую делать программный UART, чтоб денди подключить к интернету, а UART почти как PS/2 протокол работает. Если получится UART, можно попробовать и UART. Но процессорного времени оно есть будет очень много, так как таймеров нет и придется задержки делать NOP-ми.

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

Аппаратное отзеркаливание тайлов работает только со спрайтами, фоны аппаратно зеркалить нельзя.

Выглядит отлично, рубленные края вообще не мешают взгляду. Но на денди даже поинтереснее можно попробовать делать графику, какие-никакие текстуры там можно сделать. Еще можно подумать про пол и потолок. Но даже в таком виде, если получится плавная камера, это будет разрыв.

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

В ASCII почти тоже самое концептуально получается. В коде новом позже подробнее покопаюсь.

И про шестеренку отличная аналогия :) Сколько по твоему шагов на круг нормально будет? 256 шагов на 360 градусов без проблем могу добавить, переменные останутся однобайтными.

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

Тут у нас небольшие противоречия получаются, так как я стараюсь ограничиться 8-битными переменными и не использовать дроби. Поэтому у меня пока локация 16х16 блоков (условно 1х1 метр один блок). И координаты у меня пока меняются с шагом в один блок для простоты. (16х16 поле положений игрока)

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

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

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

Такой вопрос пока. Как ты получаешь конечную точку для Брезенхема (точка к которой мы шагаем)? Просто рассчитываешь ее через синусы-косинусы и дальность обзора для текущего угла обзора?

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

Information

Rating
11,648-th
Location
Воронеж, Воронежская обл., Россия
Date of birth
Registered
Activity

Specialization

Game Developer, Embedded Software Engineer
From 50,000 ₽
C++
C
Programming microcontrollers
Python
Game Development
Electronics Development