Давайте продолжим работать над нашим веб-раннером, смоделируем космический корабль и закончим развязку наших систем!

⬅️ Учебник № 2. Создание игрового класса| TOC |Урок №4: Создание бесконечной плоскости с помощью шейдера ➡️

В прошлый раз мы поработали над логикой нашей игры и подготовили скелет нашего класса Game. В этом эпизоде ​​мы собираемся еще больше усилить разъединение между игровым кодом и основной подпрограммой и увидим, как использовать утилиты геометрии Three.js для моделирования нашего маленького космический корабль!

Это руководство доступно как в формате видео, так и в текстовом формате — см. ниже :)

Завершите развязку наших систем

Краткий обзор

Если мы вернем наш скрипт main.js, то увидим, что пока он состоит из трех частей:

  • во-первых, мы создаем основные объекты Three.js (т. е. сцену, камеру и средство визуализации) и добавляем автоматически сгенерированный HTML-холст в нашу модель DOM: на этом этапе у нас есть действительный, но пустая 3D-сцена
  • затем мы фактически инициализируем нашу 3D-сцену: сейчас мы создаем наш зеленый куб из геометрии и материала — затем мы добавляем его в сцену и размещаем нашу камеру
  • и, наконец, мы определяем нашу функцию анимации цикла и вызываем ее один раз, чтобы запустить автоматический цикл.

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

Это означает, что мы хотим извлечь из этой логики средний фрагмент кода, который создает содержимое 3D-сцены. Это часть, которая будет варьироваться от игры к игре, и нам нужно будет перейти к классу Game. Помните, как в прошлый раз мы создавали наш конструктор и метод _initializeScene()? Это те, которые мы собираемся постепенно заполнять, чтобы переместить эту логику внутри класса.

Делаем то же самое... но лучше!

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

Если мы посмотрим на наш код, то увидим, что есть 2 объекта, которые нам нужно передать нашему экземпляру игры, чтобы он мог воссоздать сцену: сцена и камера. Мы отправим их нашему экземпляру через его конструктор в виде простых входных параметров:

И теперь мы можем передать их прямо в метод _initializeScene(), а также можем скопировать часть кода в середине нашего скрипта main.js:

Вместо этой логики инициализации сцены наш скрипт main.js будет иметь только одну строку, в которой мы создаем наш экземпляр игры и передаем ему сцену и объекты камеры:

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

Давайте возьмем эти строки и переместим их в метод update() нашего класса Game:

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

Наконец, в сценарии main.js вместо непосредственного обновления cube мы просто вызовем метод update() нашего экземпляра игры:

Теперь, если вы сохраните это, вы получите точно такую ​​же 3D-сцену, как и раньше, но все делается внутри класса Game, что намного лучше! :)

Время заняться моделированием!

С учетом сказанного, наконец-то пришло время приступить к моделированию нашего космического корабля ;)

Перво-наперво: что именно мы хотим смоделировать? Three.js предоставляет нам галерею геометрических утилит, но мы должны знать, какую форму мы хотим, чтобы наш корабль имел, прежде чем погрузиться в документы!

Здесь я опишу процесс создания корабля, который я использую в Hyperspeed, но, конечно же, вы можете найти свой собственный дизайн космического корабля…

Изучаем конструкцию корабля

Итак, как вы можете видеть на этом изображении, моя модель космического корабля состоит из 7 частей: у вас есть корпус корабля, который представляет собой тетраэдр (это похоже на пирамиду), а затем 3 реактора, каждый из которых имеет «розетку» и «свет»/«энергию» цилиндры:

Мы создадим экземпляр этого корабля в нашем скрипте game.js, в методе _initializeScene(): мы просто заменим зеленый куб нашим космическим кораблем.

Создание тела (и создание конкретной подиерархии)

Начнем с корпуса корабля. Чтобы создать эту сетку, мы будем использовать геометрию тетраэдра Three.js.

Но!

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

Когда вы работаете с пользовательской геометрией и строите свою форму вручную, разработчику может быть немного сложнее использовать буферную геометрию, но здесь мы просто используем базовые примитивы, поэтому они явно лучший выбор ;)

Итак, вместо использования геометрии тетраэдра давайте на самом деле используем геометрию буфера тетраэдра — и передаем наш параметр радиуса, который устанавливает размер объекта. Мы также определим базовый материал светло-серого цвета, чтобы получить сетку корпуса нашего корабля:

