что такое ресурсы в игре
Игровые технологии. Игры, создающие будущее
В своей книге я раскрываю глубину понятия игра. Рассказываю, что игра – это не только детское развитие, но и инструмент развития взрослых. Почему игра глубинно влияет на подсознание? Как игры влияют на картину мира? Также я раскрываю секреты игровых технологий, заложенных в настольных играх компании «Уник-Ум». В книге даются пошаговые инструкции для успешного проведения игр. Книга будет полезна родителям, психологам, педагогам, трекерам, коучам и другим людям, работающим с человеческим потенциалом
Оглавление
Приведённый ознакомительный фрагмент книги Игровые технологии. Игры, создающие будущее предоставлен нашим книжным партнёром — компанией ЛитРес.
Что такое ресурсы в игре и зачем в них играть
«Ресурсы» включены не во все игры «Уник-Ум», но игропрактики могут их включать по своему усмотрению. В этой главе мы поговорим о тех играх, в которых «ресурсы» предусморены автором, и о их смыслах.
Игры «Калейдоскоп Уникальности», «Калейдоскоп Профессий», «Островитяне».
В игровых технологиях «Калейдоскоп Уникальности» и «Калейдоскоп Профессий» есть два вида «ресурсов»: материальные и нематериальные. В игровую технологию «Островитяне» включены только нематериальные «ресурсы».
Материальные ресурсы — это ресурсы, олицетворяющие материальный мир. Это все материальные вещи, в том числе и деньги.
Нематериальные ресурсы — это ресурсы, олицетворяющие внутренние ресурсы человека. Такие как эмоции, чувства, эмпатия, вера, поддержка и т. д.
Баланс материальных и нематериальных ресурсов — это психологическая граница человека.
Психологическая граница человека понимается автором игр следующим образом: это стыковка внутреннего состояния и внешнего воздействия. Психологическая граница тесно связана с желаниями человека. Потому что, как мы уже говорили, желания зарождаются внутри личности, и для создания границ человек должен действовать. Таким образом, человек самостоятельно строит свои границы, и они полностью зависят от его способности держать этот баланс. Под балансом в данном случае я понимаю реакцию человека на какое-либо состояние, возникающее внутри. Например, если человек испытывает боль, то он должен создать механизм, который уберёт источник боли. Это и есть баланс. Боль возникла внутри в виде скорби, печали, ужаса и т.д., и человек способен самостоятельно убрать источник этой боли. Если же у человека на появление боли нет механизмов защиты, значит, у него нет баланса, и можно говорить о том, что граница разрушена.
Это коротко о границах и значении внутренних и внешних ресурсов.
Обращайте внимание на то, как игроки обращаются с «ресурсами». Например, есть игроки, которые аккуратно складывают материальные «ресурсы» по номиналу, по размеру. Я всегда рекомендую профессиональным ведущим покупать денежные купюры разного номинала и разной валюты. Также мне нравится вариант, когда ведущий выдаёт золотые слитки или дома с автомобилями. Иногда игроки строят из материальных «ресурсов» целый город. Эти варианты мы чаще используем в квестах. Некоторые игроки очень небрежно относятся к «ресурсам». «Ресурсы» у них пропадают, или они неправильно их считают, бездумно отдают другим игрокам, или всем помогают и стараются посеять семена добра.
С нематериальными «ресурсами» также можно сделать очень много интересных наблюдений. Игроки тоже могут их систематизировать, обменивать. Я рекомендую профессиональным ведущим покупать свои «ресурсы» для своих игр. Подойдут бусины, пластиковые или стеклянные фигурки, камешки. Очень интересно наблюдать за тем, как игроки обращаются со своими нематериальными «ресурсами». Например, иногда они складывают бусины-«ресурсы» на колени, затем забывают о них и встают со стула. Игроку приходится собирать их из всех углов комнаты. «Как часто Вы так забываете о своём здоровье?» — спрашиваю я в такие моменты.
Игра с использованием «ресурсов» — это, можно сказать, отдельная игра, которая состоит из наблюдений ведущего. Наблюдая за игроком, ведущий даёт обратную связь и рефлексирует, может проводить короткие коучсессии.
В игровой технологии «Островитяне» «ресурсы» играют роль не такую важную. В этой игре они нужны скорее для игровой динамики, чтобы в конце игры ведущий мог определить победителя. Ведущий может заменить «ресурсы» на более важные вещи, которые может дать игра.
Работа с кубиком. Магический кубик: дело случая или судьба?
Игроки бывают разные, и кубик в их руках ведёт себя по-разному. У одних это всегда случайность, и они либо верят, либо не верят в случай. Те, кто верят в случай, ведут более интересную игру. С теми, кто не верит, играть бывает сложно, но профессиональные игропрактики знают, что результат у таких игроков всегда очень яркий.
Подростки и дети обычно наделяют кубик магической силой, и умеют им управлять. Для них кубик — это не случайность, а совершенно управляемый элемент игры.
Заметьте, что те, кого слушается кубик (эти люди для общества кажутся «везунчиками») придерживаются примерно таких правил:
— Минимально касаться пальцами кубиков и минимально на кубиках оставлять следы от пальцев;
— Складывать кубики в руке как единый объект, кубики у них практически не имеют расстояние между друг другом;
— Запоминать комбинацию лежащих на столе кубиков, бросать их низко от кромки стола и замечать ось вращения;
— Важно. В момент броска кубиков постарайтесь подумать о части тела, которая остаётся вами больше всего незамеченной. Например, кончики пальцев ног, ушные раковины или мышцы черепа. Пройдитесь мысленно в эту часть тела, понаблюдайте, что там происходит, и в этот момент бросайте кубики.
Все эти советы игроки упорно отрабатывают и тратят на это много времени, зато и бросок кубика у них становится идеальным, что придаёт им уверенности в том, что я называю «управление миром» или «мир слышит меня»
Совет: постарайтесь для игры запастись фишками, ресурсами и кубиками, которые игрок может взять себе на память об игре. Порой это бывает очень важным и ценным.
Формула клика: ресурсы в инкрементальных играх
Математика, приёмы и графики роста.
За кажущейся простотой idle-игр и кликеров часто скрывается продуманный геймдизайн. Геймдизайнер и участник коллективного блога «Манжеты ГД» Илья Осташко перевёл вторую статью из цикла аналитика Александра Кинга, посвящённого этому жанру. DTF уже публиковал первую часть материала.
Хоть инкрементальные игры и выглядят простыми, их нужно тщательно продумывать. Взглянув на некоторые успешные примеры, можно лучше оценить основные особенности жанра и понять, как устроить свою игру.
Кликаешь на кнопку и число растёт. Кликаешь ещё раз — растёт снова. Продолжишь кликать, и, в конце концов, откроешь что-то, увеличивающее это число за тебя. Теперь можно даже не находиться в игре. А потом всё заново — и так до бесконечности.
Это, по сути, и есть «инкрементальная» игра.
Перед тем, как погрузиться в математику, я хотел бы отметить три важных момента в дизайне инкрементальных проектов, о которых необходимо помнить: ощущения исследования, разница между «айдлером» и «кликером», важность темы и художественного стиля.
Один из самых важных «радостных» элементов в инкрементальной игре — это открытие нового. Многие из них начинаются очень просто, но комплексность растёт по спирали вместе с прогрессом игрока. Процесс раскрытия комплексности игры побуждает игрока и дальше исследовать её, открывая новые и спрятанные игровые элементы. Например, Candy Box можно воспринимать целиком как игру об исследовании самой себя, и повышение уровня — это просто механизм открытия дальнейшего контента.
Поэтому в большинстве инкрементальных игр весь контент сразу не доступен, а открывается путём траты внутриигровой валюты. Масштаб контента может быть известен игроку с самого начала, как в Idling to Rule the Gods, где конкретные части игры специально оставлены пустыми, давая понять, как и когда они будут открыты. А может быть скрытым, когда пользователю не сообщают, что те или иные элементы существуют, пока он не достигнет определённого уровня (например, почти весь контент в Cookie Clicker). Некоторые игры совмещают оба варианта: в AdVenture Capitalist игроку демонстрируют, как много контента можно открыть, но в то же время есть много скрытых достижений.
Открытие нового — важный элемент, на который нужно обращать внимание в дизайне инкрементальной игры, поскольку он предполагает систему наград для игрока за исследование в процессе изучения основных механик. «Вываливая» на игрока всё сразу, вы не только поднимете порог вхождения и усложните изучение игры, но также и лишите его радости от последовательного открытия нового контента в процессе ознакомления с игрой.
Инкрементальные игры сконцентрированы на двух пересекающихся, но разных механиках:
Игры, базирующиеся на второй механике, буквально заставляют игрока «кликать», чтобы зарабатывать, или имеют другие способы активного вовлечения, например, ограничение хранилищ, требующих часто обращать на них внимание, чтобы прогресс не останавливался. В CivClicker игрок в основном активно управляет поселением, лишь иногда оставляя игру для пассивного заработка.
Те проекты, что основаны на первой механике (автономный прогресс), могут также включать и «кликанье», но зачастую эффективность кликов снижается в пользу автоматических заработков. В AdVenture Capitalist игрок поначалу должен активно кликать, но быстро открывает возможность автоматизировать процесс и сократить необходимость своего участия в нём.
Выбор того или иного варианта — в целом вопрос предпочтений и влияния на цели игры. Игра, которая требует активного управления, может больше вовлекать в течение короткого периода времени, но, если она будет слишком часто требовать много внимания от игрока, то легко нарушит принципы этичного геймдизайна. Более автономная игра может требовать меньше вовлечения игрока в каждой сессии, но может вызвать большую привязанность. Отчасти это объясняет высокие показатели возврата у idle-игр на Kongregate. AdVenture Capitalist даже напоминает игроку, что же произошло в его отсутствие, и подчёркивает, что игра не требует постоянного внимания:
Часто нарратив, в который обёрнута основная механика, принесёт только пользу инкрементальной игре (хотя это и не очевидно, учитывая минимализм механик в таких играх).
Осмысленная тематика может задать контекст для просто растущих чисел. Понятно, что любым играм надо бы иметь хороший арт-стиль и хороший дизайн, и инкрементальные игры тут не исключение. Выдержанный стиль позволит игре произвести цельное впечатление, а лёгкий интерфейс помогает игроку сконцентрироваться на самой игре, а не на попытках разобраться в кнопках.
Хороший тому пример — AdVenture Capitalist. Игра сделана в тематике бизнес-менеджера (что отлично дополняет геймплей, где нужно увеличивать числа), а в качестве стиля выбрана гуги-эстетика 1950-х годов. Всё это очень цельно выдержано и разбавлено юмором, что только усиливает визуальную и нарративную составляющую игры.
По сравнению с другими жанрами, инкрементальным играм не так нужны хорошая графика и проработанные тексты. Но очень важно понимать, что это не то же самое, что «совсем не нужны».
Определяющая жанр механика для инкрементальных игр — рост чисел. В предыдущей статье мы определили её так:
наличие как минимум одной валюты или числа;
это число растёт с определённой скоростью и не требует вмешательства игрока, либо требует незначительных действий с его стороны;
Именно третий пункт сильно влияет на ощущения от игры, это самый сложный аспект дизайна. Возьмём самый простой пример из проекта Тайлера Глайеля (Tyler Glaiel) Number: в нём нет ничего кроме трёх вышеупомянутых пунктов. Число-ресурс растёт, и можно его тратить, чтобы он рос быстрее.
Когда игра начинается, скорость роста равна 0,1 единицы в секунду. Число накапливается, и его можно потратить на ускорение роста. Первые пять улучшений выглядят так:
Первая колонка отвечает за стоимость улучшения, а вторая — за рост числа в секунду.
Если даже бегло взглянуть на числа, мы сможем определить ключевые принципы дизайна инкрементальных игр. Один их них — нелинейный рост как цены, так и пользы от улучшения, но рост цены значительно выше относительно роста прибыли.
В перспективе такой рост имеет смысл: если бы соотношение цены и пользы не увеличивалось (например, за цену 1 всегда бы покупалось 0,2 скорости роста), то не было бы никакой вариативности дохода, и скорость росла бы стабильно и предсказуемо относительно цены. Это бы очень быстро наскучило.
Но вместо этого мы имеет вот такой график соотношения цены (синий) и скорости роста (оранжевый) для первых 20 улучшений:
На этом графике видно, что функции нелинейные (и даже вовсе игнорируют функцию при скачке цены на 12 шаге). Цена растёт быстро, обгоняя скорость дохода. Этот аспект очень важен, так как он позволяет с прогрессом в игре экспоненциально увеличить время ожидания следующего улучшения. Поначалу игрок продвигается достаточно бодро, но чем больше он делает улучшений, тем ниже скорость дальнейшего развития.
Большинство инкрементальных игр имеют несколько источников дохода, которые можно улучшать (в отличие от единственного в Number). Это элемент исследования и стратегии: есть несколько векторов развития, растущих нелинейно и открывающих игроку простор для попыток оптимизировать доход. Если пользователь решает потратить все свои деньги на улучшение одного источника дохода, то благодаря экспоненциальному росту он дойдёт до такого момента, когда последующие улучшения будут стоить слишком много относительно пользы, которую они будут приносить. Потому игроку предлагается несколько путей развития, но ему постоянно необходимо оценивать, какое улучшение выгоднее в каждый момент времени.
Экспоненциальный рост цены — хорошее решение для постоянно увеличивающегося ресурса и времени, которое он требует. Но экспоненциального роста нет в большинстве игр. Почему?
Если взглянуть на последний график, можно заметить, что именно разница между двумя линиями даёт постоянно растущее соотношение цены к доходу. Для достижения этого, экспоненциально повышать нужно только цену (оранжевая кривая), а скорость получения ресурса оставить расти линейно.
В Clicker Heroes первым источником растущих чисел является персонаж Древозверь, который изначально стоит 50 монет и даёт прирост 5 монет в секунду. Второй уровень стоит 53,5, а прирост остаётся 5. Первые 50 уровней выглядят вот так (синий — цена, оранжевый — доход):
Имейте ввиду, что для простоты объяснения я опускаю некоторые механики в Clicker Heroes.
Функция дохода — прямая линия, так как каждое улучшение увеличивает доход ровно на 5, так что формула очень простая: общая скорость дохода в секунду = количество улучшений × 5 (y = 5x).
Стоимость же растёт, как и каждый шаг прироста к стоимости. Шаг прироста к стоимости у каждого последующего улучшения поначалу небольшой — по графику видно, что расстояние между кривыми за первые 20 улучшений почти не увеличивается. Но после 20-го уровня разница начинает расти существенно, требуя больше и больше за каждое последующее улучшение. Подобная формула используются во множестве инкрементальных игр:
Для нашего примера с Древозверем базовая цена (BaseCost) — 50, множитель (Multiplier) — 1,07. Второй уровень будет стоить 50 × 1.07¹ = 53,50, третий уровень — 50 × 1.07² = 54,24, и так далее. Множитель определяет изгиб линии: чем выше значения, тем более крутой будет кривая цены (Значение «1» даст линейный рост).
В Clicker Heroes значение равно 1,07, и этот множитель увеличивается для всех 35 улучшаемых героев, а различные здания в игре имеют значения 1,15. Интересно отметить, что все 10 построек в AdVenture Capitalist используют разные модификаторы, но в диапазоне от 1,07 до 1,15. Можно предположить, что построенные на множителях в этом диапазоне кривые сбалансированы и вполне удовлетворяют игроков.
Хотя некоторые проекты всё же отходят от шаблона. Инкрементальная «Чудовищная игра» (Monster Game), бывшая частью летней распродажи в Steam 2015 года, использовала высокие множители (около 2,5).
Экспоненциальный рост стоимости имеет плюс при балансировке несколько путей улучшений: он обеспечивает убывающую доходность на любом из них. Такой подход закладывает механику тактической балансировки доходности в саму формулу, а не в элементы, добавляемые геймдизайнером «сверху». Даже если ускорение получения того или иного ресурса кажется более выгодным в конкретный момент, экспоненциальный рост на даёт получать выгоду вечно.
В качестве примера, взглянем на список улучшаемых строений в Cookie Clicker:
Из одной этой таблицы можно вывести несколько закономерностей.
Базовая цена каждого последующего источника дохода почти в пять раз выше, чем предыдущего (кроме нескольких последних). Подобный рост даёт игроку достаточно времени, чтобы полностью насладиться каждым открытым ресурсом. Если прирост цены будет меньше, пользователь не будет изучать новые элементы, а если больше, — есть риск, что он устанет добираться до нового контента.
Скорость дохода возрастает в среднем на треть от каждого следующего источника. Значит, чем выше числа у цен, тем менее и менее эффективным становится доход по отношению к цене.
Каждая постройка имеет примерно один и тот же шаблон, так как следует одной и той же формуле:
График ниже отражает каждое из 11 зданий и первые 200 улучшений для каждого из них. По оси «y» отображается цена, по «x» — доход (так как функции экспоненциальные, логарифмическая шкала лучше отражает похожесть кривых, чем линейная).
Даже несмотря на то, что все строения выглядят совершенно разными, каждое последующее стоит и производит больше, чем предыдущее. Экспонента в формировании цены создаёт такие похожие кривые, оставляя игроку возможность оптимизировать систему.
В целом, в инкрементальных играх нужно заставлять числа расти. Но именно достижение наивысшей скорости их увеличения и обеспечивает глубину игры. Всегда есть несколько путей улучшения, и игрок вынужден постоянно оценивать их эффективность: стоит ли покупать более дешёвое улучшение прямо сейчас или накопить ещё, чтобы купить более крутое?
Так как в конце концов игрок хочет получить все апгрейды, эффективный подход заключается в оценке самого оптимального порядка. Представьте ситуацию: в данный момент мы производим пять единиц чего-то в секунду (nps = 5), и у нас есть выбор из двух улучшений. Первое стоит 20 (costA = 20) и увеличит прирост на один (rateA = 1), а другое — 100 (costB = 100) и увеличит прирост на 10 (rateB = 10). Первый апгрейд, конечно, дешевле, но и эффективность у него соответствующая.
Допустим, что мы предпочитаем более дешёвый вариант:
А теперь рассмотрим обратный вариант:
Что явно говорит нам о первом варианте как более приоритетном:
Ресурсы в игре
Некоторое время назад я имел неосторожность высказать определенные рассуждения о компьютерных играх. В частности, попытался их классифицировать. И привести алгоритм прохождения любой РЕСУРСНОЙ игры, практически всеми RTS-никами неоднократно испытанный на себе.
Чем больше в игре доступно действий, тем больше требуется ресурсов для их подтверждения. Однако игроки опытные моментально соображают, что у нескольких ресурсов есть общий эквивалент, и начинают мерить данную группу ресурсов в нем. Иначе невозможен никакой обмен и контакты с прочими персонажами очень затруднены. Если же создатели игры не предусмотрели такого эквивалента, игроки, недолго думая, вводят его сами.
А то, что за деньги не купить, например, музыкальный слух? Удачливость? Любимую RPG-играми магию и магическую энергию?
Тут я еще один реверанс медам и месье геймерам сделаю: сам я не геймер. И не компьютерщик, наверное. И Вы, разумеется, вправе считать все эти строки умствованиями теоретика. Но разве не те же принципы применяете и Вы сами?
Предвижу еще такое возражение: прямая ресурсная логика характерна для стратегических игрушек. Для ролевых игр эта логика очень косвенная, и иногда бывает несправедлива. Почему бы это?
Мы склонны признавать жизненной ту игру, с которой не в силах совладать и для которой не можем написать солюшен.
Итак, можно сказать, что в любой игре у любого Игрока и Персонажа должна быть возможность так или иначе, через одну или другую ресурсную плоскость, но достигать своих целей. «В игре У КАЖДОГО должна быть возможность подвига.» (с) Всадник Роханский.
Озвучив таким образом основной принцип балансировки игр, упомянем немного о самых распространенных ресурсных плоскостях.
Чувства, которые компьютеры ни использовать, ни имитировать пока достоверно не научились. Книжка про Электроника, называемая детской и\или фантастической, пыталась как-то поднять вопрос моделирования эмоций, но это прошло, конечно, мимо.
2) Человек может сымитировать условные выражения наподобие конструкций:
3) Человек стремится свести существование своего разума к исполнению цепочки команд, и даже довольно мелкие решения на уровне повседневных альтернатив принимать не желает.
Правда, следствия из данного предложения немного непривычны. Например: Война веры == война стандартов. Война стандартов (типа AMD versus Intel, Delphi versus C++-clones) == война веры?
Однако мы чувствительно отклонились от темы «второй ресурсной плоскости». Зачем? Нюанс в том, что не только поступки, но и мысли и эмоции фанатиков четко расписаны в этих самых стандартах на веру. Код на С++ мог бы выглядеть так:
if (Обижают_одноверца(Текущая_ситуация) == True)
Если послужил вере, вызываем что-то вроде:
class материальная_благодать: public Благодать;
class моральная_благодать: public Благодать;
virtual life Награда_Божья(Благодать, Персонаж)
Тип конкретного аргумента C++ компилятор, как известно, определяет по месту.
Есть тут и еще один мерзостный нюанс. Если смоделировать-таки человека в игре вплоть до эмоций, то, что получится в итоге, будет страдать или нет? Этично ли вообще применять его в моделях? Кому интересно, смотрите Лема, «Кибериада», «Путешествие седьмое, или как Трурля собственное совершенство к беде привело». Там подобная ситуация слегка исследована.
Тут самое время спросить: а чем это полезная игра отличается от бесполезной?
Лирическое отступление и на этот раз было долгим. Я подводил почву вот под что.
Фантастика и виртуальная реальность фантастичны и виртуальны именно потому, что ресурсные плоскости в них отличаются, или удалены некоторые общеизвестные, или изменены отношения. Но если мир несбалансирован, узнавать его неинтересно. Книгу второй раз не откроют, игру второй раз не загрузят. Not replayability.
Интересующихся подробностями отсылаю к Толкиен-Перумовским эхам и вообще к ролевым эхам и сайтам. Там часто мелькают качественные анализы взаимозависимостей мира. Вымышленного, конечно. Попробуй-ка кто сунуть нос в балансировку реальных отношений! Например, рыночных. Чай, убийства всяких там журналистов, раскопавших чего не надо, у всех на слуху.
Третье правило баланса: чем меньше законов в мире, тем меньше необходимости проверять, не нарушил ли Игрок чего-нибудь.
В полном соответствии с третьим правилом, список закрывается. Посмею предполагать, что три правила будут справедливы для ЛЮБОЙ игры и для любого мира. Количество же частностей и вариаций неимоверно, так что не будем в них и погружаться.
«Ум и сердце художника не должны быть связаны предыдущими пятьюдесятью правилами».
Работа с внешними ресурсами в Unity3D
Введение
Здравствуйте уважаемые читатели, сегодня речь пойдет о работе с внешними ресурсами в среде Unity 3d.
По традиции, для начала определимся, что это и зачем нам это надо. Итак, что же такое эти внешние ресурсы. В рамках разработки игр, такими ресурсами может быть все, что требуется для функционирования приложения и не должно храниться в конечном билде проекта. Внешние ресурсы могут находится как на жестком диска компьютера пользователя, так и на внешнем веб-сервере. В общем случае такие ресурсы — это любой файл или набор данных, который мы загружаем в наше, уже запущенное приложение. Если говорить в рамках Unity 3d, то ими могут быть:
Примечание: далее в статье используется код с использованием C# 7+ и рассчитан на компилятор Roslyn используемый в Unity3d в версиях 2018.3+.
Возможности Unity 3d
До версии Unity 2017 года для работы с серверными данными и внешними ресурсами использовался один механизм (исключая самописные), который был включен в движок – это класс WWW. Данный класс позволял использовать различные http команды (get, post, put и т.п.) в синхронном или асинхронном виде (через Coroutine). Работа с данным классом была достаточно проста и незамысловата.
Аналогичным образом можно получать не только текстовые данные, но и другие:
Работа с UWR в целом схожа с WWW в своей основе, однако есть и отличия, речь о которых пойдет дальше. Ниже приведен аналогичный пример загрузки текста.
Основные изменения, которые привнесла новая система UWR (помимо изменений принципа работы внутри) — это возможность назначать самому обработчиков для загрузки и скачивания данных с сервера, подробнее можно почитать здесь. По умолчанию это классы UploadHandler и DownloadHandler. Сам Unity предоставляет набор расширений этих классов для работы с различными данными, такими как аудио, текстуры, ассеты и т.п. Рассмотрим подробнее работу с ними.
Работа с ресурсами
Текст
Работа с текстом является одним из самых простых вариантов. Выше уже был описан способ его загрузки. Перепишем его немного с использование создания прямого http запроса Get.
Как видно из кода, здесь используется DownloadHandler по умолчанию. Свойство text это геттер, который преобразует byte массив в текст в кодировке UTF8. Основное применение загрузки текста с сервера — это получение json-файла (сериализованное представление данных в текстовом виде). Получить такие данные можно с использованием класса Unity JsonUtility.
Аудио
Для работы с аудио необходимо использовать специальный метод создания запроса UnityWebRequestMultimedia.GetAudioClip, а также для получения представления данных в нужном для работы в Unity виде, необходимо использовать DownloadHandlerAudioClip. Помимо этого, при создании запроса необходимо указать тип аудиоданных, представленный перечислением AudioType, который задает формат (wav, aiff, oggvorbis и т.д.).
Текстура
Загрузка текстур схожа с таковой для аудио файлов. Запрос создается с помощью UnityWebRequestTexture.GetTexture. Для получения данных в нужном для Unity виде используется DownloadHandlerTexture.
AssetBundle
Как было сказано ранее бандл – это, по сути, архив с ресурсами Unity, которые можно использовать в уже работающей игре. Этими ресурсами могут быть любые ассеты проекта, включая сцены. Исключение составляют C# скрипты, их нельзя передать. Для загрузки AssetBundle используется запрос, который создается с помощью UnityWebRequestAssetBundle.GetAssetBundle. Для получения данных в нужном для Unity виде используется DownloadHandlerAssetBundle.
Основные проблемы и решения при работе с веб-сервером и внешними данными
Выше были описаны простые способы взаимодействия приложения с сервером по части загрузки различных ресурсов. Однако на практике все обстоит гораздо сложнее. Рассмотрим основные проблемы, которые сопровождают разработчиков и остановимся на путях их решения.
Не хватает свободного места
Одной из первых проблем при загрузке данных с сервера является возможная нехватка свободного места на устройстве. Часто бывает, что пользователь использует для игр (особенно на Android) старые устройства, а также и сам размер скачиваемых файлов может быть достаточно большим (привет PC). В любом случае, эту ситуацию необходимо корректно обработать и заранее сообщить игроку, что места не хватает и сколько. Как это сделать? Первым дело необходимо узнать размер скачиваемого файла, это делается по средствам запроса UnityWebRequest.Head(). Ниже представлен код для получения размера.
Здесь важно отметить одну вещь, для правильной работы запроса, сервер должен уметь возвращать размер контента, в противном случае (как, собственно, и для отображения прогресса) будет возвращаться неверное значение.
После того, как мы получили размер скачиваемых данных, мы можем сравнить его с размером свободного места на диске. Для получения последнего, я использую бесплатный плагин из Asset Store.
Примечание: можно воcпользоваться классом Cache в Unity3d, он может показывать свободное и занятое место в кэше. Однако здесь стоит учесть момент, что эти данные являются относительными. Они рассчитываются исходя из размера самого кэша, по умолчанию он равен 4GB. Если у пользователя свободного места больше, чем размер кэша, то проблем никаких не будет, однако если это не так, то значения могут принимать неверные относительно реального положения дел значения.
Проверка доступа в интернет
Очень часто, перед тем, как что-либо скачивать с сервера необходимо обработать ситуацию отсутствия доступа в интернет. Существует несколько способов это сделать: от пингования адреса, до GET запроса к google.ru. Однако, на мой взгляд, наиболее правильный и дающий быстрый и стабильный результат — это скачивание со своего же сервера (того же, откуда будут качаться файлы) небольшого файла. Как это сделать, описано выше в разделе работы с текстом.
Помимо проверки самого факта наличия доступа в интернет, необходимо также определить его тип (mobile или WiFi), ведь вряд ли игроку захочется качать несколько сот мегабайт на мобильном траффике. Это можно сделать через свойство Application.internetReachability.
Кэширование
Следующей, и одной из самых важных проблем, является кэширование скачиваемых файлов. Для чего же нужно это кэширование:
Аналогично, получение данных из кэша.
Примечание: почему для загрузки текстур не используется тот же самый UWR с url вида file://. На данный момент наблюдается проблемы с этим, файл просто напросто не загружается, поэтому пришлось найти обходной путь.
Примечание: я не использую прямую загрузку AudioClip в проектах, все такие данные я храню в AssetBundle. Однако если необходимо, то это легко сделать используя функции класса AudioClip GetData и SetData.
В отличие от простых ресурсов для AssetBundle в Unity присутствует встроенный механизм кэширования. Рассмотрим его подробнее.
В своей основе этот механизм может использовать два подхода:
Итак, каким образом осуществляется кэширование:
В приведенном примере, Unity при запросе на сервер, сначала смотрит, есть ли в кэше файл с указанным hash128 значением, если есть, то будет возвращен он, если нет, то будет загружен обновленный файл. Для управления всеми файлами кэша в Unity присутствует класс Caching, с помощью которого мы можем узнать, есть ли файл в кэше, получить все кэшированные версии, а также удалить ненужные, либо полностью его очистить.
Примечание: почему такой странный способ получения hash значения? Это связано с тем, что получение hash128 способом, описанным в документации, требует загрузки всего бандла целиком, а затем получения из него AssetBundleManifest ассета и оттуда уже hash значения. Минус такого подхода в том, что качается весь AssetBundle, а нам как раз нужно, чтобы этого не было. Поэтому мы сначала скачиваем с сервера только файл манифеста, забираем из него hash128 и только потом, если надо скачаем файл бандла, при этом выдергивать значение hash128 придется через интерпретацию строк.
Работа с ресурсами в режиме редактора
Последней проблемой, а точнее вопросом удобства отладки и разработки является работа с загружаемыми ресурсами в режиме редактора, если с обычными файлами проблем нет, то с бандлами не все так просто. Можно, конечно, каждый раз делать их билд, заливать на сервер и запускать приложение в редакторе Unity и смотреть как всё работает, но это даже по описанию звучит как “костыль”. С этим надо что-то делать и для этого нам поможет класс AssetDatabase.
Для того, чтобы унифицировать работу с бандлами я сделал специальную обертку:
Теперь нам необходимо добавить два режима работы с ассетами в зависимости от того в редакторе мы или же в билде. Для билда мы используем обертки над функциями класса AssetBundle, а для редактора используем упомянутый выше класс AssetDatabase.
Примечание: в коде используется класс TaskManager, о нем пойдет речь ниже, если кратко, то это обертка для работы с Coroutine.
Помимо описанного выше, также в ходе разработки полезно смотреть, что именно мы загрузили и что находится сейчас в кэше. С этой целью можно воспользоваться возможностью установки своей папки, которая будет использоваться для кэширования (в эту же папки можно записывать и скачанные текстовые и другие файлы):
Пишем менеджер сетевых запросов или работа с веб-сервером
Выше мы рассмотрели основные аспекты работы с внешними ресурсами в Unity, теперь бы мне хотелось остановиться на реализации API, которая обобщает и унифицирует все выше сказанное. И для начала остановимся на менеджере сетевых запросов.
Примечание: здесь и далее используется обертка над Coroutine в виде класса TaskManager. Об этой обертке я писал в другой статье.
Заведем соответствующий класс:
Статическое поле NetworkType требуется для того, чтобы приложение могло получать сведения о типе интернет-соединения. В принципе это значение можно хранить, где угодно, я решил, что в классе Network ей самое место.
Как видно из кода, способ обработки завершения запроса изменен, по сравнению с кодом в предыдущих разделах. Это сделано с целью отображения прогресса загрузки данных. Также, все посылаемые запросы сохраняются в списке, с тем чтобы, если это необходимо, их можно было отменить.
Аналогичным образом создаются функции для текстуры, аудио, текста, байт-массива.
Теперь необходимо обеспечить отправку данных сервер через команду Post. Часто нужно, что-то передать серверу, и в зависимости от того, что именно, получить ответ. Добавим соответствующие функции.
Аналогично добавляются методы для текстуры, аудио-файла, текста и т.д.
На этом наш менеджер для работы с сетевыми запроса завершен. По необходимости, каждая подсистема игры, которая требует работы с сервером может создавать свои экземпляры класса.
Пишем менеджер загрузки внешних ресурсов
Помимо описанного выше класса, для полноценной работы с внешними данными, нам нужен отдельный менеджер, который будет не просто скачивать данные, но и уведомлять приложение о начале загрузке, завершении, прогрессе, отсутствии свободного места, а также заниматься вопросами кэширования.
Как видно, в конструкторе задается папка для кэширования в зависимости от того в редакторе мы находимся или нет. Также, мы завели приватное поле для экземпляра класса Network, который мы описали ранее.
Теперь добавим вспомогательные функции для работы с кэшем, а также определения размера скачиваемого файла и проверки свободного места для него. Далее и ниже код приводится на примере работы с AssetBundle, для остальных ресурсов все делается по аналогии.
Итак, что происходит в данной функции:
Аналогично описанному выше методу в менеджере можно/нужно завести и другие функции работы с данными: GetJson, GetTexture, GetText, GetAudio и т.д.
Здесь стоить понимать особенность работы TaskManager, который используется в менеджере сетевых запросов, по умолчанию он работает, выполняя все задачи по очереди. Поэтому загрузка файлов будет происходить соответственно.
Примечание: для тех, кто не любит Coroutine, все можно достаточно легко перевести на async/await, но в данном случае, в статье я решил использовать более понятный для новичков вариант (как мне кажется).
Заключение
В данной статье я постарался как можно более компактно описать работу с внешними ресурсами игровых приложений. Этот подход и код используется в проектах, которые были выпущены и разрабатываются при моем участии. Он достаточно прост и применим в несложных играх, где нет постоянного общения с сервером (ММО и другие сложные f2p игры), однако он сильно облегчает работу, в случае если нам надо скачать дополнительные материалы, языки, осуществить серверную валидацию покупок и другие данные, которые единовременно или не слишком часто используются в приложении.