Какую игру ни делай, а в итоге все равно получится ГТА. Каждый школьник мечтает создать свой клон ГТА. ГТА всему голова. Без труда не пройдешь и ГТА. Ой, что это я? Короче говоря, я делал игры и в какой-то момент осознал, что достиг дзена, и теперь настала пора и мне тоже написать свой вариант той самой исходной игры, игры-прародительницы всех игр, игры-протовселенной, канонической игры, а именно игры про езду на тачке в открытом мире. Каждый мужчина должен посадить дом, родить дерево и создать свой клон ГТА. Э-э... Ладно. Нет, конечно, GTA - это не только про тачку. Позже добавим и ходьбу, и копов, и плоские шуточки, хотя, последнее я, кажется - уже. Похоже, что сейчас моя игра, скорее, ближе к Need For Speed: в ней уже можно гонять по городу, но еще нельзя выходить из машины, да и пешеходов пока нет. Зато есть открытый мир. Ничего, скоро доведем этот NFS до состояния полного GTA. Тут мне подумалось, что все игры - это одна и та же игра, но с разными урезанными возможностями. Это как в случае со скульптором, который просто отсекает все лишнее... Короче, вы поняли, я философ.

Я расскажу вам о том, как я создал довольно большую локацию, содержащую более 20 000 объектов (это еще не предел), с физической моделью, при этом сохраняющую неплохую производительность в браузерах, в том числе мобильных. Будет интересно, не переключайтесь.

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

Мои претензии к автогонкам

Итак, что же мне не нравится в существующих играх в разрезе механики управления автомобилем. Ниже - сугубо мое личное мнение. И с ним, естественно, можно не соглашаться.

1. Вид не от первого лица

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

И в пику NFS, где когда-то убрали вид от первого лица, я в своей игре решил убрать вид от третьего. Барабанная дробь. Запомните этот исторический момент. Пусть это будет первая в мире гонка без вида от третьего лица. Я лишь сделал переключение камеры между тремя вариантами вида от руля с немного разными углами обзора на дорогу. А если кому-то очень хочется полюбоваться экстерьером автомобиля, так пусть идет в гараж и любуется им там! Ой, гаража пока еще нет, но он будет, когда я реализую выбор машины перед началом игры. Будет ли это гараж с блэкджеком и пивом или просто обычный гараж, я еще подумаю. Но во время поездки надо смотреть на трассу!

Кстати, есть пара претензий и к тем играм, где вид от первого лица все же присутствует. Во-первых, это слишком низкая посадка за рулем, связи с чем трассу становится видно лишь на некотором отдалении, а не прямо перед машиной. А на горке перед спуском трассу в какой-то момент может быть не видно совсем. Как можно так играть, не понимаю? Более того, когда взгляд фокусируется на дороге слишком далеко, то скорость кажется более низкой. Во-вторых - затемненное стекло. Типа, давайте, покажем реалистичное лобовое - затемним его на 30%, будет круто. Ага, в реальных авто всегда тонируют лобовуху, чтобы было плохо видно дорогу. В результате при игре от первого лица теряется половина красок окружающего мира и играть с такого ракурса как-то не тянет: быстро надоедает. То есть, такой вид - это, скорее, перфоманс, ну или вещь для галочки, а не игровое пространство. Нет, стекло должно быть прозрачным. В своей игре я реализовал переключение между тремя видами от руля и только в одном из них слегка, почти незаметно, затемнил лобовое стекло. И мне это уже не нравится. Не знаю, может быть, уберу это. Или сделаю четвертый вид, такой же, но как вариант с затемнением.

2. Слишком широкие дорожные полосы

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

NFS-3. Любишь вид от третьего лица - получи нереально широкие полосы движения
NFS-3. Любишь вид от третьего лица - получи нереально широкие полосы движения

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

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

3. Высокая скорость

Видимо, как следствие того, что у нас теперь нереально широкие полосы движения, будем увеличивать скорость машины до околосветовой, иначе ездить по таким полосам станет ну совсем уж не интересно - как по взлетной полосе. По-моему, из всей серии NFS только в первой лучше всего ощущалась скорость: просто она там была не слишком высокой. Да, увеличим скорость, чтобы ехать было сложнее, но, вот незадача - на такой скорости теперь даже в широкие полосы вписываться становится трудно... Сделаем их еще шире? Какой-то замкнутый круг. А можно было просто играть от первого лица. В общем, максимальная скорость в игре не должна быть слишком большой.

4. Утюг на льду

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

5. Кольцевые трассы, линейные трассы

Я не любитель замкнутых трасс. Кататься по кольцу на гонках в симуляторе - это нормально. Но, если сеттинг представлен дорогой общего пользования с трафиком, то кольцевая трасса тут выглядит довольно странно. По дороге общего пользования я хочу ехать вперед с целью куда-нибудь приехать. К далекому желанному финишу, к призу, к райскому уголку или как-то так. Должен быть стимул добраться докуда-то. Это некий дополнительный интерес в игре, дополнительные эмоции. Есть идея в следующей игре реализовать трассу с разными климатическими условиями: из снежной бури приезжаешь к финишу на жарком пляже. Или можно - наоборот, по желанию.

6. Стрелочки

Стрелочки в воздухе, а также, на дорожном покрытии - это ужас. Вообще, когда на более-менее реалистичное окружение накладываются подсказки в форме эффекта «дополненной реальности», то в моих глазах это сразу превращает игру в несерьезную мобильную «кликалку» со «свистоперделками» и убивает реализм. Да, знаю, что это применяется во всех современных автогонках. А мне не нравится, поэтому я в них и не играю. Мы имеем дополненную реальность в и так нереальном мире (игровом, виртуальном)? Что? Ну и бред. Я допускаю стрелочки в форме дорожных знаков-указателей направления движения. Но только не в виде светящихся объектов, просто висящих в пространстве. Дизайнерам было лень даже подумать об иммерсивности? Ну а неоновые стрелочки на асфальте - это вообще ужас. Где вы такое видели в жизни? По-моему, в Южной Корее применяется подсветка, вмонтированная в дорожное покрытие на пешеходных переходах для тех, кто ожидают зеленого сигнала светофора, уткнувшись в экран мобильного телефона. Светофор на земле. А, в целом, конечно, дизайн трассы должен быть таким, чтобы было и так понятно, куда ехать. Нельзя ограничивать движение стрелочками, невидимыми стенами и прочим непотребством. Да и зачем тогда вообще делать открытый мир? Чтобы его закрывать?

7. Анимация рук водителя

Казалось бы, руки на руле - это хорошо, это добавляет реализма. Но, если взять, например, игру «Driver: San-Francisco», то там прослеживается такой эффект, при котором дерганье рук в стороны сбивает с толку относительно направления движения машины. Это раздражитель, который заставляет суетиться и вилять влево-вправо в попытках как бы компенсировать эти движения рук. Мне сложно это объяснить, но попробуйте просто закрыть ладонью нижнюю часть экрана так, чтобы не видеть руль и руки - и водить станет намного проще. Начинаешь легче вписываться в повороты. Поэтому руки на руле не нужны - достаточно только вращения самого рулевого колеса. Либо руки не должны быть такими контрастными и заметными. Пусть они сливаются с фоном. Короче, руки на руле в играх почти всегда реализованы отвратительно и портят игровую механику вождения.

