Инди Alexander Kulkov
297

Секреты "Мироходца"

Немного об изнанке виртуального миростроустройства, на примере своего Unity-проекта.

В закладки

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

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

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

Виды порталов

Для смены сцен, как и для многого другого, используется скрипт. Я не буду углубляться в код скриптов, просто поясню, что именно они делают. Скрипт портала отправляет нас в другую сцену, которая запускается "с нуля". Чтобы не плодить 20 разных скриптов для разных порталов в Unity есть возможность сделать часть переменных скрипта видимыми в редакторе. Что это значит - на каждом портале будет висеть один и тот же скрипт, но в редакторе в поле каждого скрипта можно будет поставить разные номера, отправляющие на разные уровни. Сами номера сцен, кстати, Unity проставляет автоматически - в окне с настройками билда можно добавить текущую сцену к списку.

Добавляете текущую сцену к списку, и потом её номер можно использовать для переходов

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

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

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

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

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

Обложка . На картинке летающий корабль "Рак"

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

Указываем в скрипте на кубике отправления имя точки назначения

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

"Невангеры", видеонарезка

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

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

Светлячки летают по меняющемуся маршруту

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

Зелёным цветом выделена ограничивающая геометрия, которая будет скрыта от героя

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

Также понадобилось заново перерисовать траву и деревья, чтобы они оказались внутри клумб. Траву удобнее рисовать на специальной поверхности, которая есть в Unity и не только в нём. Это terrain, который предназначен для рисования неровного ландшафта, рисования по нему текстурами, и рисования поверх него травы, деревьев, кустов и прочих повторяющихся элементов. В городе мне от всей этой мощи данного инструмента нужна в основном только трава, поэтому я просто подгоняю terrain нужного размера под парк и рисую там траву. Саму поверхность terrain'a выключаю из отрисовки и запрещаю персонажу сталкиваться с ней.

Кстати, следует помнить, что terrain не любят магию copy/paste. Если создаёте новую сцену на основе базовой, в которой был terrain, или просто копируете terrain внутри сцены, то изменения в одном - будут отражаться на других. Так что аккуратнее с terrain'ами.

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

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

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

Добавляю модель "Технохрама", встраивая её в существующую систему переходов между измерениями.
Проектирую лес на выходе из пещер города водопадов
Моделю небесный порт в Blender и добавляю его в проект

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

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

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

Видеонарезка с новыми локациями

P.S. Что касается исполняемого файла, архив с новой версией я пока не делал, если интересно скачайте старую версию - ссылки можно найти в конце прошлого материала по проекту:

Материал опубликован пользователем. Нажмите кнопку «Написать», чтобы поделиться мнением или рассказать о своём проекте.

Написать
{ "author_name": "Alexander Kulkov", "author_type": "self", "tags": [], "comments": 9, "likes": 5, "favorites": 11, "is_advertisement": false, "subsite_label": "indie", "id": 25758, "is_wide": false, "is_ugc": true, "date": "Mon, 27 Aug 2018 17:44:34 +0300" }
{ "id": 25758, "author_id": 25870, "diff_limit": 1000, "urls": {"diff":"\/comments\/25758\/get","add":"\/comments\/25758\/add","edit":"\/comments\/edit","remove":"\/admin\/comments\/remove","pin":"\/admin\/comments\/pin","get4edit":"\/comments\/get4edit","complain":"\/comments\/complain","load_more":"\/comments\/loading\/25758"}, "attach_limit": 2, "max_comment_text_length": 5000, "subsite_id": 64960 }

9 комментариев 9 комм.

Популярные

По порядку

Написать комментарий...
1

Но почему моделька персонажа стандартная?

Ответить
0

А почему нет? :)
Вобще модель легко заменяется на что-то со сходной системой костей, вроде моделей из игры Battlerite (например). Но я более удачной модели слёту не нашёл, да и зачем брать модели из других игр, уж лучше стандартная.
По-хорошему, надо просто научиться правильным образом сохранять ту же самую модель из Blender'а, чтобы Unity понял её правильно. Тогда стандартную модель можно будет подредактировать самостоятельно и анимации останутся. Но я пока не занимался этим вопросом.

Ответить
0

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

Ответить
0

Да не собираюсь я Стим штурмовать :)

Ответить
1

Простите за IMHO, но зачем так подробно расписывать очень базовые вещи, связанные со скриптами в unity, типа подбирания светлячков и их рандомного движения?

Ответить
0

Подробно - это было бы с приведением текста самого скрипта :)

Ответить
0

Ладно, согласен)

Ответить
0

Название напомнило о комиксе Bubble "Мироходцы". Смотрите чтобы там не было каких-нибудь предъяв.

Ответить
0

Прямой эфир

[ { "id": 1, "label": "100%×150_Branding_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox_method": "createAdaptive", "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "ezfl" } } }, { "id": 2, "label": "1200х400", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "ezfn" } } }, { "id": 3, "label": "240х200 _ТГБ_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fizc" } } }, { "id": 4, "label": "240х200_mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "flbq" } } }, { "id": 5, "label": "300x500_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "ezfk" } } }, { "id": 6, "label": "1180х250_Interpool_баннер над комментариями_Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "clmf", "p2": "ffyh" } } }, { "id": 7, "label": "Article Footer 100%_desktop_mobile", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fjxb" } } }, { "id": 8, "label": "Fullscreen Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fjoh" } } }, { "id": 9, "label": "Fullscreen Mobile", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fjog" } } }, { "id": 10, "label": "Native Partner Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyb" } } }, { "id": 11, "label": "Native Partner Mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyc" } } }, { "id": 12, "label": "Кнопка в шапке", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fdhx" } } }, { "id": 13, "label": "DM InPage Video PartnerCode", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox_method": "createAdaptive", "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "clmf", "p2": "flvn" } } }, { "id": 14, "label": "Yandex context video banner", "provider": "yandex", "yandex": { "block_id": "VI-250597-0", "render_to": "inpage_VI-250597-0-1134314964", "adfox_url": "//ads.adfox.ru/228129/getCode?pp=h&ps=clmf&p2=fpjw&puid1=&puid2=&puid3=&puid4=&puid8=&puid9=&puid10=&puid21=&puid22=&puid31=&puid32=&puid33=&fmt=1&dl={REFERER}&pr=" } }, { "id": 15, "label": "Плашка на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byudo", "p2": "ftjf" } } }, { "id": 17, "label": "Stratum Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fzvb" } } }, { "id": 18, "label": "Stratum Mobile", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fzvc" } } } ]
Узнавайте новости о мостах
Санкт-Петербурга первыми
Подписаться на push-уведомления