Сам написал, сам поиграл: как работали трёхмерные игры на кнопочных телефонах нулевых? Пишем 3D-шутер с нуля

Думаю, большинство моих читателей уже пережили эпоху кнопочных телефонов, поддерживающих Java-приложения. Помните, как вы раньше мониторили раздел загрузок WAP-сайта, ожидая выхода новых игр, подходящих для вашего телефона и разрешения экрана? И с каждым выпуском графика улучшалась, доходя до уровня PlayStation 1. Какая радость от свежих 3D-игр Вам принесли?V-Rally, Galaxy On Fire, Asfalt Urban GT, Deep3D, Sony Ericsson Tennis, Left 2 Die, Counter Terrorism 3D - по крайней мере одна из этих игр, я думаю, известна. Но задумывались ли вы когда-нибудь, как эти игры работают под капотом?3D-ускоритель (видеокарта), сопроцессор для чисел с плавающей запятой (FPU) и один чип, работающий с частотой 30 секунд.Как разработчики адаптировали серьезные 3D-шутеры и гоночные игры на оборудовании без основного процессора? 100-200 МГц, помимо игр, будет обрабатывать также звуковые, входные и радиомодули.Сегодня мы узнаем, как разрабатываются игры для J2ME, какие графические API существуют и какие мобильные устройства Какие телефоны поддерживали, почему игры лучше работают на Sony Ericsson чем Nokia, и в качестве «перекуса» создаем с нуля 3D-экшен. В практической части! интересный? Так что добро пожаловать в разрез!

❯ Предыстория


Принято считать, что серьезные 3D-игры начали появляться на кнопочных телефонах где-то в 2004-2005 годах, когда был выпущен графический API M3G. Однако история мобильных 3D-игр уходит корнями в начало 2000-х годов, когда мобильные телефоны начали приобретать мультимедийные возможности.

Мы привыкли, что были Java-игры для простых кнопочных телефонов и BREW-игры для телефонов Qualcomm. Но в 2000-х годах несколько многообещающих компаний стали крупными разработчиками мобильных платформ, борясь за право заменить J2ME. Одной из самых известных была Mophun, кроссплатформенная виртуальная машина, выполнявшая байт-код в собственном формате. А уже в 2002-2003 годах Mophun представила 3D API для разработки быстрых и очень хороших мобильных игр.

Кстати, вы также можете опробовать библиотеку Mophun самостоятельно. В РФ как минимум среди устройств Mophun есть Sony Ericsson T610, который сейчас можно купить на вторичном рынке почти за 100 долларов.

Помимо Mophun, на французских мобильных телефонах производства Alcatel и Sagem была установлена ​​платформа In-Fusio. Он также был основан на J2ME (и, возможно, на каком-то собственном подмножестве API), но имел собственный набор API, ориентированный на разработку игр. Мы с EXL углубились в прошивку OT535 в HEX-редакторе, чтобы найти информацию о встроенной 2.5D приключенческой игре:

Как видите, спрос на 3D-игры возник примерно в то же время, когда мобильные устройства начали оснащаться очень мощными на тот момент процессорами (~60–100 МГц). Разработчики действительно верили, что смогут сделать мобильный телефон не только мультимедийным устройством, но и портативной портативной консолью с графикой на уровне GBA!

В сегодняшней статье я хотел бы остановиться именно на 3D Java-играх. Например, рисование 3D-графики вручную без использования внешнего API, написанного на C, явно проблематично. Устройство просто не может устранить накладные расходы Java-машины (за исключением некоторых 2.5D-игр, использующих собственный портальный рендерер, например Duke Nukem 3D).

В эпоху J2ME любая компания могла предложить расширить возможности мобильной платформы. Это называется JCP (Java Community Process), а его спецификация называется JSR. Поэтому появились различные расширения, такие как AWT, GLES, M3G, PIM, Bluetooth API и т д., и примерно к 2005 году большинство функциональных телефонов имели M3G. Но мы не ограничиваемся только M3G. Давайте посмотрим, какие еще GAPI существуют на вашем телефоне.

❯ Какие были 3D GAPI?


Для телефонов J2ME существовало три стандартизированных графических API для рендеринга 3D-графики, написанных в форме JSR. Вероятно, существовали еще какие-то специальные API для азиатских телефонов (которые традиционно были ориентированы на игры с крутой 3D-графикой), но они не были такими же, как штатные. Они не поддерживались на устройстве и информации о них было мало.