Driver: San-Francisco. Сложно управлять от первого лица: эти дерганые движения рук сильно сбивают с толку
Driver: San-Francisco. Сложно управлять от первого лица: эти дерганые движения рук сильно сбивают с толку

8. Чрезмерная кастомизация автомобиля или тюнинг

Я понимаю, что есть большие любители данного процесса, которые могут 90% игрового времени проводить в гараже. Но ведь для этого существуют симуляторы автомеханика. Я считаю, что в игре про гонки кастомизация авто не нужна совсем. Достаточно только выбора самой машины. Я до сих пор помню эти синий Додж и черный Ламборгини из первой части NFS. Уже забыл, можно ли там было менять их цвета, но я даже и не пытался. Те машины были харизматичными. Если бы их можно было глубоко кастомизировать, то они, скорее всего, не отпечатались бы в моей памяти так сильно. Я в детстве не разбирался в машинах и садился играть в данную игру с мыслью типа: «Хочу погонять на той черной спортивной тачке». А оттого, что она была черная, казалось, что она и едет как-то особенно, как-то круто что ли. Машины должны быть с характером, их нельзя давать кастомизировать игроку, а особенно - менять цвет. Вот, какая есть в гараже, на той и езжай. И нечего перед каждой гонкой ее перекрашивать. В реальном мире же не будешь этим заниматься. Стучит где-то в двигателе - и пусть стучит. Зато это будет запоминающейся особенностью данного автомобиля. А еще машин должно быть мало, чтобы запомнилась каждая. А миллион автомобилей на выбор, которыми хвастаются разные крутые игры, мне напоминает гонку за количеством мегапикселей среди производителей цифровых камер - абсолютно бессмысленно.

9. Сюжет

Кому-то скучно просто ехать, и хочется еще и какого-то сюжета, может быть, с длинной предысторией и социальной составляющей, завязанной на взаимодействии между персонажами. Но, слушайте, так это гонка или РПГ? Зачем смешивать эти два жанра? Мне всегда было неинтересно в гонках даже проходить карьеру. А особенно бесило то, что я не мог выбрать любую трассу: я хочу прокатиться по зимней - а нет, она закрыта, сначала пройди осеннюю. Нет, я просто хотел погонять с другими соперниками, выбрав трассу по своему настроению... Что же касается финала, то максимум, что мне нужно, это таблица рекордов с указанием того, какое я занял место на данной трассе, и, может быть, еще время моего заезда. Всё. Я считаю, что сюжет в гонках не нужен. Просто садись в тачку - и дави на газ. Даже карьера не нужна. Незачем разбираться с этими нюансами попадания в какие-то полуфиналы и финалы. Фан игры в гонки - в том, чтобы просто весело гонять, а не читать длинные правила и таблицы. Это же гонки, а не симулятор менеджера гоночной команды.

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

Существуют люди, которые относятся к играм с точки зрения прохождения этапов прокачки. И им, вероятно, даже не так важно, что это за игра - гонки, шутер или рогалик. Удовольствие доставляет само продвижение вверх по некой игровой карьерной лестнице. По крайней мере, эту возможность они воспринимают как обязательную, и без нее играть не интересно. Можно сравнить с бизнесменом, которому не так важно, каким заниматься делом - торговать помидорами, нефтью или держать сеть салонов красоты. Главное - чтобы зарабатывались деньги. Не имею ничего против, но я не отношусь к такому типу людей. Для меня гонки должны быть про гонки, а не про карьеру. Впрочем, сложно сказать где проходит грань. Является ли карьера частью гонок? Если нет, то является ли победа на отдельной трассе частью гонок? А это, очевидно, да...

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

10. Последним пунктом выделю сразу несколько мелких неудобств

Слишком чувствительное или слишком тугое управление: иногда складывается такое впечатление, что авторы игры сами не играли в свое творение.

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

Слишком короткие расстояния между поворотами или светофорами (как в GTA V в черте города - я понимаю, что это не автосимулятор, но как пример плохой локации для гонок). Не успел разогнаться - и уже надо тормозить перед следующим перекрестком. Не интересно. В этом смысле в первой части NFS между кривыми располагались куда более длинные и динамичные скоростные участки, на которых можно было вдоволь наиграться в «шашечки».

Фокусное расстояние камеры. Если камера в игре настроена с большим «зумом», то движение ощущается как намного более медленное. Зато становится уже угол обзора на дорогу, что позволяет легче вписываться в полосы движения. То есть, полосы визуально становятся шире. По мне так лучше маленький зум и игра от первого лица, где угол будет и так достаточно узким, а видимая полоса движения широкая.

Так о чем игра-то?

Я так увлекся размышлениями об автогонках, что забыл, что делаю GTA, а эта игра, как известно, совсем не про гонки. Но это шутка, на самом деле. В действительности, у меня нет цели создать клон GTA по геймплею, по крайней мере. Моя игра, скорее, все-таки, про вождение. (Помним про скульптора, который просто отсекает... Ладно.) У меня есть открытый мир с возможностью менять машину. Будет также возможность перемещаться на небольшие расстояния пешком. Чисто внешне это напоминает GTA. По форме, но не по содержанию. В дальнейшем на этой базе я планирую написать коридорные гонки с длинными трассами без открытого мира, но с небольшими ветвлениями. Но это уже другая история. А данную игру я назвал «Капиталист». Здесь нужно ездить и собирать монеты, купюры и кредитные карты. Цель - набрать всего этого на миллион. За вами будет гоняться полиция и пытаться вас оштрафовать. Если вас настигают копы, то они забирают у вас все наличные. Поэтому деньги нужно периодически свозить в свой банк, расположенный в городке Булл Таун. Метафора: в большом городе можно легко стать миллионером, так как там заработки высокие, но, однако, они не совсем законные. На данном этапе разработки такая незамысловатая завязка позволит создать какую-то законченную игру. Позже я буду добавлять человечков, не слишком замысловатые миссии и так далее. Придам домам больше объема, а то сейчас они представляют собой просто коробки. Новая игра будет называться по-другому. А сейчас это такая мини-игра в локации некой будущей большой и более красивой игры. Демо-уровень, так сказать.

Успешный успех

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

Теперь перейдем к техническим моментам

Далее я опишу различные задачи оптимизации, которые мне предстояло решить для достижения максимальной производительности и минимального размера игры в мегабайтах, а также, сложности, с которыми я столкнулся при использовании Three.js для вывода трехмерной графики и Cannon.js в качестве физического движка.

Генератор города

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

Greeble. Довольно простой инструмент, плагин к Blender или 3DS Max. Здесь все сводится к тому, чтобы рисовать улицы на плоскости вручную. А потом скрипт построит здания. Однако нужно очень хорошо знать 3D-редактор, чтобы сделать дороги изогнутыми и, при этом, остающимися равномерными по ширине, а также, придется вручную размечать тротуары и так далее. Мне этот плагин показался недостаточно автоматизированным.