Теперь вы можете подумать, что мы должны добавить этот объект на сцену, как мы сделали это раньше с нашим кубом. И да, мы делаем…

Но дело в том, что вместо того, чтобы добавлять это тело корабля напрямую в сцену, мы на самом деле собираемся создать виртуальный якорь для 7 частей нашего корабля. Таким образом, сам корабль будет не 3D-объектом, а 3D-группой (THREE.Group), это будет коллекция нескольких дочерних элементов, которые все упакованы вместе в этой конкретной подиерархии в нашей сцене.

Это позволит нам трансформировать 7 объектов, как если бы они были одним целым, поэтому мы сможем легко перемещать, вращать или масштабировать весь корабль одновременно!

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

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

На данный момент мы оставим камеру как есть, что дает нам следующий код для нашего метода _initializeScene():

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

Так что давайте просто избавимся от этой переменной this.cube и пока ничего не будем делать в нашем методе update():

В этот момент мы можем сохранить, и мы видим, что у нас есть небольшой объект в нашей сцене: это корпус нашего корабля! Но, будем честными: пока не очень…

Исправление некоторых переводов и поворотов

Проблема в том, что этот корпус корабля неправильно повернут: мы хотим видеть его сзади. Это означает, что мы должны повернуть его на 45 градусов вокруг осей X и Y, что можно сделать с помощью функций .rotateX() и .rotateY().

Однако нам нужно быть осторожными, потому что углы, которые мы здесь приводим, должны быть преобразованы в радианы — поэтому мы просто умножим на число пи и разделим на 180, чтобы получить эквивалентные углы в радианах:

Теперь мы видим, что наш корабль правильно повернут:

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

В порядке! Теперь у нас есть хороший вид сзади на корпус нашего корабля :)

Обратите внимание, что в Hyperspeed я добавил небольшую дополнительную часть в режим каркаса, чтобы отметить края сетки — мы поработаем над этим в одном из последних эпизодов этой серии, но вы можете уже попробуйте, если хотите!

Ставим реакторы!

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

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

В нашем случае это особенно полезно, потому что все гнезда реактора и все реактивные источники света имеют одинаковую геометрию и материалы:

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

Давайте начнем с сокетов и создадим 2 переменные: reactorSocketGeometry и reactorSocketMaterial для совместного использования между нашими 3 объектами. Геометрия будет использовать геометрию цилиндрического буфера с некоторыми пользовательскими параметрами, а материал будет базовым материалом среднего серого цвета:

Теперь мы можем использовать эти две переменные для создания 3 мешей для сокетов реактора нашего корабля, и мы можем добавить их в группу кораблей, которую мы создали ранее:

Но если вы сохраните это, вы ничего не увидите — потому что сейчас розетки фактически находятся внутри корпуса корабля, в исходной точке!

Итак, снова давайте установим их положение и поворот:

Очевидно, что эти значения зависят от формы вашего корабля, и вам, возможно, придется настроить их в зависимости от вашего собственного дизайна…

Если мы сохранимся, мы увидим, что у нас есть 3 гнезда реактора позади нашего корабля! :)

Источники света очень похожи: нам просто нужно создать другую геометрию цилиндра, немного меньшего размера, и базовый светло-голубой материал:

Затем мы можем создать еще один набор объектов, добавить их на корабль и задать их положения и повороты:

Сохраните это, и вы получите наш маленький космический корабль в своей 3D-сцене, готовый к полету по сетке! ;)

Вывод

Сегодняшний эпизод был немного короче, но мы позаботились о нескольких вещах: нам удалось перенести всю нашу логику инициализации 3D-сцены в класс Game, и мы изучили геометрию Three.js и геометрию буфера. инструменты для создания модели нашего космического корабля.

В следующий раз мы увидим, как добавить бесконечную сетку в нашу сцену и анимировать ее так, чтобы казалось, что мы движемся вперед бесконечно. strong> Мы немного поговорим о шейдерах и о том, как они могут помочь вам оптимизировать множество визуальных эффектов в вашей игре…

⬅️ Учебник № 2. Создание игрового класса| TOC |Урок №4: Создание бесконечной плоскости с помощью шейдера ➡️

Если вам понравилась эта статья, вы можете найти другие записи в блоге о технологиях, искусственном интеллекте и программировании на мой сайт :)