Давайте посмотрим на основные GAPI, используемые в большинстве мобильных телефонов:

  • M3G JSR184: Mobile 3D Graphics был самым популярным графическим API и поддерживался большинством мобильных телефонов Java. Он известен своей открытостью, функциональностью и исключительной производительностью. Строго говоря, M3G — это не только готовый API для рисования треугольников в привычном нам духе OpenGL и D3D, но и готовый граф сцены (типа Unity), формат модели, система материалов. Она стала стандартом для мобильных телефонов, поскольку такие функции, как обработка столкновений и освещение, были хорошо оптимизированы, а входной барьер был низким.

  • Mascot Capsule: графический движок, разработанный японской компанией Hi Corp.. В основном он использовался в мобильных телефонах для азиатского рынка и на момент выпуска обеспечивал очень хороший уровень графики. Он во многом лучше M3G, но порог входа немного выше. Это чем-то напоминает мне D3D/OpenGL. Поддерживается Sony Ericsson и Motorola.

  • OpenGL ES JSR239: поддерживается только на некоторых моделях и появился довольно поздно (в контексте телефонов Java). Поэтому на простых телефонах она не пользовалась популярностью, но на смартфонах активно использовалась (хотя рассмотреть игру стоит) iPhone 2G для сравнения). Это самый «тяжелый» и функциональный графический API из перечисленных. Что интересно, Google изначально полностью портировал JSR239 на Android, чтобы было проще портировать игру с Java-телефонов на смартфоны с зелеными роботами. Во-первых, это, наверное, помогло.


Большинство читателей нашли игры, использующие платформу M3G, которая помимо рендеринга 3D-графики предлагает множество функций. Например, упомянутый выше фирменный формат файла Scene Graph. Плагины для импорта и экспорта в 3D MAX были доступны каждому, а так как сам M3G не очень хорошо умел использовать собственные форматы файлов, то вскоре стали массово появляться моды для некоторых игр. Наверное, одним из рекордсменов по количеству модов является игра Left 2 Die. Эта игра была переделана всеми возможными способами. Я делал это в Half-Life, я делал это в Quake, я делал это в обычном Left 4 Dead.. как угодно случилось

Еще одна игра, которую часто модифицируют, — Comcraft, написанная студентами в начале 2010-х годов. Там моды были по сути чистыми ретекстурами, вроде «новых типов блоков». Все это возможно благодаря наличию различных Zip-архиваторов для мобильных телефонов Java. Это позволило молодым моддерам перекомпоновать ресурсы игры по своему вкусу.

Третья легендарная игра, которую выпустят через несколько лет после ее выпуска, — это, конечно же, Galaxy on Fire 2. Изначально игра предназначалась для работы на мощных устройствах, таких как смартфоны Symbian и мобильные телефоны Sony Ericsson. Но умелец вырезал звук, музыку и игра заработала на s40!

Однако использование глобальных модов, меняющих игровой процесс игры, не помогло. Хотя Java-программы легко декомпилировать, большинство разработчиков запутывали свой код при публикации своих игр. Кому по каким-то причинам хочется покопаться в чужом коде и деобфусцировать его (хотя это явно проще, чем копаться в нативном коде IDA Pro) и хотя бы попытаться создать что-то вроде "SDK для модов"? Там тоже никого не было ах :(

❯ Секреты производительности


Характеристики типичного кнопочного телефона того времени не особо впечатляли:

  • Процессор: 104–200 МГц, ядро ​​ARM926EJ-S, может поддерживать собственное выполнение байт-кода Java. Без сопроцессора с плавающей запятой (FPU) или видеоускорителя (за редким исключением) вся нагрузка ложится на процессор и, в некоторых случаях, на вспомогательный DSP.

  • Оперативная память: 8–16 МБ SDRAM. Приложение Java получило не всю память, а лишь небольшую ее часть, называемую кучей. Обычно размер кучи составлял от 800 КБ до 2 МБ. Умельцы пропатчили Java-машины некоторых телефонов, чтобы выделить программам больше памяти. От размера кучи зависела работа тяжелых игр и программ. Когда куча закончилась, приложение аварийно завершилось с ошибкой OutOfMemoryException.

  • Память: флэш-память 10–8 ГБ.

  • Дисплей: матрица CSTN, TN или AMOLED (редко) с разрешением от 128x128 до 480x320 (иногда выше).


Очевидно, что традиционные методы рендеринга 3D-графики не будут работать на устройствах с такими характеристиками из-за небольшого объема памяти. Поэтому мы использовали несколько интересных хаков, хорошо известных еще со времен PlayStation 1 и старых игровых консолей!

Начнем с сортировки примитивов. Представим, что за ней стоит машина и дом. Если я сначала нарисую машину, а затем дом, дом появится перед машиной, полностью нарушая эффект погружения и проекцию:

Примеры такого эффекта можно найти в большинстве игр для PlayStation 1. Например, здесь вы можете увидеть на камне ногу обезьяны, которая не такая, какой должна быть.

Современные приложения используют методы Z-буферизации экранного пространства для сортировки геометрий в зависимости от их расстояния от наблюдателя. Это позволяет «дешево» вырезать части невидимой геометрии. Принцип прост. Буфер создается по размеру окна приложения, и каждый пиксель содержит информацию не о цвете, а о расстоянии фрагмента в этой конкретной точке. В результате при отрисовке автомобиля в Z-буфер записывается глубина (диапазон) конкретного фрагмента (участка геометрии), а при отрисовке дома видеочип сравнивает значения глубины фрагментов. Если значение глубины, хранящееся в буфере, меньше предыдущего значения (или в зависимости от реализации), части автомобиля отрисовываются там, где это необходимо. Думаю, это объяснение более чем понятно :)