CityEngine. Тяжелый софт, создающий тяжелый город. Я его попробовал и сразу снес. Уверен, это прекрасный инструмент, но я просто не смог с ним разобраться. На выбор предлагается почему-то всего пара-тройка вариантов города, причем, я не нашел, как задать произвольный размер карты. Сам город получается очень тяжелым и требует серьезного редактирования. Сразу после генерирования он в моей игре выдавал 3 кадра в секунду. Вероятно, там нужно вручную оптимизировать геометрию, объединять материалы и так далее. В общем, я от него отказался. Скорее всего, это инструмент создания города для какого-нибудь рендера, а не для игры.

GhostTown. Вот этот мне подошел. GT старых версий умеет строить изогнутые улицы, но не позволяет создавать простые дома-коробки: геометрия зачем-то разбивается по этажам. А это лишние полигоны, ведущие к сильной просадке производительности в игре. Если у вас 50-этажный небоскреб и каждый этаж - это отдельные полигоны... Полигоны приходилось объединять вручную. Зато GT последней версии позволяет строить требуемые здания. Однако не умеет делать изогнутые улицы. У него улицы почему-то всегда представляют собой прямые. Тогда я решил сгенерировать саму карту в старой версии, а дома построить поквартально в новой - благо там можно указывать отдельные участки (меши), на которых должны появляться здания. Задавая настройки этажности и размеры зданий для каждого квартала в отдельности, я потихоньку создал более-менее вменяемый город. И все равно ни одна из этих версий не умеет корректно накладывать текстуры ни на здания, ни на дороги. Поэтому я генерировал чисто геометрию и текстурировал ее потом вручную... Та еще канитель. Ну и стык дороги с тротуаром создается как-то криво, поэтому во многих местах его приходилось подтягивать, чтобы не было дыр. Стоковые текстуры имеют размер не кратный двойке и не подходят для игр. Автор явно не из сферы геймдева. Но я и не стал использовать стоковые, а сделал свои, а также, скачал бесплатных, ну и что-то по мелочи прикупил. Пришлось везде менять текстурную развертку.

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

Первый результат, полученный при помощи Greeble
Первый результат, полученный при помощи Greeble

А это уже город, полученный с помощью GhostTown последней версии. Неплохо. Но прямые улицы меня не устроили. К тому же, он лепит много полигонов на дороги и тротуары. Но зачем, если все они прямые? От этого падает производительность. Можно было просто замостить текстурой. В итоге этот город тоже отправился в утиль.

GhostTown последней версии
GhostTown последней версии

Ну а актуальный город вы можете видеть на всех остальных скриншотах из моей игры.

Коллайдеры

Ездить по городу - это хорошо, но проходить сквозь стены - не очень: это вторжение на частную собственность и в личную жизнь, что, как вы понимаете, чревато. Первое - справедливым судом, а второе - веселым мордобоем. Но, поскольку мы пишем не Mortal Kombat, то нам это не нужно. Может быть, как-нибудь в будущем: вломился в квартиру - и начинается файтинг с использованием подручных средств - сковородок, ножей... Гибрид NFS, GTA и MK, супер-игра обо всем сразу. Кстати, в моей игре нужно собирать разбросанные по городу деньги, убегая от полиции, поэтому она еще похожа и на Pac-Man... На данном этапе я, как великий творец, просто гениально отсекаю эту идею. Столкновения с препятствиями можно реализовать множеством различных способов. Я постарался выбрать такой, чтобы он оказывал максимально щадящее влияние на производительность. Для обработки столкновений со стенами я взял физический движок Cannon.js.

Сначала нужно подготовить коллайдеры. Я решил отказаться от использования самих стен домов в их качестве: ведь, стены могут состоять из большого количества полигонов, тем более, в будущем я, как раз, планирую это самое количество увеличить, чтобы немного разнообразить архитектуру зданий и затекстурировать их не одной повторяющейся текстурой, а двумя-тремя, преимущественно на первых этажах. Дело в том, что для одной текстуры достаточно одной плоскости из двух треугольников, а для разнообразных нужно бить стены на гораздо большее количество элементов. Поэтому в качестве колладйеров я взял простые низкополигонные коробки, повторяющие здания. Кроме того, я удалил с них полы и потолки, потому что мне нужны только сами стены. Так физический движок будет меньше напрягаться, рассчитывая столкновения. Я сохранил эти коробки в отдельном файле. Они будут загружаться в игру, но станут прозрачными, невидимыми.

Коллайдеры зданий четырех из семи районов
Коллайдеры зданий четырех из семи районов

И это еще только здания четырех из семи районов - около 3000 коллайдеров. А есть еще тысячи фонарных столбов и тысячи деревьев. О них - позже. Коллайдеры большого размера здесь - это не препятствия, а маяки для включения анимации воды в водоемах, которые где-то там, в городе, находятся в центрах этих коллайдеров: воду незачем анимировать, если ее не видно за домами.

Эти коллайдеры зданий загружаются в игру из файлов формата json (сохраненных из 3D-редактора), их всего 7 штук, то есть, по семи районам города. Каждый файл весит 1-2 Мб. Затем элементы их массива children (то есть, уже отдельные здания) помещаются в большой массив colliders в виде элементов:

colliders.push({
    t: 'walls',
    v: colliderVisual,
    o: colliderBody,
    added: false,
    bounds: bounds,
    p: {
        x:colliderBody.position.x,
        y:colliderBody.position.y,
        z:colliderBody.position.z
    }
});

colliderVisual - это, собственно, визуальная модель коллайдера одного здания (один из children всей загруженной 3D-модели), это будет работать в пространстве Three.js

colliderBody - это физическая модель в пространстве Cannon.js, создаваемая из загруженной визуальной модели Three.js. Как это делается:

var scale = 1;
var colliderGeometry = colliders[i];
var colliderGeomData = getVertsFaces(colliderGeometry, scale);
var colliderVerts = colliderGeomData.verts;
var colliderFaces = colliderGeomData.faces;	
var colliderBody = new CANNON.Body({mass: 0});
colliderBody.addShape(new CANNON.Trimesh(colliderVerts, colliderFaces));

// процессы создания физической модели визуальных объектов из файлов форматов json и fbx немного различаются

