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

Эдвин Эбботт, "Флатландия"



Эта статья про застарелые комплексы, а также про успешное их преодоление. Восемь лет назад, Dagaz создавался не на пустом месте. В его основу легли Zillions, Axiom и… Jocly. Как раз с последним и было связано больше всего терзаний. Сейчас Jocly выглядит как 100%-ый 3D-проект, но так было не всегда. Когда-то в нём были 2D-игры (например манкалы). Где они теперь? Всё просто, разработчики выпилили их из проекта, поскольку посчитали, что те не вписались в новую концепцию трёхмерности. Было понятно, что это не путь для Dagaz, но какой путь для Dagaz было непонятно…

3D — это красиво, но, положа руку на сердце, существует совершенно ничтожный процент настольных игр, для которых 3D необходимо. Кроме того, 3D — это очень трудоёмко! Подготовка трёхмерных моделей и текстур для них требует гораздо больше времени чем простое рисование картинок в Paint-е. По этой причине, перевод всего массива игр Dagaz в 3D вряд ли оправдан. Но есть игры, в которых трёхмерность весьма востребована.


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

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

Как это было
Прежде всего, в 2D эта игра уже была реализована. Потребовалось всего лишь добавить Z-координату к пунктам доски. Конечно, представление и контроллер игры пришлось разрабатывать с нуля (на базе Three.js, разумеется). Сама реализация каких-то сюрпризов не принесла, за исключением того, что в этом месте пришлось впервые столкнуться с CORS. В общем, если приложение загружает модели или текстуры, для отладки придётся поднимать Web-сервер, либо пользоваться экзотическими флагами:

chrome.exe --disable-web-security --user-data-dir="C:/Temp"

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

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


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

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


Конечно, больше для полноты картины. Разработка полноценного бота для игры в Го — задача не тривиальная, да и людям ориентироваться в трёхмерных досках совсем не просто. По этим причинам, к доскам был прикручен простенький бот для игры в "Атари Го" (до первого захвата), а в такой игре ситуация Ко, по понятным причинам, возникнуть не может.


С этой менюшечкой тоже пришлось повозиться. Прежде всего, понадобился ещё один канвас для 2D-элементов (на размеры внимания не обращайте, они устанавливаются динамически). Само отображение меню и отрисовка tooltip-ов выглядят довольно тривиально.

С оверлейным канвасом был связан забавный баг, особенно проявлявшийся на мобильных устройствах, с их экстремально небольшим экраном. Для управления камерой в проекте используется OrbitControls. В тех случаях, когда события мыши попадали на 2D-канвас, до OrbitControls они просто не доходили и управление камерой не работало.

Далее, пришло время разбираться с движущимися фигурами:


Это вариация "Танца трёх мужчин" на необычной доске. У каждого игрока по 3 шара, они ходят по очереди в любой пункт доски, стараясь выстроить три своих шара в ряд. Если это не удалось, игроки каждым своим ходом перемещают один из своих шаров на пустой соседний пункт. Цель игры та же — тот кто первым выстроит 3 своих шара в ряд победил. В указанной выше позиции красные почти победили, но синие пока ещё могут двигаться. Зелёные сферы показывают куда можно переместиться.

Как двигать сферы?
Превращение последовательности кликов в допустимый ход — не такое простое дело. MoveList может показаться избыточным, но только до тех пор пока не придётся столкнуться с составными ходами в таких играх, как например Шашки. Но даже в максимально простом виде, он решает ряд важных задач:

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

Попробуем проследить всю цепочку. Контроллер создаёт MoveList для текущего состояния доски (генерируется список допустимых ходов). Сформированный список стартовых позиций используется для управления курсором, подсветки drop-ходов и отработки кликов по стартовым позициям. Получив стартовую позицию, MoveList формирует список целевых позиций, передаваемых в представление для отображения «зелёных шаров». При выполнении клика по отмеченной целевой позиции, MoveList завершает поиск выбранного хода и передаёт его в контроллер для выполнения.