Однако для Z-буфера требуется ценная память (минимальная ширина экрана * высота экрана * 2 — число байтов с половиной числа с плавающей запятой — или по крайней мере 153 килобайта для экрана 240x320) и достаточная точность буфера глубины. Требуется наличие FPU. Если вы попытаетесь использовать для этого обычные числа, вы быстро столкнетесь с проблемами точности. Так что произойдет глубокий бой, и эта техника доставит больше хлопот, чем пользы.

В этих телефонах используется та же технология, что и в PS1: сортировка отдельных полигонов. В PS1 этому способствовал отдельный векторный сопроцессор, который управлял преобразованиями геометрии, освещением и мог эффективно менять порядок треугольников, не загружая основной процессор. Однако на мобильном телефоне это выполняет основной процессор вместе с растеризацией, игровой логикой, обработкой звука и радиомодуля. Итак, чтобы разобраться правильно, весь рендеринг в GAPI на телефоне разбивается на «батчи», куда программист отправляет необходимый набор команд (текстуры такие-то и трансформации такие-то). Затем API преобразует и меняет порядок вершин наиболее эффективным способом.

второй интересный момент — это система координат. Как упоминалось ранее, на телефонах зачастую не было аппаратной поддержки чисел с плавающей запятой. Однако машины j2me и компиляторы C (в то время для прошивки чаще использовался ADS, чем GCC) обеспечивали программную реализацию чисел с плавающей запятой, которая была значительно медленнее, чем аппаратный FPU. Поэтому умножение матриц и даже такие операции, как вычисление синуса и косинуса, могут быть относительно сложными на телефоне.
Чтобы обойти это ограничение, были использованы два метода. Один из них — это число с фиксированной запятой (M3G), где число с десятичной частью представлено как обычное целое число, которое может быть обработано процессором, а другой — обычное целое число, которое представляет собой нормализованное число 0,0... 1.0 (Капсула Талисмана). Оба метода имеют ограниченную точность и иногда могут вносить артефакты, но здесь все во многом зависит от самой игры. Например, из-за низкой точности мир может «трястись» при движении персонажа».

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

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

Другой пример — шахматная доска. Шахматная доска при приближении не является идеально горизонтальной, а при очень близком приближении возникают значительные артефакты, полностью искажающие визуальное представление. Для коррекции перспективы также требуется FPU. Это не бесплатная операция, и от нее обычно отказываются (за исключением M3G). Вот почему игры с J2ME могут выглядеть очень странно:

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

В процессе подготовки этой статьи я декомпилировал и изучил несколько 2.5D-игр 2000-х годов, таких как Wolfenstein 3D. Для повышения производительности многие использовали пакет Nokia UI с DirectGraphics. Это обеспечило отличную функциональность для 2D-игр, предоставив возможность переносить произвольные изображения непосредственно в буфер экрана. Поэтому разработчики приложили немало усилий для внедрения классического рейкастинга и портальных рендереров. И все заработало очень быстро :)

Но вы пришли сюда не для того, чтобы прочитать учебник «Magic Single» или Красную книгу OpenGL, верно? Итак, давайте создадим собственную приключенческую 3D-игру для мобильных телефонов Java с нуля. Да, это не совсем игра, но она дает четкое представление о том, как писались игры в то время.

❯ Пишем свою «игру» с нуля