getVertsFaces = function(geometry,sc) {
    var res = {verts:[], faces:[]};

    var terrainVerts = [];
    var terrainFaces = [];

    if (geometry.index === null) {
        // если вы сохранили коллайдеры в формате fbx

        var positionArray = geometry.attributes.position.array;
        var indexArrayO=THREE.BufferGeometryUtils.mergeVertices(geometry);
        var indexArray = indexArrayO.index.array;
        var vertexCount = positionArray.length / 3;
        var faceCount = indexArray.length / 3;
        for (var i = 0; i < vertexCount; i++) {
            var x = positionArray[i * 3];
            var y = positionArray[i * 3 + 1];
            var z = positionArray[i * 3 + 2];
            terrainVerts.push(sc*x);
            terrainVerts.push(sc*y);
            terrainVerts.push(sc*z);
        };
        for (var i = 0; i < faceCount; i++) {
            var a = indexArray[i * 3];
            var b = indexArray[i * 3 + 1];
            var c = indexArray[i * 3 + 2];
            terrainFaces.push(a);
            terrainFaces.push(b);
            terrainFaces.push(c);
        };
		
    } else {
        // если вы сохранили коллайдеры в форматах json, glb

        for (var i = 0; i < geometry.vertices.length; i++) {
            terrainVerts.push(sc*geometry.vertices[i].x);
            terrainVerts.push(sc*geometry.vertices[i].y);
            terrainVerts.push(sc*geometry.vertices[i].z);
        };
        for (var i = 0; i < geometry.faces.length; i++) {
            terrainFaces.push(geometry.faces[i].a);
            terrainFaces.push(geometry.faces[i].b);
            terrainFaces.push(geometry.faces[i].c);
        };

    };
	
    res.verts = terrainVerts;
    res.faces = terrainFaces;
	
    return res;
};

added - флаг того, добавлен ли данный коллайдер в данный момент времени в физический движок, будет использоваться уже во время игрового процесса

p - координаты колладйера, честно говоря, зря продублировал, можно их было просто взять из colliderBody.position, но так было удобнее

bounds - это границы коллайдера, ориентированные по осям координат, иначе говоря, boundingBox коллайдера (mesh), создаваемый средствами. Three.js:

var helper = new THREE.BoxHelper(mesh);
helper.geometry.computeBoundingBox();
var b = helper.geometry.boundingBox;
var d = 10;
b.min.x-=d; b.max.x+=d; b.min.z-=d; b.max.z+=d;

Самое важное! Bounds (границы) строятся вокруг каждого коллайдера в форме боксов, стороны которых ориентированы по осям координат. Причем я беру только данные, а сама модель - как визуальная, так и физическая - конкретно от этих боксов мне не нужна. В этом весь смысл: в процессе игры не нужно будет проверять рейкастингом весь огромный массив коллайдеров сцены. По данным границам путем простой проверки координат игрока на нахождение внутри (больше min и меньше max по всем осям) будет производиться включение коллайдера, то есть добавление colliderBody в физическую модель Cannon.js. Вот так можно представить это схематически:

Почему бы просто не делать рейкастинг со всеми моделями зданий по всему массиву? При десятках тысяч объектов это накладно: гораздо быстрее проверять на вхождение в границы тупо по координате, да и, к тому же, рейкастинг требует наличия всех проверяемых объектов всегда на сцене, а координаты - это просто массив данных. Далее. Зачем расширять границы на некое d? А чтобы коллайдер здания добавлялся в физическую модель на некотором расстоянии нахождения игрока возле здания, а не в момент касания. Иначе физический движок не успеет среагировать на столкновение; d подбирается экспериментально. Слишком малое значение приведет к тому, что движок не успеет отреагировать на столкновение, а слишком большое - к большому количеству активных коллайдеров в момент времени, что скажется на производительности. В итоге на сцене всегда включено всего 3-5 коллайдеров вокруг игрока. Ну, может быть, в какие-то моменты до 10. Но не десятки тысяч.

Подсветка активных коллайдеров - для примера
Подсветка активных коллайдеров - для примера

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

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

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

Подытожив, можно сказать, что коллайдеры всех многочисленных объектов добавляются и удаляются из физического движка Cannon.js вокруг игрока по мере его движения. Проверка происходит по массиву данных границ (bounds) путем простого сравнения координат. А уже для добавленных коллайдеров, коих всегда оказывается не более 3-5 штук, работает рейкастинг и физическая модель столкновений. Таким образом на сцену можно добавить практически неограниченное количество объектов. Если же возникнет проблема со скоростью перебора массива, то его можно разбить, например, на некие подмассивы по регионам.

Жажда скорости

На низких скоростях движения автомобиля (примерно до 120 км/ч) коллайдеры работают отлично. Но не плестись же, как черепаха: жажду скорости никто не отменял. И вот на более высоких скоростях довольно часто случались пролеты сквозь препятствия. Если подумать, то есть способы, как этого избежать:

1. Уменьшить скорость.

Ну, сами понимаете... Нет. Здесь вспоминается мем с Денни Де Вито. 

2. Увеличить число итераций обработки столкновений в физическом движке.

Тоже нет. Это требует больше вычислительных мощностей. Да и, как показала практика, все равно не страхует на 100% от прохождения сквозь стены: они нет-нет, да случаются.

3. Ничего не делать, но устранить последствия.

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

3.1. При помощи рейкастинга физического движка Cannon.js.

К сожалению, функция испускания луча в Cannon.js выдает только самое первое пересечение. А смысл рейкастинга состоит в том, чтобы испустив луч от позиции машины игрока в любую сторону, подсчитать количества его пересечений с каждым активным боксом-коллайдером. Если луч пересек только одну стенку коллайдера, значит, машина находится внутри. Если луч пересек две стены либо ни одной, значит, машина находится снаружи.

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

3.2. При помощи рейкастинга визуального движка Three.js.

Про некоторой информации из Интернета, рейкастинг в Three.js работает быстрее, чем в Cannon.js. Я не проверял, но что точно, так это то, что он работает стабильнее и выдает все пересечения луча со всеми активными коллайдерами. Здесь проблем вообще не возникло, и я взял этот вариант.

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

4. Устранить причину проблемы: снизить скорость перед столкновением.

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

4.1. Поиск расстояния до стен.

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

Cannon.js действительно вычисляет расстояние до стен. Однако, как я уже упомянул, ввиду какой-то не очень понятной мне и нестабильной работы его рейкастинга, я отказался от этой идеи.

Three.js, кажется, тоже может вычислять расстояние до стенок коллайдера, но на данном этапе я решил, что это пока не приоритетная задача. А так, да, можно совмещать телепортацию игрока из метода 3.2 с данным вариантом снижения скорости перед препятствиями. Должно выглядеть реалистично.

Как еще можно снизить скорость перед столкновением?

4.2. Дополнительные коллайдеры большего размера вокруг препятствий

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

4.3. Коллайдеры, которые ограничивают дорогу

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

4.4. Дорога-коллайдер

Я имею в виду - взять дорогу в 3D-редакторе и выдавить по ее контуру форму вверх. То есть, создать на ней некий тоннель, который будет использоваться в качестве коллайдера. И здесь уже вести проверку не на отсутствие внутри него машины игрока, а, наоборот, на наличие. То есть, это будет не black-, а white-лист, так сказать. Но, во-первых, это тоже дополнительный коллайдер. А, во-вторых, не уверен, что это хорошая идея - принудительно сбрасывать скорость либо вообще делать полную остановку всегда, когда съезжаешь с дороги. Такое широко практиковалось в старых играх. Впрочем, вариант с небольшим снижением скорости вне дороги не так уж и плох... Но, опять же, тогда придется полагаться только на события столкновений в физическом движке, а они там срабатывают не в 100% случаев. Вероятно, ограничение скорости вне дороги можно было бы использовать как дополнительную фичу к событиям столкновения в физическом движке и рейкастингу (с телепортацией) в визуальном движке. Еще думаю над этим. В любом случае, красоту в деталях будем наводить позже.

