Как создавались 3D-игры для кнопочных телефонов: от M3G до Mascot Capsule и практическая разработка шутера с нуля

Многие из нас застали эпоху кнопочных телефонов с поддержкой Java-приложений. Помните то волнение, когда вы проверяли WAP-сайты в поисках новых игр, которые подходили бы именно для вашей модели и разрешения экрана? С каждым годом графика становилась всё лучше, приближаясь по уровню к PlayStation 1. Такие игры, как V-Rally, Galaxy On Fire, Asphalt Urban GT или Counter Terrorism 3D, наверняка принесли вам немало радости. Но задумывались ли вы, как эти, казалось бы, сложные трёхмерные миры умещались в устройство без видеокарты и мощного процессора? Как разработчикам удавалось адаптировать шутеры и гонки под железо с частотой 100-200 МГц, которое параллельно управляло звуком, радиомодулем и обработкой ввода? В этой статье мы разберёмся в технологиях мобильной 3D-графики нулевых, сравним графические API и на практике создадим свой собственный 3D-шутер для платформы J2ME.

❯ Предыстория: первые шаги мобильной 3D-графики

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

Помимо стандартных Java (J2ME) и BREW-игр, на рынке существовали альтернативные платформы. Одной из самых известных была Mophun — кроссплатформенная виртуальная машина, выполнявшая собственный байт-код. Уже в 2002-2003 годах она представила свой 3D API, позволявший создавать быстрые и визуально привлекательные игры.

Интересно, что библиотеку Mophun можно опробовать и сегодня. Например, на телефоне Sony Ericsson T610, который ещё можно найти на вторичном рынке.

Были и другие платформы, например, In-Fusio на телефонах Alcatel и Sagem. Она также базировалась на J2ME, но имела собственный набор API, заточенный под игры. Спрос на 3D-развлечения возник одновременно с появлением более мощных процессоров (около 60-100 МГц), и разработчики верили, что смогут превратить телефон в портативную консоль с графикой уровня Game Boy Advance.

В этой статье мы сосредоточимся именно на Java-играх, так как написание 3D-рендерера «вручную», без использования оптимизированных API на C, было крайне затруднительно из-за накладных расходов виртуальной машины Java.

❯ Какие графические API (GAPI) существовали?

Для платформы J2ME было стандартизировано три основных графических API для работы с 3D, оформленных в виде спецификаций JSR. Давайте рассмотрим их.

  • M3G (JSR184: Mobile 3D Graphics) — самый популярный и распространённый API. Это был не просто низкоуровневый инструмент для отрисовки треугольников, а целый готовый граф сцены (подобный современным Unity или Unreal Engine), со встроенной системой материалов, освещения и даже форматом моделей. Благодаря высокой оптимизации и низкому порогу входа он стал стандартом для многих телефонов.

  • Mascot Capsule — движок от японской компании Hi Corp., который в основном использовался на азиатском рынке и обеспечивал выдающуюся для своего времени графику. Он был ближе к классическим Direct3D/OpenGL и требовал от разработчика больше знаний. Поддерживался такими брендами, как Sony Ericsson и Motorola.

  • OpenGL ES (JSR239) — «тяжёлый» и функциональный API, который появился относительно поздно и поддерживался в основном на смартфонах (например, на ранних iPhone и Android-устройствах). На простых кнопочных телефонах он не получил широкого распространения.

Большинство игр, которые вы помните, использовали M3G. Его особенностью был собственный формат файлов для графа сцены, для которого существовали плагины к 3D-редакторам. Это привело к буму модификаций: игры вроде Left 2 Die или Comcraft обзавелись множеством пользовательских модов, от ретекстур до полноценных конверсий в стиле Half-Life или Quake.

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

❯ Секреты производительности: как умещали 3D в слабое железо