И я буду писать основы 3D-шутеров от первого лица, а не только совсем примитивные. Да, вы правильно прочитали :) Графика, конечно, из 90-х, не на уровне Crysis, но для кнопочного устройства очень даже неплохо. Плюс я написал эту игрушку за полтора дня. Основной рендерер я написал за полдня, а остальные базовые части геймплея — за день.

Я решил использовать Mascot Capsule в качестве графического API. Материалов об этом в Интернете относительно мало, и для многих остается загадкой, как это работает «под капотом» aNNiMON немного написал о M3G, и в Интернете есть некоторая информация, но JSR239, в частности, плохо поддерживается на реальных устройствах. Здесь важно понимать, что M3G и Mascot Capsule не являются DX11 или Vulkan, имеют очень низкий входной барьер и их очень легко понять, если вы понимаете основы работы 3D-графики. Итак, создаем проект, мидлет (приложение в терминологии J2ME) и приступаем!

Рендерер


Конечно, мы начинаем с инициализации контекста.
В J2ME игровой цикл обычно реализуется путем наследования GameCanvas. Чтобы начать использовать MascotCapsule и M3G, просто создайте объект Graphics3D (для M3G ​​вы получаете ссылку на синглтон) и выделите необходимые ресурсы. В данном случае это матрицы под названием AffineTrans здесь.

Самый примитивный игровой цикл выглядит так: Сначала залейте экран цветом, чтобы предотвратить эффект зеркального зала, и привяжите объект Graphics к капсуле талисмана. Затем нарисуйте сцену, чтобы освободить контекст, а затем вызовите flashGraphics, чтобы отобразить изображение на экране:

Результат: Синий экран.

Давайте что-нибудь нарисуем. Mascot Capsule может использовать как собственный формат модели mbac, так и формат анимации ActionTable, а также произвольную геометрию. Слишком просто использовать готовые форматы. Я не особо хочу использовать 3ds Max с плагинами, поэтому геометрию сгенерирую сам. Для начала нарисуем треугольник и трансформируем геометрию.

Effect3D — это материал в терминах Mascot Capsule. Этот объект позволяет вам устанавливать визуальные параметры геометрии, такие как освещение и источники света, мультяшное затенение для мультяшного эффекта, альфа-смешение и другие эффекты.

Далее выполняется геометрическое преобразование. Это процесс преобразования треугольника из локальной системы координат в мир, экран и пространство клипа.
здесь affineMatrix — основная матрица, хранящая результат умножения матрицы viewProj, а affineRotationY и affineTranslate — матрицы преобразования сначала для камеры, а затем для преобразования модели в мировые координаты. Кроме того, эта проекция также является частью AffineTrans. Это очень сбивает с толку.

FOV(512) задается в радианах. 0 — 0, 4096 — 3,14*2 (360 градусов). То же самое касается и углов поворота.

обратите внимание, что размеры матрицы составляют 3x3, а не 4x4, что распространено в «больших» системах. Здесь нет переводов. Взгляни.

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

UV-координаты указаны в абсолютных координатах текстуры. То есть 0...ширина текстуры и 0...высота текстуры вместо 0..255, как, например, в 3dfx Voodoo. Решение в порядке, учитывая, что вы даже не можете проверить его размер в классе текстуры.

результат:

Добавьте второй треугольник, чтобы создать прямоугольник:

Здесь будет написана половина рендерера. Я не шучу :)

Далее создайте кубическую геометрию. Для ясности я создал простой класс, который генерирует геометрию на лету. Нам пока не нужен другой формат для нашей модели, поэтому мы «запечем» геометрию в другой массив вершин.

результат:

Он позволяет рисовать текстурированные кубики, а также имеет камеру. Уже можно реализовать экшен :)

Уровни


Создавайте уровни прямо в IDE как? Уровни сохраняются классическим для подобных игр способом. Сетка x x x, где каждое число указывает идентификатор текстуры (оставшиеся биты могут содержать некоторые атрибуты). 0 означает отсутствие блокировки. Все блоки считаются сплошными.

