Задний план
Правила Пико-8. Фэнтезийная консоль, дающая жизнь чудесной альтернативной вселенной с открытой консолью, лежащей где-то между 8 и 16 битами. Таким образом, он отлично подходит для быстрого рендеринга множества спрайтов (2D-изображений) и их перебрасывания по экрану. Несмотря на это, люди заставляли его делать то, для чего он не предназначался, включая 3D-рендеринг. Окунуться в эту воду было самым увлекательным хобби-программированием, которое я когда-либо делал в yeeeeeeaaaaaars.
Raycasting
Мое первое знакомство с Raycasting как серьезным программным предложением было, когда Мэтт Хьюсон опубликовал это. Благодаря 3D-рендерингу Wolfenstein и ссылке на учебное пособие рендеринг в 3D (2.5D, что угодно) с нуля был в пределах досягаемости.
Итак, я следил за учебником (с lodev.org), его продолжением и третьим продолжением. Я реализовал рендерер raycasting в Pico-8 и на Android (с помощью AIDE on my commute). Все было замечательно, пока я реализовал первый урок. Стены можно визуализировать с помощью функции sspr () Pico-8, которая может быстро визуализировать масштабированные вертикальные срезы таблицы спрайтов - и вам нужно делать это только 128 раз за кадр. Но когда дело дошло до пола (алгоритм объясняется здесь), я столкнулся с проблемами. Метод, описанный в руководстве, рассчитал текстуру пола цвет текселя для каждого пикселя между нижним пикселем стены и нижней частью экрана. При использовании pset () Pico-8 это было очень-очень медленно. В течение следующего {insert-long-period-of-time-here} я улучшил скорость примерно в 2 раза.
Код
Вы можете увидеть код, который реализует большую часть того, о чем я говорю в этом посте на Github. Есть еще несколько оптимизаций, но я рассмотрю их дальше.
От медленного к быстрому
Pico-8 работает со скоростью 30 кадров в секунду (60 кадров в секунду возможно, но поддерживается не во всех версиях), так что это моя цель. Ниже приведены шаги, которые я предпринял для достижения стабильной частоты кадров.
Версия 1: просто заполните каждую вертикальную линию на несколько пикселей (10) ниже конца стены. (CPU на 1.582 - больше 1.0, и частота кадров автоматически падает до 15 кадров в секунду)

В этой версии были действительно очевидные артефакты из-за неточности чисел с фиксированной запятой.
Версия 2: Именно тогда я задумался о проблеме. Как я мог делать меньше расчетов? Самый простой способ - визуализировать только каждую вторую строку с помощью pset (), удалив таким образом половину поиска текселей. Но сейчас в полу большие щели. (ЦП на 1.004)

Примерно в это же время я начинаю экспериментировать с тем, какие именно точки пола рисую, и придумываю новый метод, пропуская вертикальные линии и используя line (), чтобы начать заполнять пространство. Это выглядит немного странно, и много места кажется незаполненным. Однако это окажется на правильном пути.

Версия 3: Теперь с идеей использования line () для покрытия большего пространства, мне интересно, сработает ли использование шаблона шахматной доски (как в пикселях, выбранных для обработки пола).

На данный момент все еще выполняется рендеринг большинства пикселей пола. Так почему бы не ограничить это, чтобы дать эффект глубины резкости / темноты?
Версия 4: здесь просто используется sin (), чтобы создать красивую кривую для края «детализированной» части пола. Также снижает нагрузку на ЦП.

Все еще слишком много раз вычислял тексель. Что мы действительно хотим, так это снова сократить вдвое.
Версия 5: это делается путем рисования пола только для каждой второй вертикальной колонны. Значение для расчета смещения, чтобы разрешить шахматную доску, должно увеличиваться по мере рендеринга стен, но это небольшая цена за огромное падение ЦП. Это значение смещения увеличивается на единицу для каждого отображаемого столбца, а значение «смещения% 2» используется для начала каждого столбца в нужном месте.

У вас по-прежнему остается хорошее впечатление от текстуры пола. Но это можно улучшить, и теперь у ЦП есть запас, чтобы делать более интересные вещи.
Версия 6: Используя line () длиной 4, можно заполнить промежутки между разреженными пикселями. Это даже не так дорого ...

Версия 7:… но ее можно улучшить, заменив строку () на rectfill (). Будет ли этот скачок производительности сохраняться в будущих версиях Pico-8, еще предстоит увидеть, но пока это можно только приветствовать.

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

Это делает более удаленные участки пола более темными и менее детализированными, что улучшает общий вид и снижает визуальный шум, связанный с изменением цвета пикселей в каждом кадре при перемещении. Стоимость процессора увеличилась, но готовый продукт выглядит действительно красиво.
Прочие оптимизации
Я сделал еще несколько вещей, чтобы сделать рендеринг пола по-настоящему быстрым.
- Разделите рендеринг ближнего / дальнего этажа на два цикла, чтобы больше не было операторов if в цикле, охватывающем весь пол.
- Сбросил 2D-карту индексов текстуры пола в пространство пользовательской памяти Pico-8, чтобы избежать дорогостоящего поиска в таблице для большого количества пикселей.
- Предварительно рассчитайте как можно больше
- Визуализируйте стены от нижней части экрана до границы стены. В учебнике, который я прочитал, все обстоит наоборот, но для этого требуется расчет положения стены. Вы можете проработать текстуру пола относительно игрока за меньшие деньги.
- Все данные передаются как параметры функции. Я начал с массива 2D-мира и игрока, на который ссылались (и на все остальное) как на глобальные переменные, но в Lua это медленно - локальные переменные работают намного быстрее!
- Обновление от 05.03.19 - установка положительных значений текстуры карты устраняет дорогостоящее вычисление abs () из 2 контуров, рисующих пол. Это означает, что нужно изменить некоторые другие проверки на лучи и коллизии, но оно того стоит!
Вывод
Значения ЦП являются относительными, так как некоторые из приведенных выше оптимизаций уже используются. Я надеюсь, что он по-прежнему дает вам представление о том, чего можно достичь, если пойти на некоторые компромиссы и по возможности избегать математических вычислений.
Я пошел от этого ...

…к этому…

… И получил ›увеличение скорости в 2 раза для (разумного) падения качества изображения. Я надеюсь, что это было полезно для вас, и даже если вам не нравится заниматься рейкастингом, я думаю, что эти методы можно использовать и в другом месте! :) Я думаю, что было бы круто удалить стены и просто использовать рендеринг пола, чтобы получить эффект Mode-7!
Довольно свежий код доступен на Github здесь! Скоро появится лучший код.
Другие люди, занимающиеся аналогичными двигателями
Многие другие люди делали подобную работу, и я определенно был вдохновлен ими и другими!
Если вам интересно узнать, что сделали другие люди, внедряя такого рода движок Wolfenstein 3D в фэнтезийные консоли, просмотрите эти аккаунты в Twitter:
Также посетите lodev.org, чтобы найти отличные уроки по графике, которые я нашел действительно полезными.