Размер дистрибутива

Игра загружается в браузер. И для сокращения времени загрузки она должна «весить» как можно меньше. В итоге я уложился в 40 мегабайт. Что я для этого сделал.

Текстуры. Первый очевидный шаг - использовать повторяющиеся текстуры для стен зданий. Первые этажи можно замостить другой текстурой - с витринами. Я использовал максимально простую геометрию - буквально пара треугольников на каждую стену. И две текстуры на здание. Одной из них я мостил стены выше первого этажа, эта текстура тайлится (повторяется) во все стороны. А вторая текстура тайлится только по горизонтали и представляет собой текстурный атлас, в него вместились 4 варианта первых этажей. Зачем пихать в него 4 первых этажа? Ну, у 4-х разных зданий будет общая текстура первого этажа, а значит, один материал. А чем меньше материалов на сцене, тем лучше производительность. Кроме того, текстура должна быть квадратная, поэтому все равно придется ее чем-то заполнить. Текстуры же верхних этажей поместить в единый атлас не представляется возможным, поскольку они должны тайлиться во все 4 стороны.

Мощение стен текстурами на примере одного из зданий
Мощение стен текстурами на примере одного из зданий

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

var matPlants=null;
function loadPlantCallback(obj) {					
	if (matPlants===null) {
		matPlants=obj.material;
	} else {
		obj.material=matPlants;
	};
};

Одинаковые объекты. И да, я загружал по одному экземпляру каждого объекта - дерева, например, коих всего 6 видов. А потом просто клонировал их функцией .clone(). 6 деревьев с текстурой «весят» всего 686 Кб. И из этих шести клонируются тысячи и расставляются по карте с применением одного и того же материала. Тогда как все предварительно расставленные объекты (и это только деревья), то есть, весь лес, при выгрузке из редактора в одном файле весил бы 108 Мб. И загружать такой объем в игру было бы недопустимо.

Как я заготовил карту объектов (фонарных столбов и деревьев). Собственно, я расставил все объекты на территории в 3D редакторе, а потом выгрузил их в fbx-файл. Я написал простой скрипт, который считал этот файл, вытащил из него координаты, углы поворота и масштабы всех однотипных объектов и поместил эту информацию в массив данных. Это происходит один раз, это заготовка. А по данному массиву (например, для деревьев это 115 Кб данных) уже в игре и расставляются объекты. То есть, загружается по одному экземпляру объекта каждого вида, а затем происходит их клонирование по позициям и углам в соответствии с массивом данных, который уже предопределен в игре как константа. Таким образом, из 108 Мб деревьев получается менее 1 мегабайта: 686 Кб (шесть моделей с текстурой) плюс 115 Кб данных (с координатами, углами вращения и масштабами) в массиве. Ну а большой 108-мегабайтный файл больше не нужен. С фонарными столбами аналогично - выгруженные из редактора они «весят» 32 Мб, а затем превращаются в модель одного столба - 300 Кб + массив данных по их координатам и углам поворота 50 Кб.

Расстановка клонированных объектов. Из всего вышесказанного вытекает одна проблема - тысячи однотипных объектов начинают долго клонироваться и расставляться по игровому миру сразу после загрузки игры - примерно 3 минуты на моем компьютере. Я решил вместо экрана ожидания поставить внизу информационную панель с отображением хода загрузки мелких объектов. Да, это может выглядеть некрасиво, когда у тебя перед глазами появляется дерево или столб. Но я отсортировал массив данных по расстоянию удаления объектов от точки старта игрока. При желании, конечно, можно нагнать появляющиеся столбы и деревья, если начать быстро двигаться в каком-нибудь одном направлении. Что ж, это недостаток. Кстати, коллайдеры деревьев и столбов определяются чуть ранее - они загружаются из файлов коллайдеров вместе с локацией. Поэтому оказаться внутри какого-нибудь объекта все равно невозможно: можно врезаться в пустое пространство, где чуть позже появится объект. Я разбил объекты на важные и второстепенные. Сначала расставляются первые, а затем вторые. Последние - это деревья в глубине лесных массивов, куда так сразу не поедешь. Деревья же, стоящие вдоль дорог так же, как и, например, фонарные столбы, отнесены к важным и загружаются в первую очередь.

Формат моделей. Наконец, все 3D-модели я выгружал из Блендера в формате fbx. Он обеспечивает довольно компактный размер контента. А коллайдеры выгружал в формате json.

Производительность

Про объединение материалов и супер-легкую геометрию я уже рассказал - а это все влияет и на производительность тоже.

Еще один момент - это дальность прорисовки объектов. Имея на сцене более 10 000 моделей, конечно, ограничиваешь радиус их прорисовки. Здания ограничены настройкой общей дальностью видимости в игре, а мелкие объекты прорисовываются чуть ближе. Когда это происходит на фоне зданий, то это не так заметно.

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

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

Низкая общая дальность обзора камеры и высокая дистанция прорисовки объектов слева и наоборот - справа
Низкая общая дальность обзора камеры и высокая дистанция прорисовки объектов слева и наоборот - справа

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

Еще один пример
Еще один пример

На самом деле, можно найти приемлемую комбинацию этих двух параметров, при которой у вас будут и ближайшие здания отображаться полностью, и исчезновение удаленных объектов не будет особо заметно. Но самое главное - что в результате всего этого большинство отдаленных объектов сцены скрывается (делается невидимыми или visible=false) и перестает обрабатываться визуальным движком Three.js. С этим и связано общее увеличение производительности.

Еще я рекомендую использовать браузер на основе Хромиум и включить максимальную производительность в настройках питания.

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

Коллайдеры. Как я уже упомянул ранее, невидимые коллайдеры Three.js включаются вокруг объектов вблизи игрока, чтобы застраховать его от прохождения сквозь стены. Этим коллайдерам я отключил свойства castShadow и receiveShadow, чтобы они не участвовали в расчетах теней, а также, для их материалов отключил свойства fog, depthTest и depthWrite, то есть участие в расчете тумана и в сортировке отображения объектов на сцене по удалению от камеры - ведь они все равно не видимы.

Но самое интересное, что мне помогло существенно уменьшить «тормоза», это, как я уже упомянул, исключение из просчета мира невидимых в данный момент объектов. По умолчанию Three.js просчитывает матрицы всех, как видимых, так и невидимых, объектов (параметр visible=false) на случай, если разработчики используют последние на сцене в качестве коллайдеров. Я пошел на хитрость. Да, коллайдеры, конечно, должны быть невидимыми, но ведь можно добиться этого другим способом: оставить их параметр visible в true, но убрать свойство opacity материала в ноль - и они станут невидимыми, а, точнее, видимыми, но прозрачными. А по-настоящему невидимые можно не просчитывать совсем. Видимыми, но прозрачными (visible=true, opacity=0) можно делать только активные (ближайшие к игроку) коллайдеры. И тогда просчитываться движком могли бы только они, а не все скрытые объекты сцены вообще. Я задался этим вопросом, но сначала пошел с ним в Интернет, где нашел уже готовый скрипт, который как раз патчит движок нужным образом, и невидимые объекты перестают обрабатываться.