З.Ы. В Пикабу нет тега с кодом, поэтому пришлось вставить код в виде скриншота. Кроме того, существует ограничение на количество медиа-элементов в сообщении в 25 блоков, поэтому остальной код пришлось кастрировать :(

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

https://pastebin.com/cwrZ4vDJ

результат:

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

Напомним, что углы хранятся в формате 4096=360 градусов. = 3,14 * 2.

https://pastebin.com/1Hy26kfk

Ваш персонаж теперь может ходить!

Однако без полов и потолков игра выглядит не очень привлекательно. Пришло время добавить!Из-за отсутствия коррекции текстур сложно реализовать настоящий вертикальный потолок. Пол всегда «плавает» и поэтому не очень эстетичен. Итак, давайте воспользуемся старомодным методом и закрасим нижнюю часть экрана серым, а в качестве фонового изображения используем потолок! Таким образом, каким бы большим ни был ваш уровень, в центре экрана всегда будет какая-то стена, которая не позволит перспективной проекции сломаться!

Вы не можете одновременно смешивать графику и Graphics3D. Производительность будет значительно снижена. Однако после рендеринга кадра вы можете использовать Graphics для рисования интерфейса поверх Graphics3D. Суть взлома проста. Он использует ортогональную (параллельную) матрицу для рисования половины экрана с пустой текстурой, а другая половина представляет собой просто серый прямоугольник. Все так легко и просто!

https://pastebin.com/4P6DRB7V

Нарисуйте оружие на экране и создайте своего рода 3D-шутер.

Game.current.renderer.getGraphics().drawImage(weapon, Game.current.renderer.getWidth() - Weapon.getWidth() + (int)viewBobFactor, Game.current.renderer.getHeight() - Weapon.getHeight() , Графика.ЛЕВО | Графика.ТОП);

результат:

Помните обо всех артефактах, упомянутых в разделе «Оптимизация игры». Затем всплывают текстуры и мир становится шатким. Все это является результатом оптимизации разработчиков GAPI. Но работает быстро!

Обработка столкновений


Каждому стрелку необходима обработка столкновений. Даже космический скролл-шутер :)
Здесь я немного растерялся. Нет необходимости проверять наличие столкновений между каждым кубом на сцене и игроком. Было бы достаточно выбрать из восьми ближайших блоков, но я оставил так для ясности.

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

https://pastebin.com/jqT7d5XM

Можно предположить, что основы игры уже пройдены.

❯ Что у нас получилось?


Давайте взглянем. Запускаем игру в эмуляторе:

А как насчет реальных устройств?У меня есть SE W200i, W610i и J10i2 для тестирования!
Mascot Capsule поддерживают все, поэтому с тестированием проблем нет. Ниже тест моего первого комментария к W200i на других устройствах - Пикабу не позволяет загружать слишком много фото/видео в один пост :(

❯ Заключение


Так мы создавали 3D-игры для кнопочных телефонов в 2000-х. Да, мы практически не затронули тему 3D-игр на Symbian-смартфонах (ранее я писал статью о разработке игр для WinMobile), но обсудили множество тонкостей и обсудили подобные вопросы для Java.Я написал собственное доказательство концепции для приключенческая игра мобильный телефон. Делюсь исходным кодом приключенческой игры. Любой может использовать его как основу для своих игр и т д. По крайней мере, я видел несколько Java-игр, выпущенных несколько лет назад, которые кто-то до сих пор делает.

Напишите, пожалуйста, свой опыт работы с Java-играми в комментариях :)

ПС: Друзья! В будущих статьях я буду периодически писать статьи о поиске различных китайских устройств (подделок, реплик, трюков и т.п для iPhone, Samsung, Sony, HTC и т.п.). Но мы решили в конце каждой статьи вставлять рекламу о поиске устройств для контента, потому что читатели очень часто пишут: «Где ты был месяц назад, я сумку выбросил!». Выбросить что-то или отправить на чермет? Даже «нерабочие» или полурабочие, которые не работают? Или, может быть, эти устройства могут стать еще более интересными. Мой ответ на китайские подделки айфонов, самсунгов, макбуков и айпадов читайте в соответствующих постах!

Вам нравится материал? У меня есть канал на Телеге, где я публикую закулисные статьи и всякие мысли и советы по ремонту и программированию различных устройств, а также время от времени ссылки на новые статьи. 1-2 поста в день, никакого мусора!

Всего голосов:
Всего голосов:

Этот документ был создан при поддержке TimeWeb Cloud. Подпишитесь на меня @Timeweb.Cloud, чтобы не пропустить новую статью каждую неделю!

[my]Опрос Гаджет Программирование Графика 3D-графика Разработка игр Opengl Sony Ericsson Nokia Кнопка телефона Оператор Ностальгия Видео YouTube Без звука Вертикальное видео Длинный пост 17

Больше интересных статей здесь: Гаджеты.

Источник статьи: Сам написал, сам поиграл: как работали трёхмерные игры на кнопочных телефонах нулевых? Пишем 3D-шутер с нуля.