Характеристики типичного кнопочного телефона середины 2000-х сегодня выглядят скромно:

  • Процессор: 104–200 МГц, ядро ARM, обычно без FPU (сопроцессора для вычислений с плавающей запятой) и без видеокарты.

  • Оперативная память: 8–16 МБ. Приложению Java выделялась лишь часть — «куча» размером от 800 КБ до 2 МБ.

  • Экран: разрешение от 128x128 до 480x320 пикселей.

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

Одна из ключевых проблем — правильная сортировка объектов. Если нарисовать машину, а потом дом, дом перекроет машину, что нарушит перспективу. Современные системы используют Z-буфер (буфер глубины), который для каждого пикселя хранит информацию о расстоянии до объекта. Но для Z-буфера нужна дополнительная память (например, около 150 КБ для экрана 240x320) и высокая точность вычислений, что было проблемой без FPU.

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

Вторая проблема — вычисления. Без аппаратного FPU операции с плавающей запятой выполнялись очень медленно. Выходом стали числа с фиксированной запятой (в M3G) или нормализованные целые числа (в Mascot Capsule). Это могло приводить к артефактам, например, к «дрожанию» мира при движении камеры.

Третья проблема — текстурирование и перспектива. Без коррекции перспективы текстуры на поверхностях искажались при приближении камеры. Эта операция также требовала FPU, поэтому от неё часто отказывались, что придавало играм характерный «странный» вид.

Чтобы избежать артефактов, дизайнеры уровней старались не размещать объекты слишком близко и не допускать ситуаций, когда камера видит сразу много крупных полигонов.

❯ Практика: пишем свой 3D-шутер с нуля

Давайте на практике разберёмся, как создавались такие игры. Мы напишем основу для 3D-шутера от первого лица, используя API Mascot Capsule. Графика будет в духе 90-х, но для кнопочного телефона это более чем достойно.

Создание рендерера

В J2ME игровой цикл обычно строится на основе класса GameCanvas. Для работы с Mascot Capsule нужно создать объект Graphics3D и настроить необходимые ресурсы, например, матрицы преобразований (AffineTrans).

Базовый игровой цикл выглядит так: очистка экрана, привязка графического контекста, отрисовка сцены, освобождение контекста и вывод кадра на экран методом flushGraphics().

Для начала нарисуем простой треугольник. В Mascot Capsule материал задаётся объектом Effect3D, который определяет параметры освещения, затенения и прозрачности. Геометрию (вершины, текстурные координаты) можно сгенерировать программно.

Обратите внимание, что матрицы преобразований здесь имеют размер 3x3 (без компонента перемещения), а углы задаются в специфичном формате (например, 4096 соответствует 360 градусам).

Добавив второй треугольник, получим прямоугольник, а сгенерировав 8 вершин и 12 треугольников — текстурированный куб. Уже можно реализовать простую камеру.

Построение уровней и геймплея

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

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

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

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

Оружие и интерфейс рисуются поверх 3D-сцены с помощью стандартного объекта Graphics после завершения рендеринга кадра.

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

Для проверки столкновений игрока со стенами используется метод AABB (ограничивающий параллелепипед). Вместо проверки со всеми кубами уровня достаточно проверить лишь несколько ближайших. При обнаружении коллизии игрока просто «отталкивают» назад от стены.

❯ Что в итоге?

В результате мы получили работающий каркас 3D-шутера, который можно запустить как в эмуляторе, так и на реальных устройствах вроде Sony Ericsson W200i или W610i, поддерживающих Mascot Capsule. Код демонстрирует основные принципы: рендеринг примитивов, управление камерой, простую физику и оптимизации, характерные для эпохи.

❯ Заключение

Так создавались 3D-миры на кнопочных телефонах в нулевые. Несмотря на ограничения железа, разработчики находили остроумные решения, а стандарты вроде M3G и Mascot Capsule значительно упрощали процесс. Представленный в статье код может служить основой для собственных экспериментов или ностальгических проектов. А какие Java-игры запомнились вам больше всего?

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

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

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

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

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