(function () {
  let _updateMatrixWorld = THREE.Object3D.prototype.updateMatrixWorld
  THREE.Object3D.prototype.updateMatrixWorld = function () {
	if (!this.visible) {
	  return
	}
	_updateMatrixWorld.apply(this)
  }
})();

Патч можно применить на этапе загрузки в своем js-коде, не влезая в код движка

Наверно без этих строк создание игры с такой огромной картой и таким большим количеством объектов было бы невозможно: потому что все начинало жутко лагать и тормозить.

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

Не прощаюсь 

Когда у тебя спортивное авто, но нет денег - хоть телефон продавай
Когда у тебя спортивное авто, но нет денег - хоть телефон продавай

Собственно, игры как таковой пока еще нет: есть только свободное вождение по городу. Что-то вроде демо-версии города получается. В данный момент я как раз работаю над маршрутами трафика и полицейской погоней. Поэтому и расскажу обо всем об этом наверно уже в следующей статье. Кажется, эта статья и так получилась довольно длинной. И испугавшись этого, я решил даже не упоминать о смене времени суток и погоды и много о чем еще... Ждите дальнейших рассказов о ходе разработки моего убийцы GTA и NFS в одном флаконе.

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


  1. shaman4d
    09.08.2024 19:05
    +1

    Очень интересно. Но вот не увидел ссылки на демо (


    1. Kempston Автор
      09.08.2024 19:05
      +5

      Пожалуйста:

      https://m3d.me/game/capitalist/t20h/?lang=ru

      Или вот, если мой сервер вдруг приляжет:

      https://m3d.itch.io/capitalist3d


      1. shaman4d
        09.08.2024 19:05

        Спасибо!


      1. punzik
        09.08.2024 19:05

        Застрял в мусорных контейнерах. Потрачено.


        1. Kempston Автор
          09.08.2024 19:05

          Да, коллизии с мелкими предметами я еще пока не проработал. Там получается, что не машина застревает в предмете, а предмет в машине, и луч надо испускать не от машины, а от этого предмета... Или найти еще какое-то другое решение. Это реализуем чуть позже. А пока есть пункт респаун в меню настроек. Либо можно перезагрузить игру, если закончились деньги.


      1. orbion
        09.08.2024 19:05
        +1

        Залипательно! Будет здорово, если перерастёт в нечто большее.

        Насчёт графического движка — не рассматривали PlayCanvas? На его основе есть классные демки: After The Flood, BMW i8. Правда, редактор сцен у них, кажется, закрытый.


        1. Kempston Автор
          09.08.2024 19:05

          Присматривался к нему, но отпугнуло то, что я что-то не могу себе скачать. А могу ли я менять код или выбирать нужную мне версию? С three.js ощущаю как-то больше свободы действий. Ну а так, демки впечатляют, конечно.


  1. Vladus
    09.08.2024 19:05
    +4

    "Запомните этот исторический момент. Пусть это будет первая в мире гонка без вида от третьего лица"..

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


    1. Kempston Автор
      09.08.2024 19:05
      +1

      Я не испытываю кайфа в том, чтобы выйти из себя, увы.


    1. danilasar
      09.08.2024 19:05
      +1

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


  1. SquareRootOfZero
    09.08.2024 19:05
    +3

    Генератор города (...)
    Игра загружается в браузер. И для сокращения времени загрузки она должна «весить» как можно меньше.

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

    поскольку мы пишем не Mortal Combat

    Вообще, идея перспективная - вместо того, чтобы, врезавшись в чужое транспортное средство, как ни в чём не бывало поехать далее, чтобы вылезали пацаны и, такие: "Ты куда прёшь, баран? - Да сам-то козёл! - Ты чо буровишь?!..." - и т. д., ветки диалога, потом бой, и, если проиграл, тебя везут квартиру отписывать.

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

    Это ж, вроде, стандартная практика - использовать отдельную, сильно упрощённую модель для обсчёта физики? Что там "решать"?

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

    В смысле, а какое-нибудь стандартное octree не спасёт отца русской демократии?

    на более высоких скоростях довольно часто случались пролеты сквозь препятствия (...)

    Есть же, вроде, эти, как их, недискретные какие-то методы расчёта коллизий, как раз для подобных случаев? Типа, не увеличивать до бесконечности частоту, а, имея вектор скорости, посчитать, пересечём мы хитбокс до начала следующей итерации или нет.


    1. Kempston Автор
      09.08.2024 19:05

      Если используется настолько "квадратная" и повторяющаяся геометрия, не проще вообще было бы не связываться с 3D-редакторами городов и сделать процедурную геометрию?

      Сгенерировать процедурно такие дороги, как выдает редактор города (изогнутые), довольно непростая задача. Ну и дома не хотелось делать совсем уж повторяющими улицы. Там кое-где есть дворы, проходы между домами и так далее. Реализовать это программно - это, по сути, написать свой такой же генератор города.

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

      Ну, да, все как в реальной жизни. Есть такая передача, "Дорожные войны", кажется.

      Это ж, вроде, стандартная практика - использовать отдельную, сильно упрощённую модель для обсчёта физики? Что там "решать"?

      Может быть, и есть. Но когда у тебя дома тоже довольно простые, есть выбор - не делать еще и дополнительные упрощенные модели.

      В смысле, а какое-нибудь стандартное octree не спасёт отца русской демократии?

      Ох, не силен отец русской демократии в octree... И, как понимаю, это все равно не спасает от добавления/удаления элементов в список активных коллайдеров. Просто другой способ. Да и те самые границы (bounds) все равно будут нужны, нет? Чтобы добавлять коллайдеры с определенного минимального расстояния.

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

      Непрерывное обнаружение столкновений (CCD), к сожалению, не поддерживается cannon.js. А он неплохо моделирует физику отскока от препятствий. CCD есть в three.js, но там нет физики. Вот я и комбинирую эти методы. Впрочем, вы несколько о другом. Рассчитать, произойдет ли коллизия в будущем. Не задумывался об этом. А, хотя, что мне от этой информации. Ну узнаю я, что коллизия будет на следующей итерации. Мне не нужна эта инфа, а нужно, чтобы физический движок среагировал на столкновение. Принудительно его это сделать не заставить, если не влезть глубоко в его код... Если он пропускает коллизию, значит пропускает.


  1. rionnagel
    09.08.2024 19:05
    +1

    Я конечно всё понимаю про вид от третьего лица, но абсолютно не понимаю почему вы выбрали вид из именно из кабины, где большая часть экрана заблокирована бесполезной мишурой. Я бы рекомендовал попробовать из капота тогда.
    Да, если ставить руль на стол, то вид из кабины рушит "реализм" не меньше, чем игра от третьего лица.


    1. Kempston Автор
      09.08.2024 19:05
      +2

      А там есть вид от капота, переключается кнопкой V. Но мне почему-то все равно больше нравится - от руля. Ну, как бы, ощущаешь себя водителем машины, что ли.


    1. Kempston Автор
      09.08.2024 19:05
      +1

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


  1. TomKallen
    09.08.2024 19:05
    +1

    Зачем здесь тег java?


    1. Kempston Автор
      09.08.2024 19:05

      Действительно. Моя ошибка. Исправил на javascript.


  1. isumix
    09.08.2024 19:05
    +1

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


    1. Kempston Автор
      09.08.2024 19:05

      В случае с 3D все ограничивается производительностью видеокарты. Это узкое место точно так же, как и в подобных нативных приложениях. А CPU большей частью ожидает завершения операция на видеоподсистеме. Разве что javascript на CPU выполняется помедленнее. Но, раз мы все равно ожидаем GPU, то какая разница.


  1. isumix
    09.08.2024 19:05
    +2

    Немного странно видеть везде var-ы конечно когда есть const/let ну да ладно)


    1. Kempston Автор
      09.08.2024 19:05

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


      1. isumix
        09.08.2024 19:05
        +1

        По идее компилятор должен лучше оптимизировать const так как он знает что значение не изменится.


  1. Mingun
    09.08.2024 19:05
    +1

    На гонки люди приходят не только покататься, но и обставить своего железного коня. А если есть только вид из кабины, то часть с обставкой становится абсолютно бесполезной. Потому что ты 99% времени не видишь свою машину. В гараже на неё любоваться? А если не видно разницы, зачем тогда вообще машины менять? Всё равно во многих гонках они ездят одинаково. Ну и кроме того, вид из кабины загораживает существенную часть экрана бесполезной торпедой и рулём и всё равно не способствует реалистичности, за которую вы топите, так как даже рук на руле нет. Так и зачем всё это?

    По демке — недостаёт описания управления. В начале новой игры что-то мелькает, но быстро пропадает и больше не появляется. Например, полазив по менюшке, я включил показ интерфейса для мобильных устройств и там с удивлением обнаружил кнопку «Ручник», нажатие на которую действительно стопорит машину. Но вот где она на клавиатуре, найти так и не смог… Очевидный вариант — пробел — не работает. Кстати, не всем удобны стрелочки, многие к WASD привыкли — неплохо бы сделать если не кастомизацию всех кнопок, то хотя бы две схемы управления.

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

    Кстати, о деньгах. Со стартовой локации после мусорных баков повернул сразу направо — подобрал одну купюру (вроде 10-ку) и дальше в том направлении денег не видел, хотя колесил довольно долго. Судя по всему, они все сосредоточены в другой части города. Огромный пустой город никому не будет интересен. Надо более равномерно заполнить его предметами.

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

    Firefox 115.14.0esr (64-разрядный), Windows 7


    1. Kempston Автор
      09.08.2024 19:05
      +1

      вид из кабины загораживает существенную часть экрана бесполезной торпедой и рулём

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

      не способствует реалистичности, за которую вы топите, так как даже рук на руле нет

      То есть, с руками на руле уже будет реалистично? А если взглянуть на это под таким углом: с торпедой и рулем просто недостаточно реалистично, а с руками - полностью реалистично? А с видом сзади - не реалистично совсем.

       А если не видно разницы, зачем тогда вообще машины менять? Всё равно во многих гонках они ездят одинаково. 

      Физику делают одинаковой, видимо, чтобы игроку сильно не заморачиваться с выбором тачки. А что, если сделать ее разной? Одна машина быстрее на прямых, но ее больше заносит на поворотах, а с другой - все наоборот. Я еще пока не особо работал над физикой, но постараюсь сделать именно так. И ведь вид внутри у тачек тоже разный. Почему можно любоваться их разнообразием снаружи, но нельзя изнутри.

      По демке — недостаёт описания управления.

      Согласен. Вынесу это в справку по игре, чтобы было доступно в любой момент.

      обнаружил кнопку «Ручник», нажатие на которую действительно стопорит машину. Но вот где она на клавиатуре, найти так и не смог… Очевидный вариант — пробел — не работает

      Кнопка B - brakes. Соглашусь, пробел удобнее. Особенно, когда несешься и надо резко затормозить, сложно так сразу ткнуть в кнопку B. Я ее почему-то взял из примеров cannnon.js с транспортом. Перенесу на пробел.

      Кстати, не всем удобны стрелочки, многие к WASD привыкли — неплохо бы сделать если не кастомизацию всех кнопок, то хотя бы две схемы управления.

      А вот с WASD не все так однозначно. Я бы сказал, это философский вопрос) Сейчас я включу Стива Дждобса и буду доказывать, что эта возможность вам не нужна и все портит) WASD используется, скажем, в той же ГТА, чтобы правой рукой во время движения можно было крутить камеру мышкой. Но когда так едешь, крутя камерой, то сшибаешь все столбы и все, что находится вообще рядом. И не получается вписаться не то, чтобы в свою полосу, а даже в дорогу в целом. Какие уж тут гонки. Кроме того, управлять машиной кнопками левой рукой невозможно так же хорошо, как правой (ну если вы не левша, конечно). Вроде бы все качественные автосимы имеют управление стрелочками. Управлять левой рукой можно, если вождение в игре - это какая-то побочная механика. А здесь она основная.

      Единственная причина убрать управление со стрелок - это то, что на многих клавиатурах их зачем-то делают миниатюрными, я имею в виду стрелки вверх и вниз. Тогда да, это боль... Пожалуй, я продублирую управление, но не на WASD, на, например, на IJKL - чтобы играть не стрелочками, но все равно правой рукой. могу еще на numpad продублировать, например - 5123.

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

      Насчет ее размера подумаю. И правда, мелкая. А какие-то метки на ней будут - банк, деньги, географические названия и т.д Просто пока не реализовано. Как раз работаю над большой картой и текстовыми слоями. Все это будет и на маленькой тоже.

      подобрал одну купюру (вроде 10-ку) и дальше в том направлении денег не видел

      Я упомянул в конце статьи, что игры как таковой еще нет. Из главного меню сейчас доступно только свободное вождение. Я туда запихнул всего 4 купюры так, для примера. Купюры будут очень часто и по всему городу. Будет также и трафик.

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

      Звуков пока просто нет. Только гроза.

      Firefox 115.14.0esr (64-разрядный), Windows 7

      Спасибо. Кстати, попробуйте Хром. У меня 10-ка и в Firefox больше фризов. В Хроме все плавнее. По-моему, не только в моей игре, но и во всех, где WebGL, Firefox его отображает как-то рывками.


      1. Mingun
        09.08.2024 19:05

        А почему бесполезной? Ведь в реальной машине мы их тоже видим.

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

        Спасибо. Кстати, попробуйте Хром.

        Хрома у меня нет, а проблем с Firefox'ом я не заметил, всё работает плавно на максимальных настройках — я выставил их до начала игры. Кстати, не уверен, что в процессе игры они меняются правильно, потому что я снизил дальность прорисовки с максимума до нуля, но в картинке вообще ничего не поменялось. Возможно, это только с новой игровой сессией работает?

        А вот с WASD не все так однозначно.

        WASD — это привычно. Нет смысла делать управление а-ля vim, им ещё меньше народу пользоваться будет. (в сторону) И с мелкими стрелочками — это прямо по-больному, не думал, что будет так сложно найти ноутбук с нормальной полноразмерной клавиатурой и не посечёнными стрелками — все производители как с ума посходили с этой экономией на спичках. И то она далека от идеала. (кхм-кхм)

        Я туда запихнул всего 4 купюры так, для примера.

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


        1. Kempston Автор
          09.08.2024 19:05

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

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

          Хрома у меня нет, а проблем с Firefox'ом я не заметил, всё работает плавно на максимальных настройках

          Хорошо. Вероятно, у вас мощное железо. На моем Firefox лагает.

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

          Там до игры настраивается только количество второстепенных объектов. Самая последняя настройка. Вероятно, вы ее и имели в виду? Остальные оказывают эффект прямо в игре - "Дальность обзора" и "Дальность объектов". Просто они не дают снизить значение прямо до нуля, поэтому их можно наблюдать только на какой-то длинной улице.

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

          Это точно. Я нашел. Но там другие кнопки расположены не очень удобно... Питание, например и так далее.


      1. SquareRootOfZero
        09.08.2024 19:05
        +2

        Ведь в реальной машине мы их тоже видим. Чем не реализм?

        Осспаде, да забудьте вы уже слово "реализм" применительно к компьютерным играм (не конкретно вы, а вообще все забудьте). Нет там реализма. Есть набор декораций "под реализм", который нужен не для вящего усложнения, а совсем наоборот - для того, чтобы привычные визуальные метафоры хорошо накладывались на привычный гештальт реального мира ("я бегу с ружьём и во врагов стреляю"), а не требовали привыкания и постоянного осмысления ("я передвигаю свой хитбокс и трассирую лучи в направлении других хитбоксов"). В "реальной машине" у юзера разрешение выше, угол зрения больше, VR-гарнитура навороченная встроена прямо в башку, манипуляторы с тактильным фидбэком и пр., и пр. А тут он сидит, будто весь в гипс залитый после травмы позвоночника, вызвавшей паралич глазодвигательных мышц, и пырится - действительно, "чем не реализм"? (Но за наезд на висящую над дорогой огромную зелёную купюру снял бы перед автором шляпу, если бы носил - вот уж реализм такой, что прямо всем реализмам реализм :)

        Одна машина быстрее на прямых, но ее больше заносит на поворотах

        Играл лет десять назад в гоночную игру ReVolt, там всё так и было.

        управлять машиной кнопками левой рукой невозможно так же хорошо, как правой (ну если вы не левша, конечно)

        Не особо фанат гонок, но в некоторые поигрывал - везде, насколько я помню, был WASD по-дефолту. Так лучше и привычнее - во всяком случае, для нефаната гонок (фанат бы, наверное, купил руль или хотя бы джойстик, но, с другой стороны, вряд ли вообще стал бы играть в гонки в браузере). Оглядываться мышью помогает хоть как-то ощутить габариты происходящего и не мешает управлять транспортным средством. А что, сделать управление настраиваемым - это совсем нельзя, потому что не по стивджобски?


        1. Kempston Автор
          09.08.2024 19:05

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

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

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

          Согласен, здесь нет такой свободы обзора, как в реальном мире. Но вид от бога - еще хуже, такого в реале и в помине нет.

          Но за наезд на висящую над дорогой огромную зелёную купюру снял бы перед автором шляпу, если бы носил - вот уж реализм такой

          Упрощения тоже должны быть. Но только упрощения, а не увод совсем в сторону (как от вида от руля к виду от третьего лица). Если не собирать купюры, то надо тогда устраиваться на работу, ехать в офис, разбирать бумажки... Совсем другая игра получается. Я считаю, упрощение со сбором купюр допустимо.

          Не особо фанат гонок, но в некоторые поигрывал - везде, насколько я помню, был WASD по-дефолту.

          А я вот что-то не припомню таких. Может быть, я давно в них не играл. Всегда было на стрелочках управление. Разве что иначе в ГТА и иже с ними, где механика вождения - не основная механика.

          Оглядываться мышью помогает хоть как-то ощутить габариты происходящего и не мешает управлять транспортным средством

          Не мешает?? Да вы с ума сошли!) Отклонить камеру мышкой и при этом не потерять направление движения - наверно надо иметь железную координацию.

          А что, сделать управление настраиваемым - это совсем нельзя, потому что не по стивджобски?

          Нет. :)


          1. SquareRootOfZero
            09.08.2024 19:05
            +1

            В таком случае визуальная метафора вида от руля лучше, чем вид от третьего лица, для восприятия вождения автомобиля.

            Это не "визуальная метафора", а "страдай, чтобы страдать, потому что, сука, страдай". Визуальная метафора здесь - "я веду автомобиль". И от первого лица я веду автомобиль, и от третьего лица я веду автомобиль. Если бы геймплейные механики были как-то завязаны на рулевое колесо - к примеру, надо было бы мышкой его мацать и вручную вертеть туда-сюда - тогда бы да бы, это было бы ужасно, но это была бы "визуальная метафора вида от руля". При том, что вид от третьего лица позволяет оценить габариты авто и как оно вписывается между объектами окружающей среды - да, в реале такого нет, но в реале бинокулярное зрение и пр. органы чувств позволяют оценить габариты авто и пр., а в особых случаях можно голову из окна высунуть. У вас можно голову из окна высунуть, или водитель сидит привязанный, как заводной апельсин?

            Не мешает?? Да вы с ума сошли!

            Не мешает. Если сделано по-уму - совершенно не мешает. Обычно это было так, что зажимаешь какую-то кнопку, активирующую режим "freelok", быстро осматриваешься, отпускаешь кнопку, и взгляд автоцентрируется обратно по линии движения. Разумеется, это делается не в особо сложные и напряжённые моменты.

            Нет. :)

            Минус один потенциальный клиент. Шучу. Я бы и так не стал играть :)


            1. Kempston Автор
              09.08.2024 19:05

              И от первого лица я веду автомобиль, и от третьего лица я веду автомобиль.

              От третьего лица вы ведете автомобиль не как водитель.

              зажимаешь какую-то кнопку, активирующую режим "freelok", быстро осматриваешься, отпускаешь кнопку, и взгляд автоцентрируется обратно по линии движения

              Ну это совсем другое дело. Пожалуй, добавлю кнопки - посмотреть по сторонам и назад. Это будет вполне достаточно.


  1. volodalexey
    09.08.2024 19:05
    +4

    Ребят, у кого есть неофициальный патч для вида от третьего лица ? ))


    1. Kempston Автор
      09.08.2024 19:05
      +2

      Да я на вас в суд подам! :)