В процессе выполнения хода, изменяется состояние модели и ход передаётся представлению. Ход состоит из последовательности действий: добавления, удаления и перемещения фигур. Завершающий commit запускает анимацию. В нашем случае, анимация перемещения сводится к пошаговому изменению координат меша позиции в сторону целевой, переключения его в невидимое состояние и возврат на исходное место, при завершении анимации.

Следующей 3D-игрой стал "Кубик Рубика". В некотором роде, это был шаг в сторону, но уж больно тема интересная.


Прежде всего, для 3x3x3, каждая из 26 (центр можно оставить пустым) фигур — это куб, грани которого раскрашены в разные цвета. Такой раскрашенный куб может иметь 24 ориентации, для каждой из которых определён свой тип фигуры. Поворачивая грань, важно не забыть превратить каждую фигуру в куб правильной ориентации.

Сложности начинаются далее
Раскрасить каждую грань несложно. Визуально отделить одни кубы от других помогает EdgesGeometry (а группирование позволяет манипулировать кубом и его каркасом как единым целым). Далее, поскольку клики по отдельным граням фигур несколько не укладывались в концепцию MoveList-а, скопились некоторые залежи технического долга. Саму грань легко идентифицировать по её нормали (мы ведь сами ориентировали кубик), а перед вращением, кубики придётся перегруппировать.

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


Анимация — это красиво, но медленно. Те из вас, кто торопятся, могут нажать иконку с глазом.

Кстати, меню стало двухуровневым (чтобы начинать игру не только с собранного или перемешанного кубика, но и с бесчисленного количества пасьянсов):


Некоторым продолжением темы послужила головоломка, опубликованная в одном из номеров журнала Квант.


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

Ну вот, мы и добрались до самого главного.



Технические детали
Зелёные круги пришлось заменить на что-то более сообразное. С моделями тоже вышла заминка. Дело в том, что Three.js перестала поддерживать JSONLoader, начиная с верcии r99 (довольно давно). Поскольку морочиться с Blender-ом и переконвертациями не хотелось, пришлось использовать более старую версию библиотеки. Грузить приходится всего и много, поэтому за этим приходится внимательно следить. Ну и всё что было сказано про CORS выше, остаётся в силе.

В части механики анимации ходов, пришлось добавить небольшой фикс, для того чтобы взятая фигура убиралась с доски (ещё немного технического долга), а также предусмотреть превращение пешек (выбор типа фигуры на 2D-канвасе — в планах на следующие релизы).
Что касается самой игры, Raumschach — это, на мой взгляд, наиболее последовательный и продуманный перенос шахматных правил на трёхмерную доску. Прежде всего, это выражается в адекватном размере доски. Кроме того, все шахматные фигуры сохраняют правила своих перемещений как в вертикальных, так и горизонтальных секущих плоскостях. Появляются новые фигуры «единорогов», перемещающихся по тригоналям. Рокировок, прыжков пешек и взятий на проходе в игре нет.

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

В целом, движок вроде бы заработал. Оценить качество его игры пока затруднительно, поскольку Advisor (механизм, подсказывающий ходы игроку) пришлось перенести на следующий релиз, отчасти из-за того, что вылезли баги в интеграции нового контроллера с session-manager-ом (по этой причине, временно отключены стрелочки «вперёд» и «назад».

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

Комментарии (5)


  1. pnmv
    16.06.2025 04:42

    вы изобрели трёхмерные шахматы из франшизы star trek.


    1. GlukKazan Автор
      16.06.2025 04:42

      Если вы читали статью, то нет


      1. pnmv
        16.06.2025 04:42

        100%-ый

        я прочел.


  1. Jijiki
    16.06.2025 04:42

    мне чтото напомнило Permutation


  1. OlegZH
    16.06.2025 04:42

    Было бы здорово иметь трёхмерную оперативную память. Чтобы можно было любой массив весьма различным образом (топологически) располагать в памяти. Чтобы можно было бы сравнивать два массива практически на просвет. В результате, можно было бы добиться ускорения вычислений на порядок, когда линейная операция будет выполняться за O(1), а квадратичная операция станет линейной.