Русский
Русский
English
Статистика
Реклама

Разработка игр

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

18.02.2021 12:05:10 | Автор: admin


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

Как же и почему это происходит? Мы выбрали из эссе главное.

Говорить, что видеоигры внезапно стали мейнстримом в 2020 г., неверно: они им являются уже несколько десятилетий. Однако сейчас игры заняли куда более важное место в поп-культуре, чем когда-либо прежде и не только для геймеров. Да, отчасти это последствие вызванных Сovid-19 отмен и переносов буквально всего. Отчаявшимся маркетологам пришлось срочно искать новые каналы продвижения. Место Коачеллы и аналогичных мероприятий заняли игры типа Animal Crossing. Показательно, что жаждущие народного одобрения политики, раньше выходившие к электорату на танцпол, теперь стали дружно использовать для тех же целей хайповые игры.


В этом видео представитель Демократической партии Александрия Окасио-Кортес играет в популярную Among Us и попутно агитирует за Джо Байдена. Трансляция собрала 438 000 зрителей и показала всем, насколько эффективна площадка игрового стриминга Twitch в качестве инструмента предвыборной агитации в США.

Игры в центре внимания


Конечно, на столь восторженное восприятие видеоигр во многом повлиял карантин, но, на самом деле, он всего лишь обострил тенденцию, которая началась задолго до этого.
Вспомним, как еще в 2019 г. мировые СМИ обошла новость, удивившая многих: для аудитории поколения Z стример PewDiePie оказался не меньшим авторитетом, чем суперпопулярный баскетболист Леброн Джеймс. Потом выяснилось, что совокупная выручка видеоигр в 2019 году ($139 млрд) превысила доход спортивных лиг NFL, NBA, MLB и NHL вместе взятых.
По оценке Newzoo, в 2020 г. индустрия выросла еще на 20%: выручка достигла $ 160 млрд, а количество геймеров во всем мире составило 2,8 миллиарда человек!

image
Распределение и размер аудитории видеоигр в 2020 г.

Совокупный объем инвестиций в сектор геймдев-индустрии по итогам 2020 г. превысил $13 млрд долларов, на 77% больше, чем годом раньше, а количество сделок на рынке перевалило за 600.
Самые фетишизированные продукты прошлого года игровые платформы: Nintendo Switch весной и Playstation 5 осенью. В очередях за ними теперь стоят и селебрити, позирующие рядом с консолями. Атмосфера вокруг выхода must-have продукта, очень хорошо знакомая по айфонам, когда сначала публика возбужденно ждет старта продаж, а потом из-за высокого спроса все возмущенно пишут ну когда же придет мой заказ?!, теперь также очевидным образом сместилась в сторону видеоигр.

image
Фото из твиттера шоу-звезды Линдсей Лохан.

По своим бюджетам ААА-игры догоняют голливудские блокбастеры. Взять хотя бы Cyberpunk 2077, выход которого стал событием года: разработка проекта с участием Киану Ривза и Граймс обошлась в $317 млн., а это не так уж далеко до $478 млн. стоимости Аватара, одного из самых дорогостоящих фильмов в истории. И пусть релиз игры прошел на фоне скандалов с производительностью, зато даже с учетом возвратов к концу 2020 г. было продано 13 миллионов копий!
По прогнозам, в 2021 году рынок продолжит стремительно расти и приблизится к $190 млрд. выручки.

Геймеры на крючке


Тренд растущего интереса к видеоиграм с энтузиазмом подхватили в музыкальной индустрии и других сферах. Производители неигрового контента и товаров активно взялись за игры как инструмент привлечения, заманивания молодежной аудитории. Автор называет это явление gamerbait (буквально приманка для геймеров) и приводит в качестве примера бренд Balenciaga, который не только стилизовал новую коллекцию под эстетику популярных игр, но и саму ее презентацию оформил как видеоигру. Еще один пример геймербейта выступление Трэвиса Скотта в Fortnite. При помощи игры Скотт за один раз собрал аудиторию вдвое больше, чем удалось MTV Video Music Awards 2020. По итогам коллаборации его концерты посмотрели 37,7 миллионов игроков, а гонорар исполнителя составил не меньше $20 млн. И ведь это не какое-то исключение из правил. Так, в ноябре аналогичное мероприятие провели в онлайн-игре Roblox: концерты рэпера Lil Nas X собрали 33 млн просмотров.

image
Концерт Трэвиса Скотта в Fortnite

То, что игры становятся главным ключом к сердцу детей и подростков, уже понимают и в системе образования. В 2016 г. новость о том, что музеи и библиотеки используют Pokemon Go для привлечения посетителей, еще могла удивить. Сейчас же это очень популярный (и эффективный) подход. Университет Монреаль проводит виртуальные туры по Древнему Египту и античному миру на основе игр серии Assassins Creed. Cyberpunk 2077 стал антуражем для олимпиадных заданий по информатике, разработанных для абитуриентов российским Университетом ИТМО. А буквально на днях Университет Теннесси объявил об учебном курсе под названием Red Dead America. Программа курса охватит американскую историю с 1899 по 1911 год, а в качестве иллюстраций будут демонстрироваться материалы из игр Red Dead Redemption.
И похоже, что в ближайшем будущем нас ждет бурная экспансия видеоигр во все сферы культуры, образования и развлечений, где они будут интегрироваться с традиционными инструментами и форматами.

Культурный сдвиг


image
Музыкант Джонни Сильверхэнд ключевой персонаж Cyberpunk 2077.

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

Как недавно заметил основатель студии Brud Тревор Макфедрис, игры заменяют музыку как стержень социальной жизни, и всем старше 30 лет, с кем я общаюсь, от этого некомфортно. Когда-то рок и хип-хоп были основой стиля, а новое поколение вдохновляется киберпанком и фэнтези в играх. Раньше музыкальные площадки были местом, где формировались новые молодежные движения (рок-клубы для бумеров или гранж-бары для поколения X), но теперь поколение Z встречается с друзьями за играми в Интернете, а тусовки перетекли в Twitch и Discord популярные площадки для геймеров. Согласно недавнему исследованию, 91% зумеров мужского пола регулярно играют в видеоигры, 74% считают, что видеоигры помогают им оставаться на связи со своими друзьями, а для 68% игры неотъемлемая часть их личности. Конечно, подростки не перестали любить музыку, но теперь они предпочитают, чтобы она была частью игр.

Безусловно, триумф гейминга происходит на фоне кризиса в музыкальной индустрии: стриминговые платформы платят исполнителям катастрофически низкие гонорары, а Covid не позволяет зарабатывать на живых выступлениях. Но списывать успехи геймдева лишь на трудности в других индустриях неправильно. Если посмотреть на графику таких игровых проектов, как Ведьмак, Call of Duty или Control, становится понятным, что это действительно одни из ярчайших культурных явлений нашего времени, с огромным влиянием на аудиторию.
И хотя многих это тревожит, но если музыка и была важнейшей формой молодежной культуры в прошлом столетии, то в 21 веке ее место займут видеоигры.

Подробнее..

Перевод Разработка Diablo IV в Blizzard и отладка дампов памяти из Linux в Visual Studio

18.02.2021 16:06:37 | Автор: admin
В блоге Microsoft недавно была опубликована статья, которую написал Билл Рэндольф, старший инженер-программист Blizzard, занимающийся разработкой Diablo IV. В этой статье раскрыты некоторые особенности работы над Diablo IV и, в частности, рассказано об использовании Visual Studio для отладки кода, рассчитанного на Linux. Сегодня мы предлагаем вашему вниманию перевод этого материала.


Введение


Мы, работая над Diablo IV, пишем весь код в Windows, а потом компилируем его для разных платформ. Это относится и к нашим серверам, которые работают под управлением Linux. (Код включает в себя директивы условной компиляции и, там, где это нужно, в нём есть фрагменты, написанные специально для конкретной платформы.) Наша работа организована именно так по многим причинам. Для начала ключевые профессиональные навыки нашей команды относятся к Windows. Даже наши серверные программисты лучше всего знакомы именно с Windows-разработкой. Мы ценим возможность применения одних и тех же инструментов и одной и той же базы знаний всеми программистами нашей команды.

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

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

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

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


О применяемом нами процессе отладки кода


Отладка Linux-кода в Visual Studio возможна лишь в том случае, если в системе установлена подсистема Windows для Linux (WSL), или тогда, когда в менеджере подключений (Connection Manager) настроено подключение к Linux. Все наши серверные разработчики установили WSL, используя дистрибутив, на котором мы разворачиваем наш проект. Мы запускаем написанный мной скрипт, который устанавливает все инструменты разработки и вспомогательные библиотеки, необходимые для сборки нашего сервера в WSL.

(Ненадолго отвлекусь от нашей основной темы. Мне хотелось бы подчеркнуть, что мы пришли к выводу о том, что WSL это лучшее из существующих окружений, позволяющих разработчикам тестировать изменения в Linux-сборках. Крайне удобно выглядит такая схема работы: переход в WSL, использование команды cd для входа в общую директорию с кодом и сборка проекта прямо оттуда. Это гораздо лучшее решение, чем использование виртуальной машины или даже контейнера. Если вы собираете проекты с использованием CMake, это значит, что вы, кроме того, можете задействовать встроенную в Visual Studio поддержку WSL.)

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

Когда один из серверов нашей инфраструктуры даёт сбой, нас об этом уведомляет специальный скрипт, после чего файлы дампа записываются в общую сетевую папку. Для отладки этих файлов, либо в Linux, либо в Visual Studio, необходима работающая программа. При отладке полезно использовать в точности те общие библиотеки, которые применялись в развёрнутом контейнере. Для получения этих файлов мы используем другой скрипт. Сначала мы копируем дамп на локальную машину, а потом запускаем скрипт и передаём ему сведения об этом дампе. Скрипт загружает контейнер Docker, который был собран для исследуемой версии кода, извлекает из него исполняемые файлы нашего сервера, а так же определённые общие библиотеки времени выполнения. Всё это нужно для gdb. (Это, при работе с gdb, позволяет избежать проблем с совместимостью, которые могут возникнуть в том случае, если WSL-версия системы не является точно такой же, как её развёрнутая Linux-версия.) Скрипт, настраивая отладочную сессию, пишет данные в ~/.gdbinit, указывая, что общие библиотеки являются системными библиотеками.

Потом мы переходим в Visual Studio, где и начинается самое интересное. Мы загружаем решение для сборки Windows-версии наших серверов. Затем мы открываем новое диалоговое окно отладки, воспользовавшись командой Debug -> Other Debug Targets -> Debug Linux Core Dump with Native Only. Мы устанавливаем флажок Debug on WSL и вводим пути к файлам дампа и к серверным бинарникам (предназначенным для WSL!). После этого достаточно нажать на кнопку Debug и наблюдать за происходящим.


Запуск отладки в Visual Studio

Visual Studio самостоятельно запускает gdb в WSL. После того, как некоторое время система поработает с диском, выводится стек вызовов программы, давшей сбой, а указатель инструкций устанавливается на соответствующую строку кода. Это, воистину, дивный новый мир!

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

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

А вот Visual Studio даёт нам значительно более мощные возможности, чем имелись у нас раньше. В многопоточных средах можно открыть в отладочной сессии окно Threads и, пощёлкав по потокам, посмотреть их стеки. Это, правда, очень похоже на подход, используемый в gdb. Поэтому, если надо изучить, скажем, 50 потоков, это может превратиться в довольно трудоёмкую и скучную задачу. К счастью, в Visual Studio есть инструмент, значительно упрощающий эту задачу. Речь идёт об окне Parallel Stacks.

Признаю: большинство из нас не знали о Parallel Stacks до тех пор, пока Эрика Свит и её команда не сказали нам об этом. Если во время работы отладочной сессии выполнить команду Debug -> Windows -> Parallel Stacks будет открыто новое окно, которое выводит сведения о стеке вызовов каждого потока в исследуемом процессе. Это нечто вроде вида с высоты птичьего полёта на всё пространство процесса. По любому кадру стека любого потока можно сделать двойной щелчок. После этого Visual Studio перейдёт в этот кадр и в окне исходного кода, и в окне стека вызовов. Это очень сильно помогает нам экономить время.

После того, как мы видим код, находящийся поблизости от места сбоя, мы можем исследовать переменные с использованием мыши, с помощью QuickWatch, или применив любой другой из множества инструментов Visual Studio. Конечно, в релизных сборках многие переменные подвергаются оптимизации, но, в то же время, многие не подвергаются! Мы, применяя интерфейс Visual Studio, можем выявить проблему гораздо быстрее, чем раньше, используя gdb.



Итоги


Наша команда просто счастлива возможностью отлаживать дампы из Linux в Visual Studio, в окружении, в котором мы занимаемся разработкой. Для нас это серьёзное улучшение, так как это позволяет гораздо большему количеству разработчиков, чем раньше, диагностировать проблемы в естественных условиях их возникновения. Это позволяет всем нам пользоваться мощными отладочными инструментами Visual Studio. После того, как завершена предварительная подготовка рабочей среды, оказаться в отладочной сессии Visual Studio можно буквально за считанные минуты. Эта возможность значительно повышает скорость нахождения проблем и эффективность нашей работы. Благодарю Эрику и её команду за помощь.

Какие возможности Visual Studio вы считаете самыми полезными?

Подробнее..

Ink инструмент для создания текстовых квестов как из лучших воспоминаний детства

01.03.2021 16:18:41 | Автор: admin


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

В сравнении с историей литературы этот жанр совсем молодой, зародился в середине прошлого века в виде экспериментов, их даже пробовали использовать для геймификации образования. Расцвет жанра пришелся на 70-е годы. Поначалу интерактивность была довольно условной, с простым ветвлением; читатель всего лишь выбирал варианты развития сюжета в духе: А что если.... Тем не менее, некоторые серии были чрезвычайно популярны. Одна из них, под названием Выбери себе приключение, издавалась почти 10 лет (с 1979 по 1988). Книги серии переводились на множество языков, включая и русский.

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


Игра запущенная на DPD-10

Компьютер дал подобным играм второе рождение. Первый текстовый квест Colossal Cave Adventure был написан на фортране в 1975 году и работал на мейнфрейме PDP-10. Уже тогда рабочие компьютеры использовались как средство развлечения за счет начальства. Создатель игры, Уильям Краудер, был не только программистом, но и спелеологом, потому сюжет был о поиске сокровищ в пещере, у которой был реальный прототип Мамонтова пещера в американском штате Кентукки. ОНа знаменита тем, что является самой длинной в мире (общая длина исследованных проходов больше 500 километров); есть где спрятать сокровища!

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


Одно из реальных мест в пещере, описываемое в игре.

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

В СТОИТЕ В КОНЦЕ ДОРОГИ ПЕРЕД НЕБОЛЬШИМ КИРПИЧНМ ЗДАНИЕМ.

ВОКРУГ ВАС ЛЕС. НЕБОЛЬШОЙ РУЧЕЙ ВХОДИТ ИЗ ЗДАНИЯ И СПУСКАЕТСЯ В ОВРАГ.

иди на юг

В НАХОДИТЕСЬ В ДОЛИНЕ В ЛЕСНОЙ ДОЛИНЕ, РЯДОМ С РУЧЬЕМ, КОТОРЙ СТРУИТСЯ ПО КАМЕННОМУ ДНУ.

Формального сюжета и конкретной цели не существует. У игрока есть три жизни, которых ему должно хватить, чтобы набрать 350 очков, необходимых для выигрыша. Очки выдавались за исследования пещеры и найденные сокровища. Первоначально в игре было 66 локаций, количество которых увеличивалось с каждой следующей версией. Позже развитием игры занялся Дон Вудс, который тогда был аспирантом в Стэнфордском университете. Под его началом в следующей версии количество локаций расширилось до 140. Однако Дон никогда не был в Мамонтовой пещере, и добавленные им локации уже не совпадают с реальностью.

В 1977 году James Gillogly впервые портировал игру на другую систему, а именно на Unix. Что любопытно, этот порт до сих пор входит в состав некоторых дистрибутивов, а если его нет, то можно установить в составе репозитория bsdgames (или отдельно) и играть даже на VPS через SSH.

Установка и запуск игры на Ubuntu
$ sudo apt install colossal-cave-adventure                                           $ adventure                                                           WELCOME TO ADVENTURE!! WOULD YOU LIKE INSTRUCTIONS?                                                                                                                  > yes                                                                    SOMEWHERE NEARBY IS COLOSSAL CAVE, WHERE OTHERS HAVE FOUND FORTUNES IN                                   TREASURE AND GOLD, THOUGH IT IS RUMORED THAT SOME WHO ENTER ARE NEVER                                    SEEN AGAIN. MAGIC IS SAID TO WORK IN THE CAVE. I WILL BE YOUR EYES                                    AND HANDS. DIRECT ME WITH COMMANDS OF 1 OR 2 WORDS. I SHOULD WARN                                     YOU THAT I LOOK AT ONLY THE FIRST FIVE LETTERS OF EACH WORD, SO YOU'LL                                   HAVE TO ENTER NORTHEAST AS NE TO DISTINGUISH IT FROM NORTH.                                      (SHOULD YOU GET STUCK, TYPE HELP FOR SOME GENERAL HINTS. FOR INFOR-                                   MATION ON HOW TO END YOUR ADVENTURE, ETC., TYPE INFO.)                                                          - -                                                     THIS PROGRAM WAS ORIGINALLY DEVELOPED BY WILLIE CROWTHER. MOST OF THE                                   FEATURES OF THE CURRENT PROGRAM WERE ADDED BY DON WOODS (DON @ SU-AI).                                   CONTACT DON IF YOU HAVE ANY QUESTIONS, COMMENTS, ETC.                                                                                                                  YOU ARE STANDING AT THE END OF A ROAD BEFORE A SMALL BRICK BUILDING.                                    AROUND YOU IS A FOREST. A SMALL STREAM FLOWS OUT OF THE BUILDING AND                                    DOWN A GULLY.                                                                                                                                      >

Так как Дон активно распространял исходный код своей версии, то это способствовало росту ее популярности и тому, что она портировалась на каждую новую платформу: Apple, Commodore и, конечно же, MS-DOS.

Версия для MS-DOS была уже с простейшей графикой, содержала 130 локаций, 15 видов сокровищ, 40 предметов для инвентаря и 12 задач, которые требовалось решить по мере прохождения.


Игра на MS-DOS

В 1985 году Дэйв Платт выпустил свою версию игры, где надо было набрать уже 550 очков, но которая кардинально отличалась подходом к программированию таких игр. Он создал универсальный язык написания сценариев A-code, который обрабатывался движком написанным на FORTRAN 77. Это позволило очень легко делать модификации игры и создавать другие текстовые приключения. Практически все современные игры создаются подобным образом, на специальном языке разметки, который интерпретируется движком, как универсальным, в который можно загружать самые разные игры, так и проприетарным, выдающим исполняемый файл, который можно продавать в онлайн-магазинах.

Приключение Colossal Cave Adventure было родоначальником жанра и надолго сформировало каноны подобных квестов, а так же очень сильно повлияло развитие ролевых игр, в том числе и многопользовательских. Жанр MUD создавался по образу и подобию CCA. Даже Дональд Кнут не обошел вниманием эту игру и написал небольшую книгу, посвященную анализу кода игры в обучающих целях.

CCA является настоящим долгожителем. Дон Вудс продолжал выпускать обновления вплоть до середины 90-х, а в 2017 году Эрик Реймонд взял версию игры Дона от 1995 года, отладил ее, чтобы она запускалась на современных компьютерах, и выложил код на своей странице. Существует порт игры на Андроид и переводы ее на русский язык. Поиграть в один из приведенных вариантов можно на сайте Русский Информ, платформе для создания подобных текстовых игр. Можно играть как онлайн, так и скачав файл для движка Windows Frotz.


CCA на Windows Frotz

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

В 2011 году пара разработчиков из Кембриджа занялась производством интерактивных текстовых квестов и придумала средство для упрощения своей работы Inklewriter. Год спустя они представили его в виде сайта, который позиционировался как простое средство для обучения программированию и создания интерактивной литературы. Его даже использовали в школах и удостоили награды Best Website for Teaching and Learning от Американской ассоциации школьных библиотек. Спустя несколько лет разработчики были вынуждены закрыть его по причине нехватки времени на его поддержку, но открыли код своего инструмента для работы (позже они его перезапустили). Ink получил возможность интеграции с Unity, и с его помощью стало возможно с легкостью создавать интерактивные истории, сочетающие в себе не только текст с рисунками, но и музыку с анимацией. Он был взят на вооружение и другими крупными инди-студиями разработки игр, а сама Inklestudios выпустила несколько успешных игр для разных платформ: ссылка. Игры очень милые, другого слова не подобрать. Им удалось заполучить в команду талантливого художника Anastasia Wyatt, что замечательно рисует.


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

Проект Inkle состоит из двух частей: руководство по языку разметки ink; IDE для работы с ним Inky. Начать писать интерактивную историю очень просто. В Inky два окна: слева текст сценария, справа его интерпретация


Начнем новый сценарий: File New Project. Вставим в редактор следующий пример и сохраним в отдельную директорию: File Save Project.

Текст сценария
Лондон, 1872Резиденция месье Филеаса Фога-> london=== london ===Месье Филеас Фогг рано вернулся домой из Реформаторского клуба, да к тому же в новомодном паровом экипаже!Паспарту,  сказал он.  Мы едем вокруг света!+ Вокруг света, месье?Я был крайне удивлен.-> astonished+ [Кивнуть] -> nod=== astonished ===Вы шутите!  ответил я с достоинством.  Вы смеетесь надо мной, месье.Я совершенно серьезен.+ Как скажете, месье.-> ending=== nod ===Я кивнул, не веря ни одному его слову.-> ending=== endingМы совершим кругосветное плавание за восемьдесят дней.  Он был совершенно спокоен, когда предложил этот дикий план.  Через час, в 8.25, мы вылетаем в Париж.-> END


На приведенном фрагменте очень простая структура:

  • тройными знаками = обозначены Узлы (в терминологии Ink), метки для перехода;
  • переход по ним осуществляется с помощью оператора ->.->;
  • пункты выбора обозначаются оператором +;
  • квадратные скобки сообщают интерпретатору, что после выбора название пункта не надо отображать в диалоге;
  • END оператор окончания текста.

Интерпретация всегда идет сверху вниз. Если повествование разделено на узлы, в начале следует сделать переход на основной поток: -> london.

История готова, но выглядит довольно скучно. Добавим в нее изображение. Для этого используется тег # IMAGE.

# IMAGE: days-poster-promo.jpg

Наглядно

Картинка должна лежать там же, где файлы экспортированные для интернета. После внесения изменений экспортировать достаточно один файл: File Export story.js only, остальные содержат шаблона, в который помещается история.

Написав историю, можно экспортировать ее в интернет: File Export for web. В директории проекта появятся несколько файлов: HTML-страница; стилевое оформление в CSS; файл движка ink.js; наш сценарий 80d.js (не редактируйте его напрямую); main.js, где описывается поведение сценария. Если вы знаете CSS, то вам не составит труда отредактировать внешнее оформление истории.

Закончив оформление, историю можно выложить в интернет. Авторы Ink рекомендуют воспользоваться порталом itch.io. Это бесплатный портал с простой регистрацией. При создании проекта для Ink выберите Kind of project HTML, загрузите архив с файлами проекта и отметьте опцию This file will be played in the browser. Можно посмотреть, что получилось: ruvds.itch.io/inky-test.

Если вам в свое время тоже хотелось дополнить текстовые квесты Рейнджеров или написать свой текстовый квест, то не стоит сдерживать свою фантазию дерзайте вместе с Inky!

Подробнее..

Интервью с Яной Артищевой обучение в НИУ ВШЭ ВШБИ и страсть к VR-играм

21.02.2021 22:15:46 | Автор: admin

Яна Артищева закончилапрограмму НИУ ВШЭ ВШБИ Менеджмент игровых проектов(МИП). Теперь делает игры для виртуальной реальности. Поговорил с ней насчёт любимых игр детства, перспектив и проблем VR и фишек обучения геймдеву в НИУ ВШЭ ВШБИ. Интервью выходит при поддержке МИП.

1. Почему решили прийти в игровую индустрию? Сложно было перейти?

Сцена из древней игры Gabriel Knight: Sins of the Fathers компании Sierra On-LineСцена из древней игры Gabriel Knight: Sins of the Fathers компании Sierra On-Line

Любимыми компьютерными играми моего детства были игры компании Konami (спасибо, Кодзима!), в которые я играла на школьных компьютерах Yamaha MSX. Просто играть было мало, сразу же захотелось сделать сделать собственную игру. И я окунулась в изучение программирования - Basic, ассемблер и машинные коды, затем Pascal, C, Java С тех пор я постоянно пробовала писать игры. Но профессией это стало только в начале 2000х, когда появились мобильные телефоны с играми на Java. Мобильные игры возродили жанр старых "8-битных" казуалок и пиксел-арта. Если для создания качественной игры для PC требовалась Команда, Деньги и Месяцы (а нередко и Годы), то сделать игру для мобильного телефона можно было вдвоём с подругой-художницей за 1-2 месяца. И в 2003 году мы с друзьями создали небольшую компанию. Идея проекта была в том, чтобы игры были бесплатны, а деньги зарабатывались показами рекламы между уровнями. В общем, в 2003 мы изобрели то, что много лет спустя назовут free2play ;) К 2005 году мы выпустили дюжину небольших казуальных игр, сделали баннерную сеть с таргетингом, статистикой и прочими фишками. Нашли первых клиентов - Евросеть, издательство CD-Land (они предоставляли призы для наших еженедельных турниров), реселлера BeeLine. Мы быстро подняли аудиторию до 80,000 уникальных пользователей - больше, чем аудитория журнала "Мобильный портал", где мы давали рекламу. Но как мы ни старались, нам не удалось найти рекламодателей, готовых заплатить реальными деньгами, а не компакт-дисками и прочими призами. В 2007 году мы свернули проект, а игры сдали в аренду Samsung Fun-Club'у. В минусе не остались, но и особо не разбогатели. А через год или два встроенная реклама появилась в играх на андроидах и айфонах. Но уже без нас. Так я получила первый урок, что недостаточно сделать хороший и востребованный продукт. Кроме разработчиков продукту жизненно необходим продажник!

2. Как вам помогла образовательная программа НИУ ВШЭ "Менеджмент игровых проектов"? Что вы почерпнули? Какие, по-вашему, плюсы и минусы этого курса?

Выпуск курса МИП-7 ВШБИ, 2018 годВыпуск курса МИП-7 ВШБИ, 2018 год

Когда я услышала от своего коллеги по Azur Games, чтопрограмма Менеджмент игровых проектов просто огонь, мне показалось, что он несколько переигрывает с восторженностью. Теперь я и сама могу сказать, что курс МИП-7 одна из самых ярких страниц моей жизни. Серьёзно! Нет ничего более захватывающего, чем учиться тому, что даёт тебе самый дикий драйв в жизни. Когда среди учебных предметов нет ни одного скучного, неинтересного или ненужного. Когда тебя окружают единомышленники. Это куда круче, чем обычный ВУЗ. Мне удалось увидеть разработку игры с точки зрения других участников процесса продюсера, маркетолога, аналитика, коммьюнити-менеджера, пиарщика. Ретеншн нулевого дня, выгорание и прочие MAU-DAU для меня больше не птичий язык. Были бы у меня эти знания лет 15 назад кто знает, как бы сложилась судьба моих канувших в Лету проектов telegames.ru и AR-Go. Что же касается минусов Очень жаль, что этот курс так быстро закончился. И огромный минус для нынешних студентов то, что эпидемия коронавируса перенесла большую часть курса в онлайн. Нетворкинг, сотрудничество между студентами, живая помощь преподавателей, ставших твоими друзьями это сложно переоценить. Не знаю, удалось ли в онлайне сохранить этот драйв, этот крутейший team spirit.

3. Почему именно VR?

VR-игра Wolfenstein: Cyberpilot компании Arkane Studios/Machine GamesVR-игра Wolfenstein: Cyberpilot компании Arkane Studios/Machine Games

Создавать собственные миры непередаваемое чувство. Ты придумываешь мир, его логику (или алогичность), настроение, как он будет выглядеть. А потом Потом случается чудо. Ты не просто видишь этот мир в проекции на маленький прямоугольничек 40-дюймового дисплея. Тыоказываешьсявнутри придуманного и созданного тобой мира. Религия говорит, что Бог создал человека по своему образу и подобию? Думаю, нашему создателю доставляет радость видеть, как его дети становятся как и он Творцами. Кто-то творит миры на страницах своих книг. Кто-то дарит жизнь мирам Пандоры и мира Льда и Пламени. А кто-то, подобно Рене Корда из "Нейромансера" Роджера Желязны, создаёт почти реальные, почти осязаемые миры. VR позволяет почувствовать себя творцом, создателем, волшебником, создающим живой мир, в начале которого было Слово (и слово было гейм-дизайн документ).

4. Какие VR-игры вам нравятся?

Год назад мне посчастливилось принять участие в совместном проекте Эрмитажа и компании КРОК по воссозданию виртуальной копии нескольких залов музея ЭрмитажГод назад мне посчастливилось принять участие в совместном проекте Эрмитажа и компании КРОК по воссозданию виртуальной копии нескольких залов музея Эрмитаж

Когда-то я запоем играла в квесты тогда этим словом назывались не задания в рамках игры, а отдельный класс игр, где основу геймплея составлял нарратив, история, которую тебе нужно разгадать и пройти. Space Quest, Gabriel Knight: Sins of Fathers, Leisure Suit Larry, Secret of Monkey Island, Loom, Torin's Passage, Kyrandia, Neverhood это неполный список игр-приключений, которые сформировали мои геймерские пристрастия. Нарратив, история, в которую ты погружаешься для меня это основное удовольствие от игры. Квесты, перенесённые в VR это уже не просто приключение, которое ты помогаешь пройти персонажу на экране компьютера или мобильника. Это ещё одна жизнь, которую ты проживаешь. Это Storytelling, превратившийся в Storyliving. Из VR-игр для меня стала настоящим открытием Hellblade: Senua's Sacrifice. У игры есть множество недостатков. Это и кошмарный UI, абсолютно несовместимый с VR, и чисто программерские баги, и многое другое. Но замечательная история, прекрасный визуал, абсолютно естественные, реалистичные движения и мимика заставляют забыть обо всех минусах, запоем пройти игру в 3 дня за 27 часов. И ты бежишь в магазин за геймпадом, когда твои пальцы атрофировались от попыток вслепую (VR же) бегать по клавиатуре Буду банальной, но сейчас для меня лучшей VR-игрой стала Half-Life: Alyx. Это очень чисто и качественно сделанный продукт. Alyx определённо подняла планку для VR-игр и в плане графики, и в плане геймплея. Игра не позволяет тебе скучать, надолго застряв на каком-то этапе. Это настоящий шедевр. Неожиданно понравилась игра Wolfenstein: Cyberpilot. Да, у неё размытая графика, она проходится за одно утро. Но увлекательная история, качественное русское озвучание, французский шарм все эти моменты определённо меня покорили. Ещё у меня в фаворитах The Walking Dead: Saints & Sinners и Westworld: Awakening. Но пока они который месяц ждут, когда закончится мой роман с Alyx. Немного разочаровала игра Obduction. Я несколько лет ждала этот тайтл от создателей легендарного M.Y.S.T. (в который я в своё время так и не сыграла). Но слишком пустынный и статичный мир, которому плевать, что ты третью неделю не можешь продвинуться А тебе есть, куда уйти и я ушла в Alyx и Westworld. Когда-нибудь я непременно вернусь в Obduction. Непременно. Когда-нибудь. Наверное

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

Но не нарративом единым. Весьма неплоха оказалась VR-версия Angry Birds. А Beat Saber, этот хит всех времён и народов я долгое время презирала за примитивность. Хотя тут скорее всего взыграла ревность, что не я придумала такую простую и популярную игру. В итоге приняла и полюбила. Beat Saber стал первой покупкой для моего Oculus Quest.

5. Как оцениваете нынешнее состояние и будущее VR?

Виртуальная выставка картин художника Александра Верстова в старинном замке VR-игры "Owling Crowling Bowling". Иллюстрация предоставлена Яной АртищевойВиртуальная выставка картин художника Александра Верстова в старинном замке VR-игры "Owling Crowling Bowling". Иллюстрация предоставлена Яной Артищевой

Что касается B2B VR он уверенно занял своё место под солнцем, и в его будущем особых сомнений ни у кого нет. А вот домашний VR много лет постоянно пытаются хоронить. В том, что у бытового, домашнего VR есть будущее, я не сомневаюсь. Есть игроки, для которых важен нарратив. Есть любители крутого визуала. VRжевыявил ещё одну категорию игроков: иммерсионисты те, кому важно погружение в игру. Начав в 2015 активно играть в VR-игры на лучшей на тот момент мобильной VR-платформе Gear VR, я через пару лет обнаружила, что больше не могу в плоскоэкранные игры. Я честно пыталась заставить себя играть в Far Cry 5, в Метро 2033 и бросала, не отыграв и пары дней. Вместо удовольствия от игры на плоском экране я чувствовала недоумение что я делаю? зачем? Даже Armikrog, сиквел любимейшего много лет назад Neverhood, не удержал меня и недели Последний год армиягеймеровдомашнего VR пополнилась владельцами шлемов Oculus Quest. И последние полгода я постоянно встречаю в фейсбучных группах, посвящённых Квесту, изумлённые посты: Я больше не получаю удовольствия от обычных игр! А вы? Да, рынок пока невелик по объёму в сравнении с мобильным или PC-рынком. Но он уверенно растёт. Нет недостатка в качественных VR-играх. Многие американские семьи покупают по 2-3 шлема Oculus Quest и делают полноценные домашние VR-зоны, о которых владельцы отечественных малогабариток и мечтать не могут Полагаю, в ближайшие 2-3 года рост VR-аудитории ускорится, а лет через 5 она будет вполне сравнима с аудиторией игровых консолей.

6. Какие жанры, по-вашему, больше подходят для VR?

Lost Horizon музыкальный фестиваль в VRLost Horizon музыкальный фестиваль в VR

Однозначно спортивные и фитнесс-игры. Никакая мобильная или PC-игра не заставит вас потерять столько калорий и пропотеть так, как это сделает Beat Saber или Knockout League. Раньше на этом поле пытались играть Nintendo Wii и Kinect. Теперь их место занял домашний VR.

Социальные VR-приложения только в этом году в VR прошло множество конференций, выставок, концертов, фестивалей: Last Horizon,Laval Virtual 2020, goEast А какие бесплатные концерты в формате 360 проводит МТС! Это же потрясающе находясь на самоизоляции, надеть VR-шлем и два часа зажигать на живом концерте любимой группы!

Даже наш инди-проект Owling? Crowling? Bowling! не оказался в стороне в начале июня в интерьере виртуального готического замка нашей игры прошла выставка картин художника Александра Верстова. Осенью в нашей игре запланирована выставка известного фото-художника.

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

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

7. Какие сложности возникли при разработке для VR?

Процесс игры на VR-аренеПроцесс игры на VR-арене

Сложностей, увы, пока немало.

  • Прежде всего отсутствие отработанных приёмов UX/UI. Пока это сплошной R&D с более или менее удачными находками.

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

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

  • VR-игрок как правило не сидит за компьютером, а активно двигается на игровой площадке. Если для домашнего пользователя можно ограничиться площадкой 2x2 метра, то площадки для VR-арен могут доходить до нескольких сотен квадратных метров. Дома или в офисном опенспейсе игру для VR-арены не разработать, нужна большая площадка для тестирования хотя бы 10x10 метров.

  • У PC VR другие требования к технике нужны довольно мощные компьютеры, а для VR-арен и специализированные устройства. Стоимость оборудования для VR-арены (камеры Optitrack, компьютеры-ранцы, шлемы и др.) может достигать многих миллионов долларов.

8. Чем отличается разработка для VR от обычного геймдева? Другие специалисты, методология?

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

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

Dragonsnake VRDragonsnake VR

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

Не каждый игрок способен провести в виртуальной реальности дольше 15 минут (хотя я легко могу проводить в VR по 8-9 часов непрерывно). Это тоже приходится учитывать при планировании игровой сессии.

Визуальное программирование в Unreal Engine с помощью blueprintsВизуальное программирование в Unreal Engine с помощью blueprints

Лично мне очень не хватает возможности работать с VR-контентом непосредственно в виртуальной реальности. Сейчас наконец стали появляться подобные инструменты. Unreal Engine позволяет работать со сценой, одев VR-шлем. Из средств 3D-моделлинга стоит обратить внимание на Gravity Sketch. Но, к сожалению, пока не появилось решений для полноценной разработки логики приложения в VR. Есть попытки перенести в VR редактирование исходных текстов, но это определённо не самая удачная идея. Более интересны эксперименты с визуальным программированием в VR соединяя между собой визуальные блоки (blueprints), разработчик задаёт логику виртуального мира. Увы, простое копирование способа разработки с "плоского экрана" в виртуальную реальность обречено на неудачу. Для разработки программной логики непосредственно в виртуальной реальности нужны новые идеи и решения, "заточенные" именно под VR.

Интересно, что с недавнего времени мы перенесли наши еженедельные брифинги по игре из Skype в саму игру. Запустив её, мы в облике студентов магической академии собираемся в старинном замке, где разворачивается история Owling Crowling Bowling. Кто-то надев VR-шлем, кто-то сидя перед экраном своего компьютера. Теперь во время совещаний мы можем подойти к конкретной точке игровой локации, обсудить, что не так с текстурой или освещением буквально показать пальцем, что требуется доработать. До карантина мы собирались в кафе, чтобы протестировать физику нашей игры. Теперь мы тестируем её непосредственно в VR. Ну и, конечно, непередаваемое ощущение, когда ты не просто делаешь игру, а создаёшь мир, внутри которого можешь оказатьсявнутри, а не смотреть на его плоскую проекцию через окошечко дисплея!

9. Как оцениваете состояние образования по геймдеву вообще и по VR в частности?

За последние годы появилось множество курсов, помогающих "войти" в геймдев XYZ School, Нетология и множество других. Я давно работаю в игровой индустрии, и тем не менее, несколько лет назад прошла курс "Менеджмент игровых проектов" в Высшей Школе Экономики. Мне захотелось взглянуть на процесс разработки и продвижения игр не только с программерской позиции, но и с точки зрения маркетолога, геймдизайнера, биздева и др. Курс оставил самые яркие впечатления! Теперь мне гораздо проще находить общий язык с коллегами по проекту. Появились новые профессиональные знакомства. Что касается обучения VR-разработке, возможно и есть хорошие очные курсы, но я о таких не знаю. Зато есть немало обучающих онлайн-ресурсов. У Вышки был отличный крус VR-разработки на Unreal Engine, но он был закрыт из-за недавней смерти Станислава Маеренко, одного из преподавателей курся. Стоит изучить уроки и туториалы на ресурсах Unity 3D и Unreal Engine. Весьма неплохой цикл лекций от лондонского университета (University of London) по созданию VR-персонажей я нашла на Coursera. Ну и на YouTube огромнейшее количество разнообразных мануалов и туториалов самого разного качества. Хотя по моему глубокому убеждению, и создание контента виртуальной реальности, и обучение VR-разработке со временем переместятся в виртуальную реальность ведь это наиболее естественная среда для работы с виртуальной реальностью.

Вот такая вышла беседа. Мог бы повториться, о чём говорили, но вы ведь это уже видели. Спасибо, что читаете. Будем популяризировать геймдев в месте. Глядишь, и станет он у нас гордым и классным.

Подробнее..

Свежий взгляд на честное 3D в браузере

23.02.2021 22:04:36 | Автор: admin

Приветствую.

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

Общие положения

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

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

Полигоны в пространстве

Утверждение: Любую фигуру можно представить в некотором конечном приближении как совокупность полигонов

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

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

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

Утверждение: Любую фигуру можно представить в некотором конечном приближении как совокупность ограниченных частей поверхностей второго порядка.

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

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

Отрисовка

Однако остается вопрос - как преобразовать множество поверхностей в полигоны? Тут все оказывается еще более тривиально - достаточно пустить множество лучей из камеры. В точках пересечения луча и поверхности можно ставить точку. А уже затем из данных точек формировать полигоны.

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

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

Непосредственно, код

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

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

Точка - самая базовая сущность, все, что она делает - хранит и получает/устанавливает собственные координаты, а также - измеряет расстояние до товарки.

class Point{    x=0;    y=0;    z=0;    constructor(x,y,z) {        this.x = x?x:0;        this.y = y?y:0;        this.z = z?z:0;    }    get x(){        return this.x;    }    set x(x){        this.x = x;    }    get y(){        return this.y;    }    set y(y){        this.y = y;    }    get z(){        return this.z;    }    set z(z){        this.z = z;    }    toString(){        return `{"x": ${this.x}, "y": ${this.y}, "z": ${this.z}}`;        //for debug    }    /**     * @param {object} point1 - coordinates of first point     * @param {object} point2 - coordinates of second point     * @returns {number} - distance between points     */    static getDistance(point1,  point2){        return Math.sqrt(Math.pow(point2.x - point1.x, 2) +Math.pow(point2.y - point1.y, 2)+            Math.pow(point2.z - point1.z, 2)  )    }}//console.log(new Point(2,3,4).toString()); //debug todo removeexport  {Point}

Однако точка - слишком мелкая единица. Введем код для полигона - пока что как как просто координаты трех точек в пространстве, и для несколких методов работы с ним.

 /**   * @returns {object} - coefficients for equation of plane,   * that contains current polygon   */  getPlaneEquation(){    let a1 = this.point2.x - this.point1.x;    let b1 = this.point2.y - this.point1.y;    let c1 = this.point2.z - this.point1.z;    let a2 = this.point3.x - this.point1.x;    let b2 = this.point3.y - this.point1.y;    let c2 = this.point3.z - this.point1.z;    let a = b1 * c2 - b2 * c1;    let b = a2 * c1 - a1 * c2;    let c = a1 * b2 - b1 * a2;    let d = (- a * this.point1.x- b * this.point1.y - c * this.point1.z);    let plane = {      a:a,      b:b,      c:c,      d:d    }    return plane  }  /**   * @param {object} line - line in space crossing this plane   * @param {object} plane - coefficient for equation of plane that is crossed by line   * @returns {object} - coordinates of point of crossage   */  getCrossingPointOfLIneAndPlane(line, plane){    let point1 = line.point1;    let point2 = line.point2;    let n= point2.y - point1.y;    let m = point2.x - point1.x;    let p = point2.z - point1.z;    let matrixLeft = [      [n, -1*m, 0],      [p, 0, -1*m],      [plane.a, plane.b, plane.c]    ]    let matrixRight = [[n*point2.x-m*point2.y],[p*point2.x-m*point2.z],[-1*plane.d]]    let matrixLeft_1 = MatrixOperations.InverseMatrix(matrixLeft);    let solution = MatrixOperations.MultiplyMatrix(matrixLeft_1, matrixRight);    let point = {      x:solution[0][0],      y:solution[1][0],      z:solution[2][0],    }    return point;  }  /**   * @param {object} point - some point in space   * @returns {boolean} - is that point inside of current polygon   */  isInsidePolygon(point){    let squareFull = this.getSquare(this.point1, this.point2, this.point3);    let square1 = this.getSquare(point, this.point2, this.point3);    let square2 = this.getSquare(point, this.point1, this.point3);    let square3 = this.getSquare(point, this.point2, this.point1);    const dimension = 0.0001;    /*console.log("s1 " + square1)    debug todo remove    console.log("s2 " + square2)    console.log("s3 " + square3)    console.log("s " + squareFull)*/    return Math.abs(squareFull - (square1+square2+square3)) < dimension;  }  /**   * @param {object} polygon - polygon is crossing need to check   * @returns {object} - 3 points that are lying on prolongation of passed polygon borders   * and in current polygon plane   */  getPolygonBorderLinesCrossPoints(polygon){    let plane = this.getPlaneEquation();    let crossPoint1 = this.getCrossingPointOfLIneAndPlane(new Line(polygon.point1, polygon.point2), plane);    let crossPoint2 = this.getCrossingPointOfLIneAndPlane(new Line(polygon.point1, polygon.point3), plane);    let crossPoint3 = this.getCrossingPointOfLIneAndPlane(new Line(polygon.point3, polygon.point2), plane)    let crossingPoints = {      p1: crossPoint1,      p2: crossPoint2,      p3: crossPoint3,    }    return crossingPoints;  }  /**   * @param {object} polygon - polygon is crossing need to check   * @returns {boolean} - is any of border of passed polygon crossing my plane inside my borders   */  doesPolygonCrossMe(polygon){    let points = this.getPolygonBorderLinesCrossPoints(polygon);    return this.isInsidePolygon(points.p1) && polygon.isInsidePolygon(points.p1)        ||        this.isInsidePolygon(points.p2) && polygon.isInsidePolygon(points.p2)        ||        this.isInsidePolygon(points.p3) && polygon.isInsidePolygon(points.p3);  }  /**   * @param {object} polygon - polygon is crossing need to check   * @returns {boolean} - is passed polygon crossing me or am I crossing it   */  arePolygonsCrossing(polygon){     //needs tests    return this.doesPolygonCrossMe(polygon) && polygon.doesPolygonCrossMe(this);  }  /**   * @param {object} point1 - vertex of triangle   * @param {object} point2 - vertex of triangle   * @param {object} point3 - vertex of triangle   * @returns {float} - square of triangle   */  getSquare(point1, point2, point3){    let a = Point.getDistance(point1, point2);    let b = Point.getDistance(point2, point3);    let c = Point.getDistance(point1, point3)    let p = (a+b+c)/2;    let square = Math.sqrt(p*(p-a)*(p-b)*(p-c));    return square;  }

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

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

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

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

 getPointsOfCrossedByLine(line) {        let a = this.a2 * Math.pow(line.getEquation().m, 2) +            this.b2 * Math.pow(line.getEquation().n, 2) + this.c2 * Math.pow(line.getEquation().p, 2);        let b = this.a2 * 2 * line.getEquation().m + line.getEquation.m * this.a1 +            this.b2 * 2 * line.getEquation().n + line.getEquation.n * this.b1 +            this.c2 * 2 * line.getEquation().p + line.getEquation.p * this.c1;        let c = this.a2 * line.getEquation().x1 * line.getEquation().x1 + this.a1 * line.getEquation().x1 +            this.b2 * line.getEquation().y1 * line.getEquation().y1 + this.b1 * line.getEquation().y1 +            this.c2 * line.getEquation().z1 * line.getEquation().z1 + this.c1 * line.getEquation().z1 + this.d0;        let t = this.quadraticEquation(a, b, c);        return t.map((value) => {            return new Point(line.getEquation().x1 + value * line.getEquation().m,                line.getEquation().y1 + value * line.getEquation().n,                line.getEquation().z1 + value * line.getEquation().p,            )        })    }    getPointsOfCrossedByLineArray(lineArray) {        let rezultArray = [];        for (let i of lineArray) {            let temp = this.getPointsOfCrossedByLine(i);            for (let j of temp)                rezultArray.push(j)        }        return rezultArray;    }    getSortedGraphOfPoints(lineArray) {        let pointsNotVisited = this.getPointsOfCrossedByLineArray(lineArray);        let visitedPoints = [];        visitedPoints.push(pointsNotVisited[0])        pointsNotVisited.shift();        while (pointsNotVisited.length) {            pointsNotVisited.sort((p1, p2) => {                return Point.getDistance(visitedPoints[visitedPoints.length - 1], p1) -                    Point.getDistance(visitedPoints[visitedPoints.length - 1], p2);            })            visitedPoints.push(pointsNotVisited[0])            pointsNotVisited.shift();        }    }    getPolygonMeshingArray(sortedPointsArray) {        let polygonArray = [];        for (let i = 9; i < sortedPointsArray - 3; i++)            polygonArray.push(new Polygon(sortedPointsArray[i], sortedPointsArray[i + 1], sortedPointsArray[i + 2]))        return polygonArray;    }

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

Заключение

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

Подробнее..

Приглашаю к созданию русской народной игры Колобок

21.02.2021 16:09:36 | Автор: admin

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


Почему Колобок


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


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


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


Как я готоволю Колобка


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


Т.к. всё, что видно на экране, является спрайтом (изображением), то желаемый элемент игры я:


  • сначала рисую на обычном листе бумаги
  • фотографирую нарисованные каракули на телефон
  • обрезаю изображение в GIMP
  • сохраняю изображение в PNG
  • создаю в GDevelop новый спрайт на основе изображения PNG

Самым сложным элементом на текущий момент являлся кот, т.к. мне нужно было несколько состояний кота:


  • сидит без эмоций;
  • сидит недовольный;
  • спрыгивает с подоконника;
  • лижет пролитое молоко.

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




Ближе к делу


Основная идея текущей сцены в том, чтобы колобок через окно смог удрать из избы.


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


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


Если у вас зачесались руки, то добро пожаловать в уютную группу Discord.


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

Подробнее..

Унификация поиска пути вместо различной логики ИИ

11.02.2021 12:14:32 | Автор: admin

Для одной незамысловатой игры мне понадобилось реализовать поведение ИИ с базовым функционалом: патрулирование, преследование и ведение боя. Сама по себе задача простая, однако, типов локаций было два и с разными уровнями абстракций.

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

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

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

Реализация

Основные объекты:

  • IPath<TPoint> (данные о пути)

  • IPathProvider<TPoint> (поисковик или объект предоставляющий путь)

  • IPathResponse<TPoint> (содержащий путь ответ, получаемый от поисковика)

  • IPathRequestToken<TPoint> (токен для формирования ответа)

IPath

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

public interface IPath<TPoint>{    // Текущая используемая точка.    TPoint Current { get; }    // Коллекция всех точек.    IEnumerable<TPoint> Points { get; }    // Метод для перехода к следующей точке из коллекции.    bool Continue(TPoint origin);}

Хоть изначально IPath предназначался для простого представления данных о пути, однако в процессе написания кода лично я не хотел бы вручную контролировать смену точек, индексов, какие-то проверки на null, к тому же логика у всех ИИ в данном случае будет одинакова, а разница заключается лишь в функции перехода. Поэтому было добавлено определение метода Continue.

Сделаем реализацию пути пустого. Но для чего? Разве нельзя представлять пустой путь как null? И это возможный вариант, но он добавит нам ручной работы для проверок на существование данных, в то время как реализация в виде конкретного объекта просто не позволит боту передвигаться, т.к. все время будет возвращать отрицательный результат.

public class EmptyPath<TPoint> : IPath<TPoint>{    public TPoint Current => default(TPoint);    public IEnumerable<TPoint> Points => null;        public bool Continue(TPoint origin) => false;}// Исключение, которое нужно бросить при пустых результатах.public class EmptyPathException : Exception{    public EmptyPathException()        : base("Path is empty! Try using EmptyPath<TPoint> instead of Path<TPoint>")    {}}

Добавим стандартную реализацию для пути:

public class Path<TPoint> : IPath<TPoint>{    // Функция перехода.    // Проверяет необходимость смены текущей точки.    protected readonly Func<TPoint, TPoint, bool> ContinueFunc;    protected readonly IEnumerator<TPoint> PointsEnumerator;        // Текущая точка.    public TPoint Current { get; protected set; }    // Коллекция точек.    public IEnumerable<TPoint> Points { get; protected set; }        // Продолжено ли движение по пути.    // Внутреннее свойство.    public bool Continued { get; protected set; }        public Path(IEnumerable<TPoint> points, Func<TPoint, TPoint, bool> continueFunc)    {        // Мы не должны допускать пустых данных.        if(points == null)            throw new EmptyPathException();                ContinueFunc = continueFunc;        PointsEnumerator = points.GetEnumerator();                Points = points;                // Изначально указатель никуда не указывает         // и его нужно сдвинуть на первый элемент.        MovePointer();    }    // Проверка на возможность продолжения перемещения.    public bool Continue(TPoint origin)    {        // Если нужно двигаться к следующей точке.        if (ContinueFunc(origin, Current))            MovePointer();                // Продолжен ли путь.        return Continued;    }         // Передвигаем указатель на следующий элемент,    // если возможно продолжить путь.    protected void MovePointer()    {        // Если есть элементы в коллекции точек.        if (PointsEnumerator.MoveNext())        {            Current = PointsEnumerator.Current;            Continued = true;        }        else        {            // Путь невозможно продолжить            Continued = false;        }    }}

ДелегатFunc<TPoint, TPoint, bool> ContinueFunc нужен для проверки текущей цели (точки, к которой мы двигаемся). Если бот подойдет к цели, то ее логично будет сменить на следующую точку в пути. Этот делегат передается извне.

ПеречислительIEnumerator<TPoint> PointsEnumerator нужен для ручного обхода по коллекции точек.

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

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

Да и к тому же нам все еще нужно знать к кому обращаться за поиском пути :)

IPathProvider и IPathResponse

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

IPathProvider<TPoint> интерфейс, сообщающий нам о том, что у объекта можно запросить путь нужного нам типа. Один объект может реализовывать несколько вариантов этого интерфейса. Определение поисковика:

public interface IPathProvider<TPoint>{    // Метод запроса, возвращающий путь, но внутри обекта ответа.    IPathResponse<TPoint> RequestPath(TPoint entryPoint, TPoint endPoint);}

Определение ответа на запрос:

public interface IPathResponse<TPoint>{    // Флаг о готовности данных пути.    bool Ready { get; }    // Сам путь, который может быть null.    IPath<TPoint> Path { get; }}

IPathResponse<TPoint>содержит в себе путьPathи флагReady, сигнализирующий о завершении поиска пути провайдером. При асинхронном/многопоточном вычислении флаг не сразу может быть со значением true.

Синхронная реализация ответа выглядит следующим образом:

public sealed class PathResponseSync<TPoint> : IPathResponse<TPoint>{    public bool Ready { get; private set; }    public IPath<TPoint> Path { get; private set; }    public PathResponseSync(IPath<TPoint> path)    {        if(path == null)            throw new EmptyPathException();        Path = path;        Ready = true;    }}

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

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

Токен:

public sealed class PathRequestToken<TPoint>{public bool IsReady { get; private set; }    public IPath<TPoint> Path { get; private set; }        public void Ready(IPath<TPoint> path)    {    if (path == null)        throw new EmptyPathException();                IsReady = true;        Path = path;    }        }

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

Ответ:

public sealed class PathResponse<TPoint> : IPathResponse<TPoint>{    private readonly PathRequestToken<TPoint> _token;        public bool Ready => _token.IsReady;    public IPath<TPoint> Path => _token.Path;    public PathResponse(PathRequestToken<TPoint> token)    {        _token = token;    }    // Метод для упрощенного создания объектов ответа и токена.    public static void New(out PathRequestToken<TPoint> token,        out PathResponse<TPoint> response)    {        token = new PathRequestToken<TPoint>();        response = new PathResponse<TPoint>(token);    }}

Реализация класса асинхронного/многопоточного ответа подходит и для синхронных вычислений.

Здесь нет установки значений, только обращение к значениям токена и один статический метод для удобства создания объектов ответа и токена.

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

Теперь у нас есть все, чтобы абстрагироваться от конкретных алгоритмов при этом не меняя логику ИИ! К тому же мы избавились от надобности знать способ выполнения алгоритма: синхронный он или нет нас не интересует благодаря IPathResponse.

Пример использования

В классе ИИ я определил поля для ответа и провайдера, а методе Update содержится логика запроса и следования:

..private IPathProvider<Vector3> _pathProvider;private IPathResponse<Vector3> _pathResponse;..  public override void Update(float deltaTime){    // Обновление пути при преследовании.    _pathUpdateTimer += deltaTime;    if (_pathUpdateTimer >= Owner.PathUpdateRate)    {        _pathUpdateTimer = 0f;                        if (Target == null)            Target = _scanFunction(Owner);        if (Target == null)            return;                // Запрашиваем путь у поисковика.        _pathResponse = _pathProvider            .RequestPath(Position, Target.transform.position);    }    // Следование по пути, если есть ответ и путь просчитан.    if (_pathResponse != null)    {        // Если данные о пути готовы к использованию        if (_pathResponse.Ready)        {            var path = _pathResponse.Path;            // Объект пути вычисляет надобность в смене следующей точке            // и возможности дальнейшего передвижения.            if (path.Continue(Position))            {                // Какая-то логика передвижения                var nextPosition = Vector3.MoveTowards( Position, path.Current,                    Owner.MovementSpeed * deltaTime);                                    Position = nextPosition;            }        }    }      }

Функция для для перехода по точкам:

public static bool Vector3Continuation(Vector3 origin, Vector3 current){    var distance = (origin - current).sqrMagnitude;    return distance <= float.Epsilon;}

Ну и пример поисковика:

public IPathResponse<Vector3> RequestPath(Vector3 entryPoint, Vector3 endPoint){    // ЗДЕСЬ БЛА ЛОГИКА, НО ЕЕ УКРАЛО НЛО...    // Найденный путь с объектами типа LinkedAPoint.    var pathRaw = _jastar.FindPath(startPointJastar, endPointJastar);                // Если пути нет, то возвращается синхронный ответ с пустым путем.    if(pathRaw.Count == 0)        return new PathResponseSync<Vector3>(new EmptyPath<Vector3>());      var vectorList = pathRaw.ToVector3List();      // Возвращение пути со списком точек и заданной функцией продолжения.    return new PathResponseSync<Vector3>(        new Path<Vector3>(vectorsList, PathFuncs.Vector3Continuation));}

Посмотреть исходники можно здесь. Также там есть пара алгоритмов из игрушки.

Подробнее..

Перевод Vulkan. Руководство разработчика. Swap chain

17.02.2021 18:10:52 | Автор: admin
Я продолжаю публиковать переводы руководства к Vulkan API (cсылка на оригинал vulkan-tutorial.com), и сегодня хочу поделиться переводом новой главы Swap chain из раздела Drawing a triangle, подраздела Presentation.

Содержание
1. Вступление

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

3. Настройка окружения

4. Рисуем треугольник

  1. Подготовка к работе
  2. Отображение на экране
  3. Основы графического конвейера (pipeline)
  4. Отрисовка
  5. Повторное создание цепочки показа

5. Буферы вершин

  1. Описание
  2. Создание буфера вершин
  3. Staging буфер
  4. Буфер индексов

6. Uniform-буферы

  1. Дескриптор layout и буфера
  2. Дескриптор пула и sets

7. Текстурирование

  1. Изображения
  2. Image view и image sampler
  3. Комбинированный image sampler

8. Буфер глубины

9. Загрузка моделей

10. Создание мип-карт

11. Multisampling

FAQ

Политика конфиденциальности


Swap chain



В Vulkan нет такого понятия, как default framebuffer, поэтому ему нужна инфраструктура с буферами, куда будут рендериться изображения перед выводом на экран. Такая инфраструктура называется swap chain, и ее нужно явно создать в Vulkan. Swap chain это очередь из изображений, ожидающих вывода на экран. Программа сначала запрашивает объект image(VkImage), в который будет рисовать, а после отрисовки отправляет его обратно в очередь. То, каким именно образом работает очередь, зависит от настроек, но основная задача swap chain синхронизировать вывод изображений с частотой обновления экрана.

Проверка поддержки swap chain


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

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

Начнем с того, что объявим список требуемых расширений.

const std::vector<const char*> deviceExtensions = {    VK_KHR_SWAPCHAIN_EXTENSION_NAME};

Для дополнительной проверки создадим новую функцию checkDeviceExtensionSupport, вызываемую из isDeviceSuitable:

bool isDeviceSuitable(VkPhysicalDevice device) {    QueueFamilyIndices indices = findQueueFamilies(device);    bool extensionsSupported = checkDeviceExtensionSupport(device);    return indices.isComplete() && extensionsSupported;}bool checkDeviceExtensionSupport(VkPhysicalDevice device) {    return true;}

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

bool checkDeviceExtensionSupport(VkPhysicalDevice device) {    uint32_t extensionCount;    vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);    std::vector<VkExtensionProperties> availableExtensions(extensionCount);    vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());    std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());    for (const auto& extension : availableExtensions) {        requiredExtensions.erase(extension.extensionName);    }    return requiredExtensions.empty();}

Здесь я использовал std::set<std::string>, чтобы хранить имена требуемых, но еще не подтвержденных расширений. Вы также можете использовать вложенный цикл, как в функции checkValidationLayerSupport. Разница в производительности не существенна.

Теперь запустим программу и убедимся, что наша видеокарта годится для создания swap chain. Обратите внимание, что наличие очереди отображения уже подразумевает поддержку расширения swap chain. Тем не менее, лучше убедиться в этом явно.

Подключение расширений


Чтобы использовать swap chain, сначала нужно включить расширение VK_KHR_swapchain. Для этого немного изменим заполнение VkDeviceCreateInfo при создании логического устройства:

createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());createInfo.ppEnabledExtensionNames = deviceExtensions.data();

Запрос информации о поддержке swap chain


Одной проверки, позволяющей узнать, доступна ли swap chain, недостаточно. Создание swap chain включает в себя гораздо больше настроек, поэтому нам нужно запросить больше информации.

Всего необходимо выполнить проверку 3-х типов свойств:

  • Базовые требования (capabilities) surface, такие как мин/макс число изображений в swap chain, мин/макс ширина и высота изображений
  • Формат surface (формат пикселей, цветовое пространство)
  • Доступные режимы работы

Для работы с этими данными мы будем использовать структуру:

struct SwapChainSupportDetails {    VkSurfaceCapabilitiesKHR capabilities;    std::vector<VkSurfaceFormatKHR> formats;    std::vector<VkPresentModeKHR> presentModes;};

А теперь создадим функцию querySwapChainSupport, которая заполняет эту структуру.

SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) {    SwapChainSupportDetails details;    return details;}

Начнем с surface capabilities. Их легко запросить, и они возвращаются в структуру VkSurfaceCapabilitiesKHR.

vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);

Эта функция принимает созданные ранее VkPhysicalDevice и VkSurfaceKHR. Каждый раз, когда мы будем запрашивать поддерживаемый функционал, эти два параметра будут первыми, поскольку они являются ключевыми компонентами swap chain.

Следующим шагом будет запрос поддерживаемых форматов surface. Для этого совершим уже знакомый ритуал с двойным вызовом функции:

uint32_t formatCount;vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);if (formatCount != 0) {    details.formats.resize(formatCount);    vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());}

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

Этим же способом запросим поддерживаемые режимы работы с помощью функции vkGetPhysicalDeviceSurfacePresentModesKHR:

uint32_t presentModeCount;vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);if (presentModeCount != 0) {    details.presentModes.resize(presentModeCount);    vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data());}

Когда вся необходимая информация будет в структуре, дополним функцию isDeviceSuitable, чтобы проверить, поддерживается ли swap chain. В рамках этого руководства, будем считать, что если есть хотя бы один поддерживаемый формат изображений и один поддерживаемый режим работы для window surface, значит swap chain поддерживается.

bool swapChainAdequate = false;if (extensionsSupported) {    SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device);    swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();}

Запрашивать поддержку swap chain нужно только после того, как вы убедитесь, что расширение доступно.

Последняя строка функции меняется на:

return indices.isComplete() && extensionsSupported && swapChainAdequate;

Выбор настроек для swap chain


Если swapChainAdequate имеет значение true, значит swap chain поддерживается. Но у swap chain может быть несколько режимов. Напишем несколько функций, чтобы подобрать подходящие настройки для создания наиболее эффективной swap chain.

Всего выделим 3 типа настроек:
  • формат surface (глубина цвета)
  • режим работы (условия для смены кадров на экране)
  • swap extent (разрешение изображений в swap chain)

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

Формат surface


Добавим функцию для выбора формата:

VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) {}

Позже мы будем передавать член formats из структуры SwapChainSupportDetails в качестве аргумента.

Каждый элемент availableFormats содержит члены format и colorSpace. Поле format определяет количество и типы каналов. Например, VK_FORMAT_B8G8R8A8_SRGB обозначает, что у нас есть B, G, R и альфа каналы по 8 бит, всего 32 бита на пиксель. С помощью флага VK_COLOR_SPACE_SRGB_NONLINEAR_KHR в поле colorSpace указывается, поддерживается ли цветовое пространство SRGB. Обратите внимание, что в ранней версии спецификации этот флаг назывался VK_COLORSPACE_SRGB_NONLINEAR_KHR.

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

Пройдемся по списку и проверим, доступна ли нужная нам комбинация:

for (const auto& availableFormat : availableFormats) {    if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {        return availableFormat;    }}

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

VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) {    for (const auto& availableFormat : availableFormats) {        if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {            return availableFormat;        }    }    return availableFormats[0];}

Режим работы


Режим работы, пожалуй, самая важная настройка swap chain, поскольку он определяет условия для смены кадров на экране.

Всего в Vulkan доступны четыре режима:

  • VK_PRESENT_MODE_IMMEDIATE_KHR: изображения, отправленные вашим приложением, немедленно отправляются на экран, что может приводить к артефактам.
  • VK_PRESENT_MODE_FIFO_KHR: изображения для вывода на экран берутся из начала очереди в момент обновления экрана. В то время, как программа помещает отрендеренные изображения в конец очереди. Если очередь заполнена, программа будет ждать. Это похоже на вертикальную синхронизацию, используемую в современных играх.
  • VK_PRESENT_MODE_FIFO_RELAXED_KHR: этот режим отличается от предыдущего только в одном случае, когда происходит задержка программы и в момент обновления экрана остается пустая очередь. Тогда изображение передается на экран сразу после его появления без ожидания обновления экрана. Это может привести к видимым артефактам.
  • VK_PRESENT_MODE_MAILBOX_KHR: это еще один вариант второго режима. Вместо того, чтобы блокировать программу при заполнении очереди, изображения в очереди заменяются новыми. Этот режим подходит для реализации тройной буферизации. С ней вы можете избежать появления артефактов при низком времени ожидания.

Гарантированно доступен только режим VK_PRESENT_MODE_FIFO_KHR, поэтому нам снова придется написать функцию для поиска лучшего доступного режима:

VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes) {    return VK_PRESENT_MODE_FIFO_KHR;}

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

Итак, давайте пройдемся по списку, чтобы проверить доступные режимы:

VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes) {    for (const auto& availablePresentMode : availablePresentModes) {        if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {            return availablePresentMode;        }    }    return VK_PRESENT_MODE_FIFO_KHR;}

Swap extent


Осталось настроить последнее свойство. Для этого добавим функцию:

VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) {}

Swap extent это разрешение изображений в swap chain, которое почти всегда совпадает с разрешением окна (в пикселях), куда рендерятся изображения. Допустимый диапазон мы получили в структуре VkSurfaceCapabilitiesKHR. Vulkan сообщает нам, какое разрешение мы должны выставить, с помощью поля currentExtent (соответствует размеру окна). Однако некоторые оконные менеджеры допускают использование разных разрешений. Для этого указывается специальное значение ширины и высоты в currentExtent максимальное значение типа uint32_t. В таком случае из промежутка между minImageExtent и maxImageExtent мы выберем разрешение, которое больше всего соответствует разрешению окна. Главное правильно указать единицы измерения.

В GLFW используется две единицы измерения: пиксели и экранные координаты. Так, разрешение {WIDTH, HEIGHT}, которое мы указали при создании окна, измеряется в экранных координатах. Но поскольку Vulkan работает с пикселями, разрешение swap chain тоже должно быть указано в пикселях. Если вы используете дисплей с высоким разрешением (например, дисплей Retina от Apple), экранные координаты не соответствуют пикселям: из-за более высокой плотности пикселей разрешение окна в пикселях выше, чем в экранных координатах. Так как Vulkan сам не исправит разрешение swap chain для нас, мы не можем использовать исходное разрешение {WIDTH, HEIGHT}. Вместо этого мы должны использовать glfwGetFramebufferSize, чтобы запросить разрешение окна в пикселях, прежде чем сопоставлять его с минимальным и максимальным разрешением изображений.

#include <cstdint> // Necessary for UINT32_MAX...VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) {    if (capabilities.currentExtent.width != UINT32_MAX) {        return capabilities.currentExtent;    } else {        int width, height;        glfwGetFramebufferSize(window, &width, &height);        VkExtent2D actualExtent = {            static_cast<uint32_t>(width),            static_cast<uint32_t>(height)        };        actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width));        actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height));        return actualExtent;    }}

Функции max и min здесь используются для ограничения значений width и height в пределах доступных разрешений. Не забудьте подключить заголовочный файл <algorithm> для использования функций.

Создание swap chain


Теперь у нас есть вся необходимая информация для создания подходящей swap chain.

Создадим функцию createSwapChain и вызовем ее из initVulkan после создания логического устройства.

void initVulkan() {    createInstance();    setupDebugMessenger();    createSurface();    pickPhysicalDevice();    createLogicalDevice();    createSwapChain();}void createSwapChain() {    SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice);    VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);    VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes);    VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities);}

Теперь нужно решить, сколько объектов image должно быть в swap chain. В реализации указывается минимальное количество, необходимое для работы:

uint32_t imageCount = swapChainSupport.capabilities.minImageCount;

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

uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;

Важно не превышать максимальное количество. Значение 0 обозначает, что максимум не задан.

if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) {    imageCount = swapChainSupport.capabilities.maxImageCount;}

Swap chain это объект Vulkan, поэтому для его создания требуется заполнить структуру. Начало структуры нам уже знакомо:

VkSwapchainCreateInfoKHR createInfo{};createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;createInfo.surface = surface;

Сначала указывается surface, к которой привязан swap chain, далее информация для создания image объектов:

createInfo.minImageCount = imageCount;createInfo.imageFormat = surfaceFormat.format;createInfo.imageColorSpace = surfaceFormat.colorSpace;createInfo.imageExtent = extent;createInfo.imageArrayLayers = 1;createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;

В imageArrayLayers указывается число слоев, из которых состоит каждый image. Здесь всегда будет значение 1, если, конечно, это не стереоизображения. Битовое поле imageUsage указывает, для каких операций будут использоваться images, полученные из swap chain. В руководстве мы будем рендерить непосредственно в них, но вы можете сначала рендерить в отдельный image, например, для постобработки. В таком случае используйте значение VK_IMAGE_USAGE_TRANSFER_DST_BIT, а для переноса используйте операции перемещения в памяти (memory operation).

QueueFamilyIndices indices = findQueueFamilies(physicalDevice);uint32_t queueFamilyIndices[] = {indices.graphicsFamily.value(), indices.presentFamily.value()};if (indices.graphicsFamily != indices.presentFamily) {    createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;    createInfo.queueFamilyIndexCount = 2;    createInfo.pQueueFamilyIndices = queueFamilyIndices;} else {    createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;    createInfo.queueFamilyIndexCount = 0; // Optional    createInfo.pQueueFamilyIndices = nullptr; // Optional}

Затем нужно указать, как обрабатывать объекты images, которые используются в нескольких семействах очередей. Это актуально для случаев, когда семейство с поддержкой графических операций и семейство с поддержкой отображения это разные семейства. Мы будем рендерить на image в графической очереди, а затем отправлять их в очередь отображения.

Есть два способа обработки image с доступом из нескольких очередей:

  • VK_SHARING_MODE_EXCLUSIVE: объект принадлежит одному семейству очередей, и право владения должно быть передано явно перед использованием его в другом семействе очередей. Такой способ обеспечивает самую высокую производительность.
  • VK_SHARING_MODE_CONCURRENT: объекты могут использоваться в нескольких семействах очередей без явной передачи права владения.

Если у нас несколько очередей, мы будем использовать VK_SHARING_MODE_CONCURRENT. Для этого способа требуется заранее указать, между какими семействами очередей будет разделено владение. Это можно сделать с помощью параметров queueFamilyIndexCount и pQueueFamilyIndices. Если семейство графических очередей и семейство очередей отображения совпадают, что случается чаще, используйте VK_SHARING_MODE_EXCLUSIVE.

createInfo.preTransform = swapChainSupport.capabilities.currentTransform;

Можно указать, чтобы к изображениям в swap chain применялось какое-либо преобразование из поддерживаемых (supportedTransforms в capabilities), например, поворот на 90 градусов по часовой стрелке или отражение по горизонтали. Чтобы не применять никаких преобразований, просто оставьте currentTransform.

createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;

Поле compositeAlpha указывает, нужно ли использовать альфа-канал для смешивания с другими окнами в оконной системе. Скорее всего, альфа-канал вам не понадобится, поэтому оставьте VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR.

createInfo.presentMode = presentMode;createInfo.clipped = VK_TRUE;

Поле presentMode говорит само за себя. Если мы выставим VK_TRUE в поле clipped, значит нас не интересуют скрытые пикселы (например, если часть нашего окна перекрыта другим окном). Вы всегда сможете выключить clipping, если вам понадобится прочитать пиксели, а пока оставим clipping включенным.

createInfo.oldSwapchain = VK_NULL_HANDLE;

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

Добавим член класса для хранения объекта VkSwapchainKHR:

VkSwapchainKHR swapChain;

Теперь надо просто вызвать vkCreateSwapchainKHR для создания swap chain:

if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {    throw std::runtime_error("failed to create swap chain!");}

В функцию передаются следующие параметры: логическое устройство, информация о swap chain, опциональный кастомный аллокатор и указатель для записи результата. Никаких сюрпризов. Swap chain нужно уничтожить с помощью vkDestroySwapchainKHR до уничтожения устройства:

void cleanup() {    vkDestroySwapchainKHR(device, swapChain, nullptr);    ...}

Теперь запустим программу, чтобы убедиться, что swap chain была создана успешно. Если придет сообщение об ошибке или сообщение типа Не удалось найти vkGetInstanceProcAddress в SteamOverlayVulkanLayer.dll, зайдите в раздел FAQ.

Попробуем удалить строку createInfo.imageExtent = extent; с включенными слоями валидации. Один из уровней валидации сразу же обнаружит ошибку и уведомит нас:

image

Получение image из swap chain


Теперь, когда swap chain создана, осталось получить дескрипторы VkImages. Добавим член класса для хранения дескрипторов:

std::vector<VkImage> swapChainImages;

Объекты image из swap chain будут уничтожены автоматически после уничтожения самой swap chain, поэтому добавлять код очистки не нужно.

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

vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);swapChainImages.resize(imageCount);vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());

И последнее сохраним формат и разрешение изображений swap chain в переменные класса. Они понадобятся нам в дальнейшем.

VkSwapchainKHR swapChain;std::vector<VkImage> swapChainImages;VkFormat swapChainImageFormat;VkExtent2D swapChainExtent;...swapChainImageFormat = surfaceFormat.format;swapChainExtent = extent;

Теперь у нас есть image для отрисовки и вывода на экран. В следующей главе мы расскажем, как настроить image для использования в качестве render target-ов, и начнем знакомиться с графическим конвейером и командами рисования!

C++
Подробнее..

Устройство игрового бота 16-е место в финале Russian AI Cup 2020 (и 5-е после)

19.02.2021 22:06:58 | Автор: admin

Эта статья об участии в чемпионате по написанию игрового искусственного интеллекта Russian AI Cup


Игра


Дисклеймер, пока все не разбежались


Хоть в финале я и был 16-м, статья описывает бота, удерживавшего 5-е место в общем зачете песочницы на момент её остановки.


5 место в песочнице


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


Вступление


Меня зовут Андрей Рыбалка (вдруг Вы робот и не смогли распознать текст на картинке выше), я уже восьмой год подряд участвую в Russian AI Cup. Это чемпионат для программистов по написанию игрового искусственного интеллекта. Задачей является написание бота, который будет играть в игру против ботов, написанных другими участниками.


Короче говоря,


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

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


А пока...


О задаче


Постараюсь описать покороче. Более подробное описание есть в статье GreenTea, занявшего 4-е место. Вообще, задача этого года предполагала стратегию в реальном времени (RTS) в космическом сеттинге. Но космический арт получился неудачным, юниты были практически неотличимы друг от друга визуально, поэтому все переключали визуализатор в упрощённый режим, который Вы видели на картинке в начале статьи, и забывали о космических кораблях как о страшном сне. А в упрощённом режиме на квадратиках юнитов были нарисованы меч, лук и молоток, поэтому все воспринимали игру именно в средневековом сеттинге. Так же поступлю и я в этой статье.


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


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


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


Бой 2х1


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


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


Рабочие добывают ресурсы, на эти ресурсы мы покупаем новых рабочих, либо строим здания, либо строим армию. Зданий в игре всего 4 типа: база рабочих, база мечников, база лучников и дом. Первые три умеют производить юниты соответствующих типов. То есть, чтобы начать строить лучников, нужно в начале построить их базу. Дома нужны для того, чтобы увеличивать лимит юнитов, которых можно произвести. Каждый дом позволяет купить 5 дополнительных юнитов.


Стоимость покупки юнитов растёт на $1 за каждого уже существующего юнита этого типа. Таким образом, первый рабочий стоит $10, второй $11 и т.д. Поэтому, если строить слишком много юнитов, в какой-то момент каждый последующий получается непомерно дорогим и это тоже нужно контролировать.


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


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


Этапы


Чемпионат состоит из двух раундов и финала. В каждом раунде правила несколько меняются.


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

Раунд 1


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

Раунд 2


  • Правила финала повторяют правила второго раунда, но играем 1 на 1. Выглядит примерно так:

Раунд 2


Техническая часть


Стратегия состоит из следующих основных модулей:


  1. Подготовка
  2. Экономика
  3. Строительство и ремонт
  4. Сбор ресурсов
  5. Производство юнитов
  6. Бой
  7. Перемещение по миру (поиск пути; отправка юнитов к различным целям; контроль карты)

О них и поговорим подробнее.


В поисках грааля


Я, традиционно, писал на Java. Так что таймауты мой вечный попутчик на этом чемпионате. Но в этот раз почему-то ситуация с таймаутами была гораздо плачевнее, чем в предыдущие годы. По словам организаторов, они не меняли инфраструктуру, поэтому я не знаю, чем объяснить случившееся, но я ловил таймауты даже при минимуме вычислений. Локально стратегия летает, а на сервере превышает лимит в 40 секунд процессорного времени на игру. В попытках бороться с этим, я добавил логирование суммарного реального времени и был, мягко говоря, удивлён, увидев, что локально на домашнем ПК, моя стратегия тратит суммарно на все вычисления 3 секунды на всю игру, и при этом не укладывается на сервере в отведённые 40 сек. Дебагер показал, что более 90% всего времени сжирает VM джавы, с стратегии остаются лишь оставшиеся жалкие 7-10%. Я начал бить тревогу. И выяснилось, что примерно ту же самую картину видят все, кто пишет на Java или Kotlin.


Поскольку я не джавист и совершенно не разбираюсь в настройке VM, я пытался скооперироваться с теми, кто что-нибудь в этом понимает. К примеру, в воскресенье между 1 и 2 раундом мы просидели несколько часов в скайпе с победителем этого года, Commandos-ом (который давно плюнул на эти проблемы и перешёл на C++), пытаясь добиться вменяемого быстродействия. Настройкой VM, получилось ускорить примерно вдвое, но этого тоже было слишком мало.


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


Решение нашёл участник под ником karloid, писавший на котлине. Он предложил собирать нативный PE файл средствами GraalVM.


Грааль, как и полагается граалю, сотворил чудо. Собранный exe файл у меня тратил в 5 раз меньше процессорного времени. Ещё спустя пару дней организаторы добавили поддержку GraalVM в тестирующей системе. В общем, только с того момента у меня началось полноценное участие. К сожалению, на тот момент прошло уже 2.5 недели, а это больше половины чемпионата, и оставалось всего пару дней до старта второго раунда. В общей сложности, на протяжении всего чемпионата, примерно треть всего времени ушла на всевозможные оптимизации, а не на написание стратегии. А учитывая, что на поднятие с 16 на 5 место понадобилось суммарно 10-12 часов программирования, я именно с этими проблемами связываю не самый хороший результат финала. В общем, имеем что имеем, дарёному коню в зубы не смотрят, да и поскольку решение теперь известно, я полагаю, в следующий раз Грааль будет доступен изначально.


0. Подготовка


В начале происходят некоторые предпросчёты и обновления состояния. В основном, тут всё скучно. Приведу несколько наиболее интересных моментов:


Обновление мира


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


Контролируемые области


Изначально, вспомнив статью xathis о Google Ants, я подумал, что здесь вычисление линий фронта и движение к ним также может быть весьма действенным и это было одной из первых реализованных фич.


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


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


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


Линия фронта


Линия из квадратиков некрасивого цвета это и есть линия фронта.


Слоты для добычи ресурсов


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


Карта проходимости


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


Карта проходимости


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


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


1. Экономика


Здесь особо нечего описывать. Жуткие эвристические формулы из кучи составляющих. Общая суть такова:


  • Если нужно, строим базу лучников. Есть несколько условий, когда провоцируют её строительство у нас уже есть определенное количество рабочих, либо враг собрал $350+ денег (т.е. вот-вот начнёт строить базу), либо достигнут 220-й тик.
  • Если осталось меньше X юнитов до лимита, строим дома. X = 5 до тех пор, пока количество рабочих < 15, затем X = 10 (т.е. можно строить 2 дома одновременно)
  • Если мы активно дерёмся, строим армию
  • Если нет, производим рабочих, если ещё не упёрлись в текущий лимит. Лимит вычисляем так:


    double scale = Game.duel_mode ? 0.2 : (Game.fog_of_war ? 0.25 : 0.1);boolean builders_limit_not_reached = num_builders < Math.max(Game.duel_mode ? 60 : 50, World.food_slots.size() * scale);
    

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



2. Строительство и ремонт


Строительство домов и баз работает по-разному.


База лучников


В дуэли, как только будут построены 20 рабочих, группа из 6 юнитов бежит по направлению к центру карты, до тех пор, пока клетка [35, 35] не будет разведана, если только раньше не выполнится какое-то из условий срочного строительства базы. Затем они пытаются построить базу в координатах, приближенных к клетке [30, 30]. Я видел, что у большинства других участников на строительство базы выделяется 10+ рабочих, но мои тесты показывали наилучший результат именно при количестве 6. Также, я почти в самом начале резервирую какое-то место для базы лучников возле базы рабочих, на тот случай, если карта окажется "закрытой" и со свободным местом будут проблемы. Чтобы не пришлось строить базу лучников где-то на фланге, ибо это сильно снижает возможность оборонять второй фланг и в большинстве случаев ведёт к поражению.


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


Дома


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


Далее, я проверяю, не заблокирует ли дом единственный проход между двумя областями карты. Для этого я не придумал ничего лучше, чем выбирать по свободной клетке слева и справа от дома и искать между ними путь A*-ом, считая область, где я планирую строить, занятой. Затем беру пару клеток сверху и снизу и делаю то же самое. Если оба пути найдены, можно строить. У этого подхода есть недостатки. К примеру, если в упор к дому будет "карман", то я не смогу найти из него путь и дом построен не будет, даже если на самом деле он ничего не блокирует. Всё это можно было легко исправить, но были более срочные задачи, так что руки так и не дошли.


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


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


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


Турели


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


3. Сбор ресурсов


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


  1. Собираем все слоты для добычи ресурсов в список (на картинке отмечены желтыми крестиками).


    Слоты для добычи еды


  2. Сортирую слоты по количеству других свободных слотов рядом с ними. Идея в том, что если для рабочего есть несколько слотов на одинаковом удалении, то лучше добывать там, откуда в будущем понадобится меньше времени, чтобы перебраться к следующему слоту.


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


  4. Итерируем по количеству шагов от 1 до 20 (моя максимальная дистанция поиска).


  5. Для каждого шага, для каждого слота, обрабатываем все клетки открытого списка, которые находятся на удалении, совпадающем с шагом.


  6. Если в очередной клетке находится свободный рабочий, назначаем его в текущий слот.


  7. Для тех рабочих, которые после окончания этого алгоритма остались незадействоанными, просто ищем ближайшие свободные слоты и идём к ним. Когда расстояние станет <= 20, его подхватит вышеописанный алгоритм.



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


Кроме того, если вокруг рабочего есть более одного ресурса, он добывает тот их них, который имеет меньше всего граничащих слотов


Добыча самого замурованного ресурса


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


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


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


Убегание рабочих от врага


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


А вот само вычисление опасных клеток было немножко интереснее.


  1. Отмечаем все клетки в радиусе поражения вражеских юнитов как опасные.
  2. Для вражеских лучников, вычисляем все клетки, куда они могут дойти за количество тиков, равное радиусу поражения, и добавляем их в открытый и закрытый списки. Т.е. для лучников все клетки, до которых он доходит ровно за 5 шагов. Ниже объясню, зачем это надо.
  3. Добавляем в эти же списки позиции всех моих войск.
  4. Пускаем BFS из всех клеток в открытом списке. Рассматриваем только клетки в радиусе 7 единиц от юнитов. В свойство каждой просмотренной клетки я записываю, была ли она достигнута из моей клетки или из вражеской.

Таким образом, в радиусе семи клеток от каждого вражеского лучника, я оценивал, может ли враг атаковать эту клетку раньше, чем в неё подойдут мои войска. При этом, врагу достаточно было оказаться на расстоянии выстрела от клетки, а моим лучникам нужно было её занять. Т.е. у вражеских лучников была "фора" в 5 ходов. Именно поэтому во 2-м пункте я добавлял в список клетки, достижимые ими за 5 ходов, в то время, как для моих войск я добавлял только их реальные позиции. Расстояние в 7 клеток было получено путём тестирования. При значениях больше мои рабочие погибали гораздо реже, но и еды добывали меньше. При 7 клетках коэффициент побед был наивысшим.


Ещё рабочие могли было ремонтировать (лечить) других юнитов. Мало кто из участников активно использовал эту возможность. У меня, как и у многих других, лечение было случайным. То есть если раненый юнит проходил мимо рабочего, рабочий его лечил, но специально ни врачи к пациентам ни пациенты к врачам не ходили. Лечил я только на протяжении одного тика, с 5 ХП до 6 (при максимуме в 10). Так что поваляться на больничном у них особой возможности не было. Я не видел смысла тратить 5 тиков на полное восстановление ХП лучника, который, будучи вылеченным, умрёт с двух выстрелов (выстрел снимает 5 ХП), если можно было вылечивать всего 1 ХП за 1 тик с точно таким же исходом: лучник умрёт с двух выстрелов.


4. Производство юнитов


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


5. Бой


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


Боёвку я переписывал три раза, но до финала (включительно) всё равно работала первая версия, ибо остальные показывали худший результат. Первая версия минимакс с альфа-бета отсечением.


Юниты делятся на группы. В финале работало следующее разбиение:


  1. Сортирую своих юнитов по количеству противников в радиусе 5, затем 6, затем 7
  2. Создаю бой и добавляю в него первого из отсортированных юнитов, затем рекурсивно всех его врагов, всех врагов его врагов и т.д., пока есть кого добавлять
  3. Если моих юнитов в бою уже 5, больше в этот бой не добавляю. То же самое с противниками.

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


  • Юниты на расстоянии 7 клеток от врага не идут в клетки на 8
  • Генерация всех возможных ходов для моих и вражеских юнитов была достаточно дорогой операцией, и в раздумьях о том, как её оптимизировать, пришёл в голову следующий ход конём: я понял, что моя команда никогда не подходит в упор к вражеской команде (не считая варианта с мечниками, которых по факту практически не использовали). А потому, каким бы ни был ход моей команды, он никак не влияет на возможные ходы врага. Это позволило генерировать вражеские ходы только один раз за тик и затем тянуть их из кеша.
  • Юниты не умеют меняться местами и два юнита не могут идти в одну и ту же клетку.

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


Одним из немногих изменений, которые были сделаны уже после финала и которые подняли меня с 16 на 5 место, было то, что я изменил принцип разбиения всех воюющих юнитов на отдельные бои. Так что в последней версии работает так:


  1. Сортирую своих юнитов по количеству противников в радиусе 5, затем 6, затем 7
  2. Беру первого юнита из отсортированного списка, рекурсивно добавляю вместе с ним в бой всех своих юнитов на расстоянии 3. Затем обхожу всех моих юнитов в этом бою и добавляю в него всех врагов на расстоянии <= 7

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


Эта вот альтернативная боевая система работала на общих эвристических принципах, схожих с теми, которые были у многих других участников. Она не очень интересна, но вкратце опишу:


  1. Сортирую своих юнитов по количеству противников в радиусе 5, затем 6, затем 7
  2. Те, у кого уже есть противники в радиусе 5, никуда не ходят и просто стреляют
  3. Обхожу моих юнитов в отсортированном списке
  4. Для каждого, считаю общее количество врагов в зонах 6 и 7 клеток
  5. Беру ближайшего к нему врага и считаю количество моих юнитов только в зоне 6 клеток
  6. Если число из пункта 5 больше числа из пункта 4, юнит считается атакующим.
  7. Если меньше убегаем, если равное количество стоим на месте.

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


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


Перераспределение выстрелов


Юнит "C" имеет только одну цель в радиусе выстрела "3", в то время, как юниты "A" и "B" имеют по 2 цели. Если бы юниты "A" и "B" стреляли в цель "3", выстрел юнита "C" не принёс бы никакой пользы. Поэтому у меня первым стреляет юнит "C", ибо у него всего одна возможная цель, а затем "A" и "B" решают, куда стрелять им, чтобы максимизировать потери противника.


6. Перемещение по миру


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


Охота на вражеских рабочих


  1. Разбиваю всех видимых вражеских рабочих на группы (в цикле, если рабочий находится в пределах 5 единиц он какой-нибудь группы, добавляю его в неё и пересчитываю её центр. Иначе, добавляю в новую группу)
  2. Считаю score каждой группы: по 3 очка за каждого юнита, который добывает еду, и по 1 очку за остальных
  3. Сортирую по убыванию score и уже привычным движением руки, добавляю их в открытый и закрытый списки.
  4. BFS-ом ищу ближайшего свободного лучника.
  5. Скачу пугать и убивать.

К моему удивлению, одним из самых значимых изменений после окончания чемпионата, которое подняло меня с 8-10 мест места на 4-5, было изменение одной единственной константы, которая заставила охотников при поиске пути бояться вражеских солдатов.
Причём это было в последний день, за несколько часов до остановки песочницы, и локальные тесты показали улучшение всего на 30%, так что я даже сомневался, релизить ли, чтобы не потерять имеющуюся позицию. Дело в том, что в этом году у меня на протяжении всего чемпионата постоянно случалось такое, что новая версия локально выигрывала от 65% до 95% игр, а будучи залитой на сайт, против других играла так же, как предыдущая, или хуже. Вообще практически все мои релизные версии выигрывали не менее 2/3 игр против предыдущей. А тут всего-лишь 30%. В общем, я рискнул и риск оправдался.


Round 1 Opening


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


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


Это единственный случай, когда я вообще строю мечников. Более того, базу мечников я даже не ремонтирую.


Защита базы


К каждому врагу, который находится недалеко от моей базы, выбирается ближайший мой свободный лучник в определенном радиусе. Если враг находится дальше 30 клеток от базы рабочих, для этой задачи выбираются только юниты с нечетными id. Без этого условия работало хуже, ибо часто подхватывались юниты, которые могли принести больше пользы на других задачах.


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


Обход по флангам


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


Для этой задачи я пускал 3 луча из точки [79, 79] (это угол на базе противника) влево и столько же вниз, с поворотом в 9 градусов между ними. И отправлял по одному лучнику вдоль каждого луча. Точнее, луч бился на сегменты и юнит стремился к дальнему от вражеской базы сегменту. Если этот сегмент недавно был посещён, юнит шёл к следующему и таким образом продвигался к вражеской базе по флангу.


Обход по флангам


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


Перемещение по карте


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


Итак, перемещение:


  1. Инициализирую ПП. По сути это просто двумерный массив чисел, хранящий потенциалы каждой клетки. Из-за моих проблем с быстродействием до того момента, когда был найден грааль, я использовал сетку размером в 2 клетки. Позже ресурсов уже хватало и на нормальную сетку, но весь остальной код на тот момент уже полагался именно на этот размер, а времени переписывать уже не было. Короче говоря, на переправе коней не меняют. Поэтому, до самого конца у меня так и используется сетка 40х40 поверх поля 80х80.
  2. Расставляем эмиттеры. Т.е. точки, которые излучают положительный или отрицательный потенциал в определенной области. Все эмиттеры были линейными. Угадайте, почему. Правильно быстродействие! Считать квадратные корни или возводить в иные степени дорогое удовольствие. С граалем я уже мог себе это позволить, но это нарушило бы всю хрупкую экосистему и пришлось бы искать новый баланс.
    Эмиттеров было достаточно много. Вот несколько основных:
    • Отталкивающее поле радиусом в несколько клеток в позиции каждого лучника. Я изначально решил, что мои юниты будут разбредаться по всей карте, чтобы во-первых, минимизировать туман войны и во-вторых, я стремился к тому, чтобы в любой точке карты, где срочно понадобятся дополнительные юниты, кто-нибудь оказался поблизости.
    • Притягивающие поля на вражеских юнитах и зданиях, на моих турелях, на моих строящихся зданиях
    • Отталкивающее поле в точке [0, 0], чтобы при прочих равных, юниты не толпились на базе
    • Кроме того, как я уже упоминал выше, здесь также работал алгоритм с лучами, только из точки [0, 0]. Я запускал 6 лучей по флангам и ставил эмиттеры в тех местах, где эти лучи пересекались с линией фронта (с некоторым сдвигом вперёд). это заставляло юнитов стремиться в позиции между моими рабочими и вражеской армией.

Эмиттеры


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


Эмиттеры


Блеклые красные это эмиттеры с отрицательным потенциалом.


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


То есть из каждого юнита я поиском в ширину делал 25 шагов и затем рассматривал все пройдённые клетки. Потенциал каждой клетки относительно юнита считался как сумма_потенциалов_всех_пройденных_клеток + потенциал_в_целевой_клетке * (25 - количество_сделанных_шагов). То есть я "дошагивал" все оставшиеся ходы в конечной клетке, для того, чтобы я затем смог полноценно сравнивать все эти пути, имея цифры одного порядка. Ну и как несложно догадаться, я выбирал клетку с наивысшим относительным потенциалом и шёл к ней.


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


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


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


Поиск пути


Банальный A* со штрафами. Штрафы давались за прохождение через юнитов, через места, где планируется стройка, большой штраф давался за проход в зоне поражения вражескими турелями. За прохождение через ресурсы давался штраф, равный количеству ходов, которые потребуются, чтобы прорубить через них проход. И так далее. Некоторые из этих штрафов применялись или игнорировались в зависимости от аргументов. К примеру, рабочие сильно штрафовались за прохождение через зону стрельбы вражеских лучников, а войска нет.


Также, юниты умели толкать рабочих. Тут есть два варианта:


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


    Проталкивание рабочих


    Юнит "A" идёт в клетку "B". Черные стрелки показывают, кто куда будет смещён. На следующем ходу уже дальний вытолкнутый юнит окажется ближайшим свободным и вероятно, продолжит миссию. Непосредственного обмена приказами у меня не было, это получалось само собой.


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



Тестирование


Тесты я гонял на 3-х компьютерах. В этом году даже не пришлось считать, является ли результат статистически значимым, ибо игры считались достаточно быстро, так что гонять их можно было много, и при этом, практически каждая моя следующая версия выигрывала у предыдущей 2/3 игр или больше, при количестве сыгранных игр не менее 500. Т.е. результат был заведомо статистически значим и без вычислений. При этом, как я уже упоминал выше, в этом году постоянно получалось так, что моя новая версия, без шансов обыгрывающая предыдущую, но против других противников играет лишь немногим лучше (если повезёт), а то и хуже (если нет). Апогеем стала версия, которая в локальных тестах выиграла у предыдущей со счётом 480:20, но после релиза показала нулевое преимущество против других участников.


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


Подбор констант, коих было много, осуществлялся скриптом на python. Он брал значения из командной строки и для каждого набора создавал некоторое количество игр (обычно 200) против той же версии с дефолтными константами. Что-то типа такого:


python search.py run &mdash;p1 test.exe &mdash;p2 prev.exe &mdash;count 200 &mdash;teams 2 &mdash;nthreads 3 &mdash;level Finals &mdash;params "CMD_MAX_DIST_FROM_BASE_TO_COUNTER:50/100|ENEMY_UNIT_ATTRACTION:100/300|FRIENDLY_PUSH_OFF_MULT:2.5/7.5" &mdash;output tests_v42-r3-1

Конкретно эта строка создала бы 6 сетов по 200 игр в 3 потока в режиме дуэли. Первый сет игр был бы со значением CMD_MAX_DIST_FROM_BASE_TO_COUNTER = 50, второй CMD_MAX_DIST_FROM_BASE_TO_COUNTER = 100 и т.д. Можно было передавать и несколько констант за один раз. Сами тестируемые значения я обычно проверял парами брал значение заметно больше текущего и заметно меньше. В примере выше, дефолтное значение константы CMD_MAX_DIST_FROM_BASE_TO_COUNTER было 75, поэтому я тестировал значения 50 и 100.


Визуализация


Как и все предыдущие годы, я использовал свой самописный визуализатор. Тем, кто читал мои предыдущие статьи он уже знаком. Вот традиционное видео:



Заключение


На этом у меня всё. Спасибо организаторам за очередной крутой контест. Задача этого года, как по мне, была одной из самых интересных.
Ждём новых чемпионатов!

Подробнее..

Перевод Как я сократил время загрузки GTA Online на 70

01.03.2021 16:18:41 | Автор: admin
GTA Online. Многопользовательская игра, печально известная медленной загрузкой. Недавно я вернулся, чтобы завершить несколько ограблений и был потрясён, что она загружается настолько же медленно, как и в день своего выпуска, 7 лет назад.

Пришло время докопаться до сути.

Разведка


Сначала я хотел проверить, вдруг кто-то уже решил проблему. Но нашёл только рассказы о великой сложности игры, из-за чего она так долго загружается, истории о том, что сетевая p2p-архитектура мусор (хотя это не так), некоторые сложные способы загрузки в сюжетный режим, а потом в одиночную сессию, и ещё пару модов, чтобы скипнуть видео с логотипом R* во время загрузки. Ещё немного почитав форумы, я узнал, что можно сэкономить колоссальные 10-30 секунд, если использовать все эти способы вместе!

Тем временем на моём компе

Бенчмарк


Story mode load time:  ~1m 10sOnline mode load time: ~6m flatStartup menu disabled, time from R* logo until in-game (social club login time isn't counted).Old but decent CPU:   AMD FX-8350Cheap-o SSD:          KINGSTON SA400S37120GWe have to have RAM:  2x Kingston 8192 MB (DDR3-1337) 99U5471Good-ish GPU:         NVIDIA GeForce GTX 1070

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

Я (не) одинок


Если доверять этому опросу, проблема достаточно широко распространена, чтобы слегка раздражать более 80% игроков. Прошло уже семь лет!



Я немного поискал информацию о тех ~20% счастливчиках, которые загружаются быстрее трёх минут, и нашёл несколько бенчмарков с топовыми игровыми ПК и временем загрузки онлайн-режима около двух минут. Я бы кого-нибудь убил хакнул за такой комп! Действительно похоже на железячную проблему, но что-то не складывается

Почему у них сюжетный режим по-прежнему загружается около минуты? (кстати, при загрузке с M.2 NVMe не учитывались видео с логотипами). Кроме того, загрузка из сюжетного режима в онлайн занимает у них всего минуту, в то время как у меня около пяти. Я знаю, что их железо гораздо лучше, но не в пять же раз.

Высокоточные измерения


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



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

Использование диска? Нет! Использование сети? Есть немного, но через несколько секунд падает в основном до нуля (кроме загрузки вращающихся информационных баннеров). Использование GPU? Ноль. Память? Вообще ничего

Что это, майнинг биткоинов или что-то такое? Чую здесь код. Очень плохой код.

Единственный поток


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

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

Профилирование


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

Итак, добро пожаловать в образцы стека (stack sampling). Для приложений с закрытым исходным кодом есть только такой вариант. Сбросьте стек запущенного процесса и местоположение указателя текущей инструкции, чтобы построить дерево вызовов в заданные интервалы. Затем наложите их и получите статистику о том, что происходит. Я знаю только один профилировщик, который может проделать это под Windows. И он не обновлялся уже более десяти лет. Это Люк Stackwalker! Кто-нибудь, пожалуйста, подарите Люку немножко любви :)



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

Вниз по кроличьей норе


Позаимствовав у моего друга совершенно законную копию стандартного дизассемблера (нет, я действительно не могу его себе позволить когда-нибудь освою гидру), я пошёл разбирать GTA.



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

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

Проблема первая: это strlen?!


Дальнейший разбор дампа выявил один из адресов с некоей меткой strlen, которая откуда-то берётся! Спускаясь вниз по стеку вызовов, предыдущий адрес помечен как vscan_fn, и после этого метки заканчиваются, хотя я вполне уверен, что это sscanf.

Куда ж без графика

Он что-то парсит. Но что? Логический разбор займёт целую вечность, поэтому я решил сбросить некоторые образцы из запущенного процесса с помощью x64dbg. Через несколько шагов отладки выясняется, что это JSON! Он парсит JSON. Колоссальные десять мегабайт JSON'а с записями 63 тыс. предметов.

...,{    "key": "WP_WCT_TINT_21_t2_v9_n2",    "price": 45000,    "statName": "CHAR_KIT_FM_PURCHASE20",    "storageType": "BITFIELD",    "bitShift": 7,    "bitSize": 1,    "category": ["CATEGORY_WEAPON_MOD"]},...

Что это? Судя по некоторым ссылкам, это данные для сетевого торгового каталога. Предполагаю, он содержит список всех возможных предметов и обновлений, которые вы можете купить в GTA Online.

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

10 мегабайт? В принципе, не так уж и много. Хотя sscanf используется не самым оптимальным образом, но, конечно, это не так уж плохо? Что ж



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

Проблема вторая: давайте использовать хэш-массив?


Оказывается, второго преступника вызывают сразу за первым. Даже в одной и той же конструкции if, как видно из этой уродливой декомпиляции:



Все метки мои, и я понятия не имею, как на самом деле называются функции/параметры.

Вторая проблема? Сразу после разбора элемента он хранится в массиве (или встроенном списке C++? не уверен). Каждая запись выглядит примерно так:

struct {    uint64_t *hash;    item_t   *item;} entry;

А перед сохранением? Он проверяет весь массив, сравнивая хэш каждого элемента, есть он в списке или нет. С 63 тыс. записей это примерно (n^2+n)/2 = (63000^2+63000)/2 = 1984531500, если я не ошибаюсь в расчётах. И это в основном бесполезные проверки. У вас есть уникальные хэши, почему не использовать хэш-карту.



Во время реверс-инжиниринга я назвал его hashmap, но это явно не_hashmap. И дальше ещё интереснее. Этот хэш-массив-список пуст перед загрузкой JSON. И все элементы в JSON уникальны! Им даже не нужно проверять, есть они в списке или нет! У них даже есть функция прямой вставки элементов! Просто используйте её! Серьёзно, ну ребята, что за фигня!?

PoC


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

План такой. 1. Написать .dll, 2. внедрить её в GTA, 3. зацепить некоторые функции, 4. ???, 5. профит. Всё предельно просто.

Проблема с JSON нетривиальная, я не могу реально заменить их парсер. Более реалистичным кажется заменить sscanf на тот, который не зависит от strlen. Но есть ещё более простой способ.

  • зацепить strlen
  • подождать длинной строки
  • закэшировать начало и длину
  • если поступит ещё вызов в пределах диапазона строки, вернуть закэшированное значение

Что-то вроде такого:

size_t strlen_cacher(char* str){  static char* start;  static char* end;  size_t len;  const size_t cap = 20000;  // if we have a "cached" string and current pointer is within it  if (start && str >= start && str <= end) {    // calculate the new strlen    len = end - str;    // if we're near the end, unload self    // we don't want to mess something else up    if (len < cap / 2)      MH_DisableHook((LPVOID)strlen_addr);    // super-fast return!    return len;  }  // count the actual length  // we need at least one measurement of the large JSON  // or normal strlen for other strings  len = builtin_strlen(str);  // if it was the really long string  // save it's start and end addresses  if (len > cap) {    start = str;    end = str + len;  }  // slow, boring return  return len;}


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

char __fastcall netcat_insert_dedupe_hooked(uint64_t catalog, uint64_t* key, uint64_t* item){  // didn't bother reversing the structure  uint64_t not_a_hashmap = catalog + 88;  // no idea what this does, but repeat what the original did  if (!(*(uint8_t(__fastcall**)(uint64_t*))(*item + 48))(item))    return 0;  // insert directly  netcat_insert_direct(not_a_hashmap, key, &item);  // remove hooks when the last item's hash is hit  // and unload the .dll, we are done here :)  if (*key == 0x7FFFD6BE) {    MH_DisableHook((LPVOID)netcat_insert_dedupe_addr);    unload();  }  return 1;}

Полный исходный код PoC здесь.

Результаты


Ну и как оно работает?

Original online mode load time:        ~6m flatTime with only duplication check patch: 4m 30sTime with only JSON parser patch:       2m 50sTime with both issues patched:          1m 50s(6*60 - (1*60+50)) / (6*60) = 69.4% load time improvement (nice!)

Да, чёрт возьми, получилось! :))

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

Краткое содержание


  • При запуске GTA Online есть узкое место, связанное с однопоточным вычислением
  • Оказалось, GTA изо всех сил пытается распарсить 1-мегабайтный файл JSON
  • Сам парсер JSON плохо сделан/наивен и
  • После парсинга происходит медленная процедура удаления дублей

R*, пожалуйста, исправьте


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

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

ty <3
Подробнее..

Бесплатная онлайн-конференция Нарратив в играх

28.02.2021 14:11:42 | Автор: admin

21 марта 2021 года(воскресенье), с 12:00 и до 19:00, состоится Бесплатная онлайн-Конференция: Нарратив в играх.

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

Кроме интересных лекций вас ждет конкурс на лучший сценарий игры (и не только) с крутыми призами.

На конкурс принимаются видео-игры и настолки, выпущенныев 2020-2021году (даже в Раннем доступе или в виде демо/beta) на русском языке.

Для участия в конкурсе необходимоподать заявкудо 05.03.2021

Участие в мероприятии абсолютно бесплатно.
Организатор конференции: Центр развития компетенций в бизнес-информатике Высшей школы бизнеса НИУ ВШЭ, при поддержке WN Conference, Talents in Games и Союза Литераторов РФ.
Регистрация на конференцию здесь>>>

Подробнее..

Сборка Open Source GTA VC и GTA III в Linux

17.02.2021 22:18:17 | Автор: admin
image
Скорее всего нет тут такого человека, который бы не играл в GTA (или хотя бы не слышал о ней). Первая 3D версия серии вышла около 20 лет назад. Это была GTA III. Через год вышла GTA: Vice City. Несмотря на это, в эти игры до сих пор не только играют, но и создают моды. Эти игры портированы на множество платформ, но к сожалению, Linux (до недавнего времени) обошли стороной. Единственный вариант поиграть в Linux был wine. Но недавно все изменилось.

Мне на глаза новость Разработчики закончили реверс-инжиниринг GTA III и Vice City и выпустили порты для разных платформ. А так как я слежу за всем, что касается исходников(утечки, открытие и т.д.), а также люблю собирать софт из исходников разумеется это я не мог проигнорировать. Из новости я понял, что энтузиасты опубликовали исходный код GTA III и GTA: VC. Моей радости не было предела. Я сразу же пошел на github за дополнительной информацией.

Основные улучшения (касательно miami):
Исправлено множество мелких и крупных багов.
Пользовательские файлы (сохранения и настройки), теперь хранятся в корневом каталоге GTA.
Настройки теперь хранятся в файле reVC.ini (в оригинале были в gta_vc.set).
Добавлено меню отладки (доступно по CTRL+M).
Свободная камера (Debug camera). CTRL+B вкл/выкл. При включении камеру можно свободно перемещать во всей карте.
Убраны экраны загрузки между островами.
И некоторые другие исправления/улучшения, включая исправления для других платформ.
Примечание: эти параметры можно настроить в файле core/config.h. Некоторые параметры можно менять прямо в игре (используя отладочное меню), а для остальных потребуется пересборка.

Перейдем к самому интересному, а именно к сборке miami (GTA: VC).
Нам потребуются:
Дистрибутив Linux (Ubuntu, Debian, Mint, etc);
Оригинальные файлы (ассеты) с дистрибутива игры VC (в случае сборки GTA III соответственно потребуются файлы GTA III).
GCC.
Некоторые библиотеки и заголовочные файлы.

В качестве ОС использую Linux Mint 19.3 (основа Ubuntu 18.04 LTS). Установить компиляторы gcc можно так (если не установлены):
sudo apt install build-essential

Установка необходимых библиотек:
sudo apt install libopenal-dev libglew-dev libglfw3-dev libmpg123-dev

Тут остановимся и рассмотрим поподробнее. Я вчера пол часа потратил на поиск решения проблемы компиляции из-за своей невнимательности. На GitHub четко указано, что версия библиотеки glfw должна быть не ниже 3.3, в то время как в репозиториях Ubuntu 18.04 3.2. Поэтому, если у вас дистрибутив построенный на Ubuntu 18.04 (или в репозитории версия библиотеки glfw ниже 3.3) то данную библиотеку необходимо собрать из исходников. Но тут нет ничего сложного. Качаем архив github.com/glfw/glfw/releases/download/3.3.2/glfw-3.3.2.zip
Распаковываем в любую папку. Заходим в распакованную папку и выполняем следующие команды:
mkdir glfw-buildcd glfw-buildcmake -DBUILD_SHARED_LIBS=ON ../make -j2sudo checkinstall

Библиотека нужной версии установлена. Теперь клонируем репозиторий:
git clone --recursive -b miami https://github.com/GTAmodding/re3.git reVC

Это много времени не займет. После клонирования размер папки reVC около 120 МБ(из которых 70 МБ папка .git).
Далее идем в папку reVC и запускаем premake5Linux:
./premake5Linux --with-librw gmake2

Генерация фалов сборки займет пару секунд.
Примечание: Если вы используете архитектуру arm/arm64 то вам нужно собрать утилиту premake5Linux из исходников, а потом запустить с параметром, указанном выше.

Теперь собственно сама сборка. Для сборки доступны следующие конфигурации:
debug_linux-x86-librw_gl3_glfw-oal
debug_linux-amd64-librw_gl3_glfw-oal
debug_linux-arm-librw_gl3_glfw-oal
debug_linux-arm64-librw_gl3_glfw-oal
release_linux-x86-librw_gl3_glfw-oal
release_linux-amd64-librw_gl3_glfw-oal
release_linux-arm-librw_gl3_glfw-oal
release_linux-arm64-librw_gl3_glfw-oal

Думаю, из названий и так все понятно. Главное не перепутайте amd64 и arm64(я вчера ночью сонный недосмотрел, потом не мог понять, почему не происходит сборка).
Переходим в директорию reVC/build и запускаем make с вашей конфигурацией. В случае с debug-версией для amd64 нужно так:
make config=debug_linux-amd64-librw_gl3_glfw-oal

Компиляция происходит достаточно быстро. На моём старом ноутбуке (Asus X55A, Pentium B970 2x2.3 ГГц и 4 Гб памяти) сборка отладочной версии заняла 2 мин. 10 сек.(выходной файл = 30.4 МБ), релизной 4 мин. 44 сек.(размер файла = 43,8 МБ). По своему опыту скажу, что во многих случаях обычно все наоборот.

Если компиляция прошла без ошибок, то в папке reVC/bin/ваша_конфигурация/Debug_или_Release/ будет бинарник reVC.

Копируем куда-нибудь оригинальную папку с GTA: VC (при желании, можно удалить все файлы из корня они нам не понадобятся). Потом в папку с игрой копируем наш бинарник reVC. Теоретически, игру уже можно запускать, и даже играть. Но текст на некоторых пунктах меню будут неправильно отображаться:
image
Чтобы это исправить, копируем папки с заменой из reVC/gamefiles в папку с игрой. Правда, в таком случае, игра будет на английском языке.

Теперь пробежимся по меню отладки:
image

Что бы включить или выключить меню отладки нажмите CTRL+M.
Cam все, что связанно с камерой.
Cheats Различные читы.
Debug Отображение разной отладочной информации, позиции игрока, и т.д.
Debug Render Можно скрывать или отображать различные объекты, транспорт, строения, пешеходов, показывать пути транспорта и пешеходов и т.д.
Game Телепортация в любое место (предварительно нужно поставить метку на карте). Также можно запустить абсолютно любую миссию.
Particle Не знаю что это такое.
Render Некоторые fix'ы, настройка fps, графические настройки
Spawn Спаун транспорта.
Time & Weather Настройка времени и погоды.

Сборка GTA III ничем не отличается, за исключением клонирования репозитория:
git clone --recursive https://github.com/GTAmodding/re3.git


Насчет модов: текстуры, скрипты, модели должны работать. А вот dll/asi, CLEO работать не будут. Некоторые возможности этих модов уже реализованы в re3, некоторые можно настроить в файле config.h.

Ссылки по теме:
github.com/GTAmodding/re3
Инструкция по сборке GTA III
Готовые бинарники reVC для Mac, Linux, Windows
Готовые бинарники re3 для Mac, Linux, Windows
Просмотр репозитория в Visual Studio Code
Lifehack: Если в ссылке на github изменить github.com на github1s.com, то репозиторий можно просматривать в удобном Visual Studio Code.
Подробнее..

Стики и работа с Event System в Unity 3D

10.02.2021 22:13:33 | Автор: admin

Учебные материалы для школы программирования. Часть12

Предыдущие уроки можно найти здесь:

Этот материал состоит из двух частей:

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

Познакомимся с использованием Event System в разрезе работы с UI и реализации пользовательской обработки реакции на указатель мыши/тачпада.

Далее, перейдем ко второй, где создадим скрипт, реализующий доступ к другим объектам посредством Event System.

По ходу дела, попробуем свои силы в работе со static-переменными для реализации удобной имплементации модулей в проект, и узнаем о глобальных и локальных координатах RectTransform.

Обе части занятия являют собой продолжение работы над проектом "Жидкий персонаж".

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

Порядок выполнения

Создадим новую панель со следующими параметрами:

Панель отвечает за активную зону для нажатий. От её размеров зависит площадь, на которой будет работать стик.

Внутри панели создадим 2 Image согласно иерархии на скриншотах - Joy и Mushroom Joy тело нашего стика, Mushroom его грибок.

Их параметры:

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

Создадим скрипт. Скрипт необходимо закинуть на панель. Его полный листинг выглядит так (если в таком формате совсем неудобно, пишите в комментах - перенаберу исходный код):

Разберём его подробнее
Для начала подключим пространство имён для обработки событий:

using UnityEngine.EventSystems;

За обработку нажатий отвечают методы OnPointerDown и OnPointerUp. Для их работы необходимы следующие интерфейсы: IpointerDownHandler и IpointerUpHandler.

Чтобы работать с информацией о конкретном нажатии (а в случае мультитача данных нажатий может быть несколько) объявляем поле private PointerEventData eventData;

При нажатии на экран вызывается OnPointerDown и складывает информацию о нажатии в eventData.

В дальнейшем это позволяет нам работать с eventData из метода Update().

Для того, чтобы понимать, актульна ли информация о нажатии, введена булева переменная OnScreen. Если мы нажали на экран, то переменная принимает значение true, объект Joy становится в точку нажатия и объекты Joy и Mushroom становятся видимыми.

Метод OnPointerUp отключает видимость Joy и Mushroom и переводит переменную OnScreenв false.

Остальная обработка возникает в Update().
Там мы выставляем Mushroom по глобальной точке нажатия и меряем её локальные координаты.

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

Теперь, в любом скрипте, который используем методы типа GetAxis строку типа Input.GetAxis("Horizontal")меняем наCustomStick.horizontal

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

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

Хочется напомнить, все материалы рассчитаны на использование в составе проекта с главным героем - желе.

Перейдем ко второй части.

Использование своих типов эвентов через код

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

Создадим новый скрипт. Его листинг:

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

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

Рассмотрим пару вариантов использования этого скрипта. Вариант первый создание потайной двери-стены, открывающийся ключом. Для этого нам понадобится спрайт стены с обычным коллайдером и спрайт или модель ключа с коллайдером в режиме триггера.
Также можно добавить ещё один пустой объект и закинуть на него звук, создав тем самым AudioSource. Уберём у AudioSource галочку вопроизведения при старте и закинем в него ключ и стены.

Далее настроим сам ключ. В данной конфигурации ключ выключает стену, включает звук и выключает себя.

Это самый простой пример логики. Рассмотрим посложнее.

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

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

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

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

Создадим кнопку. Для этого импортируем приложенное извображение и разрежем на 2 спрайта.

Расположим их в мире в одной точке, зелёный выключим и назовём его "Вкл", Красный назовём "Выкл".

Создадим ещё один пустой объект, закинем на него коллайдер, выставим коллайдеру режим триггера и расположим на кнопке. Настроим следующим образом:

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

Сюда же можно добавить звук нажатия кнопки, закинув его на пустой объект или на сам спрайт зелёной кнопки и оставив галочку Play On Awake.

На этом этапе занятие можно считать завершённым.

Пишите комменты, делитесь полезными ссылками, как можно улучшить проект!

Пожалуйста, поддержите инициативу - нажимайте нравится и поделиться!

Подробнее..

Анимация и экспорт. На примере игры Intravenous. Часть 1

09.02.2021 16:13:11 | Автор: admin

Сказ о том, как делать не стоит. Или, как я дважды сгорал на работе

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

Но, именно данный заказ натолкнул меня на мысль, что подобным опытом стоит поделиться. Дабы, не знакомые со сферой, знали, как работает внутренняя кухня, а коллеги, как делать не стоит и почему. К тому же, перспектива Top-Down специфическая и материалов по ней практически не существует. Когда я начинал работу, никакого опыта с top-down перспективой, кроме игровой, у меня не было, что подогревало интерес.

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


Promo-art для проектаPromo-art для проекта

Проект выполнен в жанре: Top Down Stealth - Action (уникальная смесь из серий Splinter Cell и Hotline Miami).

Движок: Love2D
Арт/Дизайн/Анимации выполнены в: Adobe Photoshop ( :) )
Художественное направление: Pixel art

Проект находится в Steam, и ознакомится с ним можно по данной ссылке: Intravenous

Скриншот из ранних версийСкриншот из ранних версийСкриншот из демо-версииСкриншот из демо-версии

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

Я изготовил для проекта UI (редизайн/арт), эффекты, персонажей, анимации, тайлсеты, объекты, портреты, promo-art.
В общем, практически всё, что вы увидите в игре. Но в данной статье, речь пойдёт именно про анимации, т.к. они стали "камнем преткновения" всей разработки.


Немного о перспективе

Enter The Gungeon - хороший пример перспективы 3/4Enter The Gungeon - хороший пример перспективы 3/4

Существует распространённое заблуждение, что "top-down" - это любой угол поворота камеры, в том числе несколько видов изометрии и, так называемая, перспектива 3/4.

Скетчи персонажей для освоения top-down перспективыСкетчи персонажей для освоения top-down перспективы

Связано это с тем, что у ряда перспектив, не существует какого-то объединяющего понятия/термина отличного от "вида сверху" т.е. "Top-Down".
Отсюда и возникающие недопонимания при обсуждении того или иного проекта.

"Top-Down" (топ-даун) - это перспектива, камера в которой привязана исключительно над головой персонажа.
Примеры: GTA 1/2, Darkwood, Hotline Miami


Анимации

Скетчи персонажей в пикселяхСкетчи персонажей в пикселях

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

Первые потуги анимации в top-down перспективеПервые потуги анимации в top-down перспективе

Список анимаций для всех персонажей включал в себя:

  • 5 видов основного оружия (Дробовик, Обрез, MP5, UZI, AK103, M4);

  • 5 видов второстепенного (Glock19, HS2000, P89, SW457, VP9);

  • 5 видов уникальных приспособлений (Тазер, Переносной ЭМИ глушитель, Светошумовая граната, Осколочная граната, Пустые магазины);

  • Ближний бой на всех видах оружия, в том числе и рукопашный;

  • Выбивание двери;

  • Idle анимации;

  • Анимации смерти;

  • Подбор и взаимодействие с предметами;

Наброски анимацийНаброски анимаций

Уникальные для персонажа игрока:

  • Перенос тел;

  • Оглушение или добивание персонажей;

  • Использование отмычки;

  • Лаз через 2 вида препятствий;

  • Движение ползком;

  • Бег;

Обрез. Умелый.Обрез. Умелый.

Помимо этого, существует 3 степени умения обращения с оружием (что увеличило список анимаций втрое!), которые мы условно назвали:

- Умелый; (персонаж игрока, профессиональные военные)
- Не умелый; (киллеры, наёмники)
- Абсолютно не умелый; (гангстеры, шпана)

Обрез. Неумелый.Обрез. Неумелый.

Все 3 степени отличаются геймплейно:

- точностью при стрельбе;
- скоростью перезарядки;
- скоростью реакции на события;

Что отражается визуально, через:
- наличие лишних телодвижений при перезарядке;
- положение персонажа (стойку);

Обрез. Абсолютно неумелый.Обрез. Абсолютно неумелый.

Список анимаций на каждого персонажа достигал значения 200+. Т.к. каждая анимация, даже в которой было изменён исключительно спрайт пистолета был уникальным.

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

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


Шаблон

Анимация падения и подъёмаАнимация падения и подъёма

Шаблон персонажа включал в себя:

- Голову;
- Тело;
- Руки;
- Оружие;
- Дополнительные слои (ладони/детали);
-
Ноги/нижняя половина тела (отдельно);

Из которых покадрово собирался цикл анимации.


Экспорт

Существует 2 пути экспорта шаблонных анимаций.

Способ 1:

  • Все части шаблона - отделены (слоями);

  • Оружие легко заменяется (если позволяет анимация);

  • Одежда кладётся поверх слоёв в игре;

Pixel art со скелетной анимациейPixel art со скелетной анимацией

+ Упор делается на сборку составляющих внутри движка игры;
+ Гибкость, возможность осуществлять исправления, буквально, на лету;
- Требователен к инструментарию движка;

Не совсем корректный, но отличный пример: Garage: Bad Trip.
(На самом деле, она известна своей скелетной анимацией скрещенной с Pixel-art графикой, и даже существует статья на эту тему, но я её не нашёл) ("Пес-песа" - тебя помнят!)

Способ 2:

  • Все части шаблона склеены (монолитный слой);

  • Оружие заменяется исключительно в исходнике (PSD/GIF файле);

  • Одежда склеивается вместе с частями шаблона;

Spritesheet персонажа из Hotline MiamiSpritesheet персонажа из Hotline Miami

+ Упор делается на финализацию работ перед отправкой;
+ Лёгкий импорт в движок игры;
- Многократно возрастающий объём работы;
- РУТИНА;
- Не подходит проектам, в которых используется большой размер спрайтов;

Отличный пример: Hotline Miami

Как вы уже понимаете, нами был выбран 2 вариант. Почему?
На это повлиял целый ряд причин:

  • Отсутствие инструментария для анимации (игра разрабатывалась на Love2D);

  • Необходимость разгрузки программиста от лишней работы (переизбыток задач);

  • Тримминг текстур (упаковка кадров анимации в spritesheets);

  • Малый размер спрайтов;

А теперь поподробнее.

  • Заказчику было необходимо добиться максимальной производительности, чтобы проект смог быть запущен на устройстве любой мощности;

  • Разработка инструментария для анимаций не рассматривалась вовсе, т.к. эти силы разумно было бросить на встроенный level-editor (редактор уровней) и проработку ИИ (искусственного интеллекта) врагов;

Тримминг кадров анимации и упаковкаТримминг кадров анимации и упаковка

Продолжение в части 2.

Подробнее..

Советы начинающему GameDeveloperу

24.02.2021 00:19:22 | Автор: admin

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

Сразу оговорюсь, что статья ни в коем случае не претендует на истину, это всего лишь субъективное мнение автора. Постарался собрать ресурсы и советы, которые либо использую сам, либо за которые многое бы отдал лично, когда был на старте карьеры.

Ресурсы, популяризирующие GameDev

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

Книги

Фильмы

Youtube-каналы

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

Вторые же два канала я лично смотрю для получения новостей из игровой индустрии и обзора интересных событий в игровом сообществе.

В целом, данного конфига мне хватает, пробовал вариант с бОльшим количеством каналов, но тогда количество дублирующей информации просто зашкаливает (хотя те же самые XYZ и DTF часто ссылаются на GDC)

Ресурсы по геймдизайну

Книги

Youtube-каналы

Ресурсы по разработке

Книги

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

Youtube-каналы

Unity-сообщества

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

Советы

  • Наличие GitHub аккаунта повышает вероятность положительного ответа, главное не забывать подробно расписывать README-файл.

  • Неплохо бы иметь аккаунт на итче, в идеале, чтобы как раз GitHub вел на итч: так можно будет потрогать игру ничего не скачивая и не собирая (делайте web-билды).

  • Также приветствуется наличие выпущенных тайтлов в сторы.

  • Не лишним будет участие во всевозможных хакатонах/гейм-джемах (хорошая возможность познакомится с единомышленниками и положить проект в копилочку).

  • Геймдев-встречи в Random Coffee - отличный способ узнать много нового, а главное побороть страх общения с незнакомыми людьми.

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

  • Старайтесь не делать однотипные проекты - у каждого второго в портфолио игра астероиды и код Ctrl-C, Ctrl-V под копирку (к вопросу про курсы выше).

Надеюсь, данная статья принесла вам пользу, планирую дополнять ее по мере поступления новой информации. Если зайдет, то возможно будет пост посвященный вопросам собеседования на позицию Unity-джуна.

Ну и самое главное - любите игры

Подробнее..

Опыт разработки первой мобильной игры на Unity или как полностью перевернуть свою жизнь

16.02.2021 16:13:42 | Автор: admin

От кого и для кого

Доброго времени суток! Меня зовут Николай, и я хочу рассказать свою историю и поделиться своим небольшим опытом в разработке своей первой игры. С чего начинал и какие трудности пришлось преодолеть на пути разработки. Статья ориентирована на тех, кто начинает, думает начать или уже разрабатывает свою первую игру. Зачем? Потому что на стадии разработки своей первой игры, сам не однократно читал статьи о подобном опыте, после прочтения которых "наматывал сопли на кулак" и продолжал разработку дальше. От идеи до выпуска в магазин.

Внимание! Статья получилось длинной, так что запаситесь чаем! Если не хочется долго читать, то выжимка из советов в концы статьи.

С чего все начиналось

Шел третий курс универа

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

Выбор направления

Появилась острая необходимость найти "дело", которое будет приносить удовольствие, не придется отрываться от современного мира на длительный срок и иметь финансовый достаток в перспективе сравнимый с моей по образованию профессией. Конец 4 курса универа и мой выбор пал на IT индустрию, а именно на python разработчика. Уделив 2 недели теории, в частности технической документации языка, я начал развивать логику и выполняя задачки каждый день на протяжении полугода, пока в конце декабря 2018 года не обнаружил геймдев.

А вот и Unity!

Выглядит комично или даже банально, но я повелся на клик-бэйт видео с подобным названием "Как сделать свою первую игру за 15 минут" или "Делаю крутую игру за 5 минут без регистрации и смс". Посмотрев данные материалы, в голове появилась мысль, выделить себе пару дней в своем графике, и утолить свое любопытство, установив данную среду разработки на свой компьютер. Потыкав разные кнопочки, и написав код методом "copy-paste", я пришел в неописуемый восторг! Моя творческая натура внутри меня ликовала. Ведь это было так приятно наблюдать за тем, что ты "сам" написал пару минут назад, сейчас заставляет кубик крутиться, перемещаться или менять цвет. Так уж вышло, что средой разработки установленной на мой компьютер оказалась Unity.

Почему Unity?

Он бесплатный, не такой сложный в освоении, большое сообщество и тонны ресурсов для самообучения, поэтому отлично подходит для начинающих разработчиков. Мобильный рынок заполнен проектами созданные на Unity. Даже такие крупные компании как Blizzard, Riot Games, CD Project RED выпустили всеми известные хиты как Hearthstone, Wild Rift и Gwent, используя эту платформу. Приняв волевое решение, я решил уйти в геймдев на пару с Unity.

Подготовка к разработке

Формирование идеи

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

  1. Проект не разрабатывать больше 2-х месяцев, иначе увеличивается вероятность потерять энтузиазм и он окажется на полке "потом доделаю" так не дойдя до выпуска;

  2. Проект должен быть простой, легкий, желательно иметь небольшую изюминку. Иначе нафантазировав себе в голове крутой ААА проект с сетевым режимом и открытом миром, рискуете себе "сломать зубы", потеряв всякую мотивацию к разработке и потеряться где то в пучине депрессии и отчаяния;

Мой выбор

2Д мобильная аркада с сетевым режимом до 6 человек , рейтинговой системой и вознаграждением. Разработка, которой заняла отнюдь не 2 , а все "12 месяцев".

Аргументы "за":

  • Мне показалось заставлять двигаться объекты будет проще, чем те же 3Д;

  • Мобильный рынок огромен и его доля более половины всей игровой индустрии;

  • Писать сюжеты для игр я не умею, да и опыта в этом нет никакого, поэтому я решил сделать упор на веселье. А играть всегда веселее вместе! Поэтому сетевая;

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

Аргументы "против":

  • Игра уже становилась не так уж проста, как советовали более опытные коллеги;

  • Сложность и продолжительность разработки для не опытного "птенца" увеличивалась многократно.

Аргументы "за" были очень привлекательны и я решил рискнуть. Как говорится - "Чем чёрт не шутит" и "Была не была"!

Знакомство с Unity и его изучение

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

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

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

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

Как только я накидал определенный план действий и уже собирался начать делать свой первый будущей "шедевр", встал серьезный вопрос

Где я возьму картинки, музыку и остальные элементы для своей будущей игры? Ведь я совершенно не умею сам это создавать

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

А ты сам все это нарисовал? А музыку ты писал тоже сам?

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

Совет: Не чурайтесь использовать чужие наработки или шаблоны, которые продают или прибегать к работе фрилансеров! Это взаимосвязанная выгода! Конечному пользователю все равно, сами вы рисовали самолетик несколько часов или потратили 10$ на его покупку в магазине, ведь главное результат!

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

Совет: Отслеживайте скидки на продаваемые ассеты в различным магазинах, особенно под новый год! Можно приобрести кучу ассетов по выгодной цене со скидкой до 90% в такое время.

Непосредственная разработка

Первые шаги

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

На этом этапе моя игра имела следующий вид:

Главное начатьГлавное начать

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

Совет: Не думайте, что сетевая игра, будет легче чем написать простенький сюжет. Это совершенно не так.

От простого к сложному

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

Эх, как же сильно была переработана финальная версия интерфейсаЭх, как же сильно была переработана финальная версия интерфейса

Интерфес и меню

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

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

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

  • усталость

  • потеря интереса

  • неувереность в своих силах

  • все кажется адом и этому нет конца и края

Совет: Скажу то, что я прочитал когда сам проходил этот этап. НЕ СДАВАЙСЯ! Как бы не было сложно, ни в кое случае НЕ СДАВАЙСЯ и НИ ШАГУ НАЗАД! Дойдя до самого конца ты познаешь лавину экстаза и самоудовлетворения от того, что ты не бросил все! И разумеется бесценный опыт!!!

Однопользовательский режим

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

Концептуальные различие с финальной версией отстствуютКонцептуальные различие с финальной версией отстствуют

Оптимизация

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

Я начал оптимизировать свой проект, только спустя 8 месяцев разработки. Из-за чего оптимизировать надо было много и долго:

  • картинки

  • материалы

  • звук

  • шейдеры

  • настройки камеры, рендеринга

  • интерфейс

  • скрипты

Это заняло у меня еще не меньше двух недель.

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

Одна голова хорошо, а несколько лучше

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

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

Реклама и внутриигровые покупки

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

Софта для рекламных интеграций имеется множетство, в том числе и от самой Unity, так называемая Unity Ads. Однако, мой выбор пал на Google AdMob. Почему не Unity Ads? Почитав обзоры, я узнал, что контент рекламы содержит казино, рулетки и ставки. Тут уже на вкус и цвет, как говорится, но я не хочу чтобы реклама была связана с подобного рода сервисами. Я использовал межстраничную и рекламу с вознаграждением.

Совет: Реклама с вознаграждением, намного лучше, ведь игрок сам нажимает на просмотр рекламы, чтобы получить какие-либо "плюшки" в игре. Разработчик и пользователь в плюсе!

Покупки в игре, я реализовал подобным образом:

Финальная версия игры

"12 месяцев" кропотливой работы , и финальная версия выглядит примерно так:

Меню игрыМеню игрыСетевой ГеймплейСетевой Геймплей

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

Совет: Тут необходимо открыть еще одно "второе" дыхание , к ранее уже открытым +100500

Публикация игры

Большим плюсом выбора Unity - кроссплатформенность, что позволяет один проект выпустить на всех желаемых платформах (Android, iOS,PC,WebGl и др). К моменту написания статьи игра была опубликована только для Android в Google Play Market, но не за горами ios в Apple Store.

Какие "подводные камни" имеются?

Технически публикация приложения в Google Play Market не составляет никакого труда всё интуитивно понятно и легко. Пройти проверку по возрастному рейтингу, загрузить картинки из игры, логотип и впринципе все готово.

Так в чем же проблема и где те самые "подводные камни"?

Политика конфиденциальности

Для публикации она не является необходимым элементом, однако, если в вашем приложении имеется реклама или внутриигровые покупки, то в кратчайшие сроки стоит обзавестись данной "бумажкой" и указать сссылку на неё в Google Play Console. К счастью, есть моножество ресурсов, которые генерирует данный документ за считанные секунды и сразу предлагают разместить на их сайте, удовлетворяя запросам гугла.
Если проигнорировать предупреждения от гугл, что у вас отсутствует политика конфидециальности, то приложение могут легко снять с публикации.

Совет: Не откладывайте на потом этот пункт, делайте его паралельно с публикацией!

Идентификатор клиента OAuth

Если у вас в игре имеется система достижений, рейтинга от гугл или вы хотя бы сохраняете данные игры в облаке от гугл, то необходимо, чтобы пользователь проходил процесс авторизации используя гугл аккаунт, а значит предоставлял некоторые разрешения на управления его данными. Теперь по порядку. При настройке игровых сервисов в Google Play Console, необходимо создать приложение для авторизации пользователя в Google Cloud Platforms, настроить учетные данные для идентификатора клиента OAuth, и Окно запроса доступа OAuth. Пожалуй это главный "подводный камень".
Сложность состоит не в его первоначальной настройке, чтобы сервисы исправно работали, а в том что приложение было опубликовано и не имело ограничений по количеству пользователей. Если вы намерены создавать крупнобюджетный проект, которые будет привлекать тысячи игроков, то вам придется обязательно пройти этот этап.

Сайт игры

Это не является обязательным пунктом, но лучше сделать сайт, где будут размещены новости вашего проекта, а так же политика конфиденциальности и прочие материалы для ознакомления. Оказывается в 2021 году сделать легкий и простой сайт достаточно просто. С шаблонами для разработки сайтов в Word Press, не долго думая, я останавливаюсь на нем. Для сайта необходим хостинг и собственный домен. Взвесив все "за" и "против", решил потратить пару тысяч рублей на его аренду, сроком на 48 месяцев и не "париться". В сети огромное количество предложений, так что проблем с этим тоже не было. Пару часов уходит на его настройку, и еще пару часов на наполнение его контентом. И вот уже есть свой собственный сайт для игры!

Совет: Чтобы получить заветную галочку во вкладке Окно запроса доступа OAuth в Google Cloud Platforms, иметь сайт игры и свой домен , где так же будет размещена политика конфиденциальности - является обязательным пунктом!

Совет: Так же, если используете рекламу от Google Admob, то сайт тоже необходим. В корневую папку вашего сайта добавляется файл app-ads.txt. Это позволяет рекламодателям понять, какие источники объявлений имеют право продавать рекламный инвентарь. Если не пройти авторизацию, то доход с рекламы будет сильно снижен!

GDPR

Еще одно бюрократическое препятствие осталось, на пути для публикации. Если ваше приложение имеет рекламу, то она может быть персонализированной, а значит ваше приложение собирает данные пользователей, чтобы успешно показывать рекламу. GDPR- (General Data Protection Regulation) -этозакон, принятый Европейским Парламентом, который описывает правила защиты данных для граждан ЕС. Это значит,чтобы показывать персональну рекламу, необходимо перед первым запуском вашей игры, пользователь должен принять соглашение, что ознакомлен с политикой конфиденциальности вашего приложения, а так же прочитать в каких целях будет использоваться его персональные данные и дать согласие/отказаться на их обработку. Разумееется это распространяется на резидентов из стран ЕС.

После выполнения всех выше изложенных пунктов, мое приложение успешно опубликовано в Google Play Market и не знает никаких проблем.

Краткая выжимка советов

  • Изучите рынок, и определитесь с направлением и жанром игры. Главное не стройте в начале "наполеоновские"планы, которые могут и не реализоваться!

  • Распишите план действий и пытайтесь четко следовать ему, попутно внося небольшие правки. Старайтесь укладываться в установленные планом сроки.

  • Обязательно спрашивайте мнение ваших близких и друзей о вашей игре на протяжении всей разработки.

  • Не стесняйтесь приобретать ресурсы для ускорения разработки и повышения качества своего проекта.

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

  • Изучите базовые навыки работы с редактированием изображений и звуков.

  • Соорудите себе в браузере пул полезных закладок с библиотекой различных ресурсов, имея быстрый к ним доступ.

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

  • Последнее и наверно самое важное. Никогда не сдавайтесь , верьте в себя, упорно трудитесь и рано или поздно, но у вас все обязательно получится! Если получилось у меня и миллионов других начинающих разработчиков данного ремесла, то почему не должно получится и у вас!?

Заключение

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

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

Если я где-то ошибся и был не прав - буду рад правкам и пожеланиям в комментариях. Готов ответить на любые вопросы по данной теме.

Чтобы не было недопониманий на счет даты релиза.

Впервые игра была опубликована 2 декабря 2019 года, и это было 10 месяцев разработки. После я был вынужден отдать долг своей родине. Срочную службу в армии я нес до 2 декабря 2020. После демобилизации, я сразу продолжил разработку. И 4 февраля 2021, после "12 месяцев" разработки, я выпустил проект.

Если Вам интересно посмотреть на результат моей работы, то вы можете найти в Google Play Market.

Название игры - Starlake

Подробнее..

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

27.02.2021 14:17:14 | Автор: admin
Количество установок приложения IntellectoKids Classroom & Learning games.Количество установок приложения IntellectoKids Classroom & Learning games.

Привет, Хабр! Меня зовут Андрей Романенков, я работаю ведущим программистом в IntellectoKids. Мы создаем образовательные приложения для дошкольников.

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

Но есть одно но.

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

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

Саб-модульность, многорепозиторность, подход к общему коду

Всего у IntellectoKids 4 приложения. Поскольку сервисы у них идентичны (например, логика работы с сервером, аналитика, покупки) и много одинакового кода, мы выделили общий функционал в отдельный репозиторий, который каждый конкретный проект подключает через git submodules. Выскажу довольно очевидную мысль, что когда ваши проекты с общим кодом множатся, код нужно скорее выделить в единую библиотеку. Вроде бы все это понимают, но часто откладывают на потом, а чем дальше вы откладываете, тем тяжелее будет процесс слияния.Помимо выделения репозитория для общих сервисов, мы создали также другой общий репозиторий для более базовой библиотеки утилит и вспомогательных классов.Второй вариант подключения дополнительного функционала появился у нас с добавлением Package Manager в Unity. Так можно делать, когда ваша библиотека устоялась и если вам необходимо подключить какую-то стороннюю библиотеку с репозитория как package.Когда проект длится давно, а количество контента увеличивается с каждым днем, то из-за раздувшейся истории и обилия больших файлов рано или поздно вы столкнетесь с проблемой размера репозитория. У нас текущий репозиторий перевалил за 10Гб (сейчас 14 гб), с чем справляются не все хостинги (большая часть из них ограничивает размеры хранилища).В борьбе за производительность нам помогают чистка истории и использование git lfs, а также внимательное отношение к размеру и формату импортируемых в проект ассетов. Например, импортирование mp3 и ogg файлов вместо wav; и отсекание слишком больших текстур.

Локализация, в том числе RTL-языки

Наши приложения локализованы более чем на 40 языков, включая RTL языки (предполагающие чтение справа налево). Система локализации самописная, но в целом она похожа на типовые решения из Asset Store (такие, как I2 Localization). В Google-таблицах хранятся ключи и значения. Есть базовая таблица для всех игр, и дополнительные таблицы для каждой конкретной игры. Каждая таблица в Google-документах скриптами собирается из других вспомогательных таблиц, которые редактируют локализаторы.

Данные из вспомогательных таблиц(цветные закладки внизу) попадают в финальные таблицы локализаций.Данные из вспомогательных таблиц(цветные закладки внизу) попадают в финальные таблицы локализаций.

На клиенте наши скрипты MonoBehaviour выцепляют нужные значения и выставляют их в TextMesh Pro компоненты. Клиент может обновлять таблицы как в режиме редактора, так и рантайма. У клиента таблицы хранятся в csv формате, и в память грузится только нужный язык, так как количество ключей для каждого языка превышает уже пять сотен!

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

Пример стандартной вёрстки на английском.Пример стандартной вёрстки на английском.То же окно, но на иврите.То же окно, но на иврите.

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

Когда волна больше определённого значения у Animator кролика включается параметр IsTalking.Когда волна больше определённого значения у Animator кролика включается параметр IsTalking.

Бандловость: 2 подхода. Эволюция в работе с бандлами

По мере развития проекта (добавления новых уровней и другого контента, увеличения количества локализаций) постоянно рос общий размер содержимого. С самого начала нам было понятно, что необходимо использовать бандлы, иначе размер клиента был бы огромен и сейчас составлял бы 3 ГБ. Да, не Modern Warfare, но всё же для еженедельной скачки это неприемлемо.

В какой-то момент мы, правда, провели эксперимент с выпуском таких больших релизов (тогда размер был примерно под два гигабайта), но это сразу заметно отразилось на общей статистике приложения. Сейчас у нас зашиты в билд только небольшие бандлы, необходимые для ускорения старта. В нашем основном приложении IntellectoKids Classroom&Learning Games больше тысячи бандлов общим размером 2.5 гигабайта. Может показаться, что это слишком много, но если умножить количество встроенных игр на количество языков, и добавить к этому, что у каждой игры есть множество уровней с насыщенным контентом, то всё сразу станет понятно.Из-за особенности геймплея каждая игра имеет свои нюансы объединения ресурсов в бандлы. Где-то можно поместить все локализованные фразы в один бандл, так как их общий размер мал, а где-то необходимо разделить и поместить каждый язык в отдельный бандл. В каких-то играх несколько уровней объединены в один бандл, а в других должен быть бандл у каждого уровня. При формировании бандлов мы создаём manifest файл, описывающий имена и хэши бандлов.

Загрузчик при обновлении версии качает этот manifest и проверяет, какие бандлы нужно закачать заново, а какие удалить. Изначально игра поддерживала фоновую загрузку бандлов во время игры, но позже мы отказались от такого подхода, так как он таил в себе много скрытых проблем (сценарии обновления бандла во время использования его игрой, баги Unity в выгрузке бандлов и т.п.) Сейчас мы перешли на более классический вариант, когда игроку в нужные моменты показывается экран загрузки бандлов.Самый маленький большой нюанс нашего проекта, это, безусловно, релиз и деплой более чем тысячи бандлов. На первоначальном этапе, когда бандлов было меньше, мы размещали их в Google репозитории и даже использовали веб-интерфейс Chrome для их загрузки, но довольно быстро перешли на собственный билдер-загрузчик, выполненный в виде инструментария в Unity. Он позволяет загрузить нужные бандлы, задать версионность билда и настроить другую рутину.

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

Первоначально бандлы лежали в Google Cloud Storage, затем мы перешли на Amazon Web Services. Основная статья затрат у бандлов это скачивание. С переходом на AWS и CloudFront нам удалось оптимизировать издержки. Хоть это, а также переход на новый API с доработкой инструментов деплоя занял некоторое время, но оно того стоило.

Переход на новые версии Unity

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

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

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

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

Что можно добавить к сказанному в статье?

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

Dixi

Подробнее..

Перевод Принципы нарративного дизайна

11.02.2021 12:14:32 | Автор: admin

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

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

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

1. Сначала общая тема, и только затем персонажи

Мне кажется, именно это самый эффективный способ начать построение нарративного дизайна рассказываемой истории.

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

Возьмем, к примеру, Каникулы Гуфи прекрасный мультик, правда же? Конечно, сами персонажи существовали еще до выхода фильма, но общий сюжет был продуман до того, как было сформировано взаимодействие Макса и Гуфи на протяжении всего фильма. Сценаристы Диснея хотели сделать мультфильм об отце и сыне, которые начинают замечать, что не проявляют должного уважения друг к другу, и во время похода узнают, что им нужно научиться взаимопониманию.

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

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

Без капли скромности позирую с Каникулами Гуфи, погружаясь в фильм гораздо глубже, чем кто-либо раньше

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

Как думаете, сначала появился Фродо, а затем общая тема или первой была тема, для которой автор затем придумал соответствующих персонажей?

Черновой вариант Властелина колец для нарративного дизайнера выглядел бы как-то так:

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

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

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

В этом наброске не рассказано о кольце самом весомом факторе повествования. Однако к этому моменту Толкин уже знает, что кольцо должно быть сосудом зла, которому Фродо проигрывает. Кольцо можно заменить ожерельем, предметом одежды чем угодно. Было выбрано кольцо но в целом нужно было нечто, что являлось бы вместилищем злой силы.

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

Тема путешествия Фродо была создана в ответ на тему, которую Толкин хотел использовать

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

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

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

Отношения Соры, Рику и Каири вписываются в тему, а затем превращаются в сюжет

2. Как выбрать тему

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

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

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

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

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

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

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

Формируйте тему, учитывая особенности целевой аудитории

3. Создание мира должно быть интерактивным

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

Говоря об интерактивном построении мира, я имею в виду, что все элементы мира должны быть взаимосвязаны: в лесу должна быть жизнь, в воде, даже в огне и в горах. У второстепенных персонажей должны быть определенные роли и собственная жизнь, которые показывают, что они отдельно от мира, созданного для меня, но и не просто где-то в мире, о котором мне рассказывает кто-то другой. Часть истории мира должны рассказывать персонажи, с которыми читатель, игрок или зритель проведет не так уж много времени: о чем хочет поговорить этот фермер, что местный ученый думает об окружающем мире, что он может рассказать об истории этого мира?

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

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

Сделайте мир живым

4. Двигайтесь в повествовании вперед

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

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

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

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

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

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


О переводчике

Перевод статьи выполнен в Alconost.

Alconost занимается локализацией игр, приложений и сайтов на 70 языков. Переводчики-носители языка, лингвистическое тестирование, облачная платформа с API, непрерывная локализация, менеджеры проектов 24/7, любые форматы строковых ресурсов.

Мы также делаем рекламные и обучающие видеоролики для сайтов, продающие, имиджевые, рекламные, обучающие, тизеры, эксплейнеры, трейлеры для Google Play и App Store.

Подробнее..

Отцы игровой индустрии. Крис Авеллон часть 13. Interplay и Black Isle Studios

12.02.2021 12:08:58 | Автор: admin

Я совершил так много ошибок, будучи гейммастером. А игроки за столом сразу же реагируют, если им не весело. Крис Авеллон

Это текстовая адаптация моего видео, так что если вам по какой-либо причине удобнее смотреть не проблема.

Крис Aвеллон. Думаю, любой, кто интересуется геймдевом, как минимум слышал это имя. Именно его стараниями Нью Рино из Fallout 2 ощущается настоящим городом с настоящими людьми. Если же вы не знаете, кто это, то не отчаивайтесь, ведь вы здесь, а я начинаю свой рассказ про известнейшего игрового сценариста современности.

Кристофер Фредерик Авеллон родился 27 сентября 1971 года в Алегзандрии, штат Вирджиния. Еще в 9 лет он увлёкся настолкой по вселенной Dungeons & Dragons, а в 16 познакомился с компьютерной игрой Bards Tale 2.

Закончив школу и колледж, он начал писать кампании для настольных ролевых игр по вселенным D&D и Champions, однако доход с этого был мизерным. Желая что-то изменить, он спросил у начальника, не знает ли тот более стабильного заработка, на что ответом ему была рекомендация обратиться в Interplay.

Глава первая: Interplay

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

Interplay была основана в 1983 году Брайаном Фарго. Первое время компания перерабатывала чужое программное обеспечение, а также трудилась по заказам военных. Всё это (а также то, что Interplay была нацелена на компьютерный рынок) помогло ей без каких-либо потерь пережить кризис индустрии консольных игр 1983-1985 годов.

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

Tales of the Unknown: Volume I The Bards Tale или просто The Bards Tale (вышедшая в 1985 году) стала первой нетекстовой игрой Interplay. Представляя собой классическую RPG того времени, игра не только снискала популярность, но и получила очень высокие отзывы как от игроков, так и от критиков. Было продано свыше четырехсот тысяч (!) копий, что стало абсолютным рекордом для жанра.

Изначально Tales of the Unknown задумывалась как трилогия, где части должны были нести следующие подзаголовки: The Bards Tale, The Archmages Tale и The Thiefs Tale. Однако из-за громоздкости получающихся названий Electronic Arts, которая выступала и издателем, и правообладателем, приняла решение переименовать серию, собственно в The Bards Tale.

Так на свет появились The Bards Tale II: The Destiny Knight (1986 год) и The Bards Tale III: Thief of Fate (1988 год), которые закрепили за Interplay звание успешного разработчика в жанре RPG.

За последующие десять лет Interplay успеет не только разработать еще игры, (например, Wasteland, которая станет прародителем Fallout), но и издать множество сторонних разработок. Среди них The Lost Vikings (её разработала Silicon & Synapse, в будущем известная как Blizzard), Alone In The Dark (1, 2, 3), Earthworm Jim 2, Shattered Steel (первая игра BioWare под издательством Interplay), Carmageddon, а также Fallout.

Глава вторая: первые шаги Криса Авеллона по Interplay

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

Первой действительно значимой работой Криса стал город Нью Рино в Fallout 2 (1998 год), однако он был не первым его заказом от Interplay, посему отмотаем время на два года назад, в 1996-й.

Во всех статьях, которые я прочитал в ходе подготовки этого материала, первым опытом Авеллона в качестве разработчика игр значится Star Trek: Starfleet Academy, но если верить Metacritic, еще в 1996-м, за полтора года до выхода вышеуказанного симулятора, Крис участвовал в разработке игры, именуемой Conquest of the New World.

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

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

Разобравшись с первым опытом, перейдём к более серьезным проектам, первым из которых стал уже упомянутый симулятор пилота космического судна во вселенной Star Trek. Долго говорить о нем не получится. Это один из проектов, где Авеллон выступал геймдизайнером, правда, не всей игры, а дополнительных миссий. Оценки симулятор получил смешанные, однако продался отлично сорок тысяч копий только за первые 4 дня, и уже на следующий проект Криса повысили до главного геймдизайнера.

Descent to Undermountain вышла 15 января 1998 года и представляла из себя очень сырую игру в жанре RPG. Хоть это была первая ролевая игра в полноценном 3D, никакой революции она не произвела. Продажи были удручающими, а критики ругали игру за все, что только можно, и даже назвали её худшим компьютерным воплощением D&D. Однако вины разработчиков в этом не было. Мало того, что ради вышеупомянутого 3D пришлось использовать движок от шутера, так еще и издатель требовал выпустить игру под Рождество.

Позже Крис Авеллон признается, что до сих пор жалеет, что трудился над этой игрой. Ведь именно из-за разработки Descent to Undermountain он отказался от предложения поработать над RPG, которая впоследствии станет культовой Fallout. Справедливости ради, стоит упомянуть, что частичка Криса в первой Fallout всё же есть в качестве внутриигрового персонажа. Плюс, в титрах ему выражена благодарность.

Глава третья: Fallout

Не будем сильно заострять внимание на истории создания Fallout. Если интересны подробности, то на StopGame есть отличная серия видео, посвященная этому. Я же пробегусь по самым важным для понимания местам.

В 1988 году из-под пера Interplay и под издательством Electronic Arts вышла Wasteland. Она стала одной из первых игр в ядерном постапокалипсисе и не только получила высокие оценки, но и продалась тиражом свыше ста тысяч копий. Несмотря на такой, хоть и не ошеломительный, но успех, продолжения игра тогда не получила (в 2003 году Брайан Фарго выкупил права на Wasteland у Electronic Arts, а в 2014-м выпустил Wasteland 2, однако, она стала не продолжением, а перезапуском серии).

В связи с тем, что у Interplay не было прав на использование названия Wasteland, компании пришлось выкручиваться. Так, в 1994 году Тим Кейн в одиночку (в 1996 году к нему присоединится только что созданное подразделение Dragon Play, более известное под именем Black Isle Studios) начал разрабатывать игру под названием Vault 13: A GURPS Post Nuclear Adventure. Как можно понять из названия, игра работала на ролевой системе GURPS, однако, в начале 1997 года ее создатель расторг контракт с Interplay.

Не столь важно, из-за чего это произошло, важно то, что Black Isle пришлось изменить название, а также в кратчайшие сроки создать новую ролевую систему для своей игры. Так появилась S.P.E.C.I.A.L., оригинальная система навыков, которая отныне будет так или иначе присутствовать в каждой игре серии.

Fallout: A Post Nuclear Role Playing Game вышла 30 сентября 1997 года и была облюбована игроками, критиками, собаками, всеми. Вот вырезка из рецензии популярного игрового портала GameSpot: Fallout это одна из лучших ролевых игр, которая будет выпущена в ближайшие несколько лет, она успешно развлекает геймеров, предоставляя свежий и убедительный сюжет, хорошую графику и звук, а также внимание к тем маленьким деталям, которые могут превратить хорошую игру в отличную.

Несмотря на отличный прием продажи у игры были скудными. Точных цифр нет, но по примерным прикидкам в первую неделю было продано около 25 тысяч копий. Как ни странно, низкие показатели не смутили руководство, и Interplay дала добро на разработку продолжения.

Тим Кейн, который был главным программистом и идейным вдохновителем Fallout, устал от своей игры, перегорел и ушел. Так и вышло, что только что прибывший в студию Крис Авеллон выступил не только главным геймдизайнером, но и главой разработки. Его сценарный почерк виден во всем: если в первой части в большинстве квестов необходимо было выбирать между абсолютным добром и абсолютным злом (как, к примеру, в главном квесте Джанктауна), то во второй части стороны не делились на черное и белое.

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

Но даже этот город не сравнится своей вариативностью с уже упомянутым Нью Рино, все квесты и персонажей для которого прописал Авеллон. При его прохождении приходится выбирать между четырех зол в лицах мафиозных группировок. Для этого города нет верных решений. Вот, что значит серая мораль (да, Call Of Duty: Modern Warfare?).

Игра снова была воспринята на ура и снова плохо продалась, но отзывы критиков взяли верх, и Interplay снова дала добро на разработку продолжения, однако перед этим Black Isle взяла передышку. Все разработчики устали от ядерных пустошей, а потому взялись за проект в сеттинге Planescape (ответвление классической Dangeons & Dragons). Авеллон, показавший, что он хороший руководитель, возглавил разработку следующей RPG, не менее культовой, чем Fallout. Называлась она Planescape: Torment.

Глава четвертая: Planescape: Torment

Когда я пришёл устраиваться в Interplay, руководителем подразделения TSR был Марк О Грин. Один из его вопросов ко мне звучал так: Если бы вам поручили создание игры по миру Planescape, на что она была бы похожа? Мне даже не пришлось особо размышлять я сразу ответил: Она бы начиналась с гибели главного героя с момента пробуждения в Морге и попыток осознать случившееся и фокусировалась на том, что происходит после этого. Не знаю, впечатлило это его или нет, но работу я в итоге получил. Крис Авеллон

На момент 1998 года жанр СRPG существовал уже около двух десятков лет. Авеллон, который начал знакомство с ним еще 1985 году, когда у него появилась уже упомянутая The Bards Tale 2, устал от классических ролевых механик и захотел сделать что-нибудь особенное, что-нибудь непохожее ни на одну из существующих RPG.

Я сидел и вспоминал RPG, в которые мне доводилось играть, а потом попробовал составить список всего, от чего я устал в подобных проектах. И попутно придумывал, как можно было бы иначе реализовать вещи, которые меня не устраивали. Заставлять игрока загружать сохраненную игру после гибели его персонажа это пустая трата времени. По идее, как разработчик, вы должны дать человеку возможность играть до тех пор, пока он сам не захочет прекратить. Поэтому наделение главного героя бессмертием и реализация смерти как части игрового процесса представлялись мне гораздо интереснее, чем стандартные процедуры сохранения и загрузки. Хотелось сделать смерть механикой, которая могла бы решать какие-то игровые задачи. Да и все эти эльфы, гномы и другие набившие оскомину расы мне чертовски надоели, и я подумал: Разве люди не готовы к чему-то новому? Torment стал хорошей площадкой для тестирования разных механик и особенностей, не слишком привычных для жанра.

И при создании Torment он пошел по рисковому пути: решил произвести деконструкцию жанра. Взять, и разом избавиться от всех существующих в ролевых играх клише. Так, еще на стадии прототипирования было решено ввести в игру следующие фишки:

  • Крысы одни из самых опасных противников.

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

  • Бордели предназначались для интеллектуальных утех, а не для физических.

  • Обладание информацией гораздо важнее высоких значений характеристик.

  • Смерть не заканчивает игру, а порой и вовсе становится элементом игрового процесса.

  • Вы часто натыкаетесь на вещи и ловушки, оставленные вами же в прошлых жизнях.

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

  • Забудьте об эльфах, гномах и всяком таком.

  • Простое и доступное воскрешение павших спутников для быстрого продолжения игры.

  • Дьяволы чертовски честны, а вот ангелы не особо.

Тим Донли, ведущий художник, вспоминает, как зарождался проект:

Мы с Крисом просто зашли в комнату, где лежали рулоны оберточной бумаги, сели и нарисовали все уровни игры, связанные между собой. Получилось что-то вроде Google Maps в изометрии. Собственно, после этого нам осталось только взять и сделать игру. Тогда мы и начали искать художников. Команда подобралась следующая: я сам, Аарон Майерс, Дэн Спитцли и Крис Авеллон. Мы сидели в крохотной комнатке я клянусь, даже у заключенных камеры больше! Думаю, помещение было где-то два на четыре метра. Но время, проведённое там, было одним из самых весёлых в моей жизни: мы приходили в 10 утра, садились и уходили в 10 вечера, и всё, чем мы там занимались, смеялись и делали игру.

Тим ДонлиТим Донли

По окончании прототипирования возник вопрос: какой использовать движок? Fallout Engine не подходил, так как он был завязан на системе S.P.E.C.I.A.L., а игру по лицензии D&D необходимо делать на правилах D&D. К тому же Крис не видел новую игру с боями в пошаговом режиме.

Было решено одолжить движок у коллег по цеху, так игру стали делать на Infinity Engine движке, разработанном BioWare и использовавшемся при создании культовой Baldurs Gate. Хорош он был тем, что все правила D&D уже были вшиты в него, а бои проходили в реальном времени с возможностью использования активной паузы. Это позволяло играм, сделанным на нем, оставаться тактическими, но в то же время гораздо более динамичными.

Однако не все было так гладко.

В Torment мы хотели сделать столько всего интересного, что к большей части этого движок BioWare оказался не подготовлен. Он не был рассчитан на игру с восемью сотнями тысяч слов. Нам пришлось основательно над ним поработать, так как в Torment были странные вещи, которые делали странные штуки. Мир Planescape -довольно необычное место, и Infinity Engine не был готов к тому, что игрок может подобрать меч, а тот внезапно заговорит с игроком, и вот у вас уже есть меч как отдельный персонаж. Но мы добились от движка BioWare всего необходимого, так что явно не прогадали, сделав его основой для нашей игры.

Рассказывает Тим Донли.

Возникли проблемы с движком и у Авеллона:

Во время работы над Neverwinter Nights 2 и Neverwinter Nights 2: Mask of the Betrayer я обычно использовал проверки разговорных навыков (Насмешка, Блеф, Дипломатия, Запугивание), которые можно было применить в любой беседе. При разработке Torment такой возможности у нас не было, поэтому все проверки в диалогах пришлось делать зависимыми от характеристик персонажа (Сила, Мудрость, Интеллект). Думаю, если бы не моя работа над Fallout 2, диалоги в Torment получились бы гораздо хуже. Когда я осознал, что в разговоры с NPC можно внедрить проверку характеристик персонажа, я сразу же понял, что нечто подобное нужно будет реализовать и в Torment. Да, это полностью моё решение сделать диалоги такими, какие они есть, и я не думаю, что совершил правильный выбор: игровые механики должны стоять во главе угла, а механика Torment получилась слишком громоздкой.

Признается Крис.

Ну а ближе к концу разработки в студии начался классический для видеоигровой индустрии кранч (очень большие переработки на износ ради выпуска проекта к обозначенному сроку). Колин Маккомб (второй геймдизайнер) вспоминает:

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

Колин МаккомбКолин Маккомб

Несмотря на все сложности и невзгоды, 12 декабря 1999 года Planescape: Torment увидела свет и произвела фурор среди всех, кто в нее поиграл. К сожалению, поиграли в нее немногие.

Продажи снова оказались неудовлетворительными. Возможно это проклятие черного острова. За семь лет своего существования Black Isle Studious разработала семь игр, из которых три стали классикой жанра, но ни одна из этих игр не оправдала коммерческих ожиданий. Interplay, которая за последние три года издала огромное количество неокупившихся проектов, начала свой болезненный, но очень короткий путь в бездну.

Глава пятая: Icewind Dale

К 2000 году команда Black Isle устала создавать литературные шедевры. Planescape: Torment, которую сделали за год, насчитывает в себе 800 тысяч слов. Для сравнения, классика мировой литературы, гроза всех десятиклассников, роман Льва Толстого Война и мир состоит примерно из 450 тысяч слов. Конечно, стоит учесть, что произведения написаны на разных языках, и за одно прохождение Torment вы не увидите и половины из этих 800 тысяч слов. Однако объем работ от этого не уменьшился.

Поэтому следующим проектом студии должна была стать игра по трилогии романов Роберта Сальваторе, носящих название The Icewind Dale Trilogy. Однако разработчики решили, что в таком случае игра будет вторична, и откатили действие на 30 лет назад относительно событий книг.

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

Уже во второй раз команда перелопатила движок от Baldurs Gate теперь ради введения в игру огромных врагов и усовершенствования боевой системы.

Если Baldurs Gate состояла из боев на 50%, Planescape: Torment на 10%, то в Icewind Dale приходилось биться все 90% времени. Этим отлично характеризуются все три игры, и, как можно заметить, в последней упор сделан на боевку. Поэтому игра, которая, кстати, вышла 29 июня 2000 года, многим показалась хорошей, но несколько вторичной относительно своих прародителей, это очень хорошо показывают вырезки из рецензий.

PC Gamer: Отличная игра для тех, кто любил боевой сегмент в Baldurs Gate.

Daily Radar: Хотя через пять лет мы не будем оглядываться на Icewind Dale как на игру, изменившую мир, мы, вероятно, будем помнить ее как одну из самых приятных РПГ, в которые имели удовольствие играть.

А вот что через несколько лет скажет Крис Авеллон в интервью:

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

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

Здесь стоит вспомнить, что у Interplay, которая давала добро почти на любую игру, наконец закончились средства. Первым звоночком стало то, что компания в 1998 году выставила на продажу свои акции. Не помогло ситуации и открытие студии Interplay Films. Деньги на это были потрачены, однако ни одного фильма никто так и не увидел, и уже в 2000 Interplay Films прикрыли.

Поэтому от успеха Icewind Dale зависело, выживет ли компания или нет. На счастье Interplay, четвертая игра Black Isle не только окупилась, но и несколько недель держалась в топах продаж, что позволило компании не погрузиться на дно.

Black Isle же тем временем начала разработку совершенно нового проекта.

Глава шестая: Black Isles Torn

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

Игра была анонсирована 22 марта 2001 года и почти сразу завладела вниманием прессы. Нас ждала первая игра Black Isle в 3D и с активной камерой. Кроме того, управлять игрок мог только собственным персонажем, а за встреченных ему на пути напарников должен был отвечать искусственный интеллект, которому можно было давать приказы.

Сейчас это довольно банальный прием, тогда идея была чуть ли не революционной. Плюс ко всему, игра работала на нашумевшем S.P.E.C.I.A.L. Ролевую систему Fallout модифицировали для работы в фэнтезийном сеттинге игры.

Но уже через 3 месяца после анонса финансовые проблемы Interplay выстрелили, аки Чеховское ружье.

На момент анонса в 2001 году игра была в разработке уже 14 месяцев. Использование незнакомого студии движка, а также переход в полноценное 3D очень сильно затянули разработку, и на момент анонса игра была готова хорошо если на 70%, что означало, что получить с нее деньги издатель смог бы в лучшем случае еще через полгода, уже не говоря о том, что в новой франшизе не было никакой финансовой уверенности.

Поэтому 25 июля 2001 года Black Isle's Torn был отменен, а в Black Isle провели сокращения, в ходе которых было уволено 5 человек.

За день до начала увольнений они сказали мне: смотри, завтра мы отменим Torn, уволим несколько сотрудников, а потом начнём разработку Icewind Dale II. Фергюс будет это отрицать, но тогда он сказал: Нам нужно сделать эту игру за четыре месяца. Что думаешь?. Я ответил: Нет, это исключено. Такого не будет никогда. А они в ответ: У нас нет выбора. У Interplay проблемы Джош Сойер

Джош СойерДжош Сойер

Глава седьмая: Icewind Dale II

На короткой дистанции единственной хорошо продавшейся игрой Black Isle являлась Icewind Dale, поэтому неудивительно, что руководство заставило студию делать именно ее сиквел.

Джош Сойер написал сюжет и основных персонажей для Icewind Dale II за 48 часов, но игры это не только сценарий. И, конечно же, в 4 месяца они не уложились, и разработка заняла 10, хотя даже эта цифра феноменальна для игры такого высокого качества.

Из-за сжатых сроков, отправления в помойку полутора лет работы компании и сокращений в студии сложилась нервная обстановка. Часть сотрудников ушла, а остальные остались из-за Помните, я упоминал, что Interplay дала добро на продолжение Fallout? Так вот проект имел рабочее название Van Buren (на стадии прототипирования игры в Black Isleназывались в честь президентов США), и Крис Авеллон уже продолжительное время корпел над сюжетом, персонажами и лором, а некоторое число программистов вовсю ваяли для игры свой трехмерный движок. Именно из-за страсти к этому проекту многие и остались на своих местах, хоть атмосфера в студии сложилась не самая благоприятная.

Итак, по истечении 10 месяцев, 26 августа 2002 года, Icewind Dale II ушла на золото и стала продолжением типа все то же самое, только больше. Из важного стоит отметить, что Icewind Dale II создавалась на основе вышедшей в 2000 году третьей редакции правил D&D, которая ввела довольно серьезные изменения в игровой процесс.

А еще Icewind Dale II стала последней игрой, разработанной на уже легендарном движке Infinity.

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

PC Gamer (оценка 87/100): С некоторыми изящными хитростями в движке и ослепительной реализацией 3-его издания правил D&D, Icewind Dale II это как вытащить вчерашнее буррито и понять, что разогретое оно еще лучше.

Armchair Empire (оценка 70/100): Без принятия правил 3-го издания Icewind Dale II по сути является той же самой D&D игрой, выпущенной много лет назад.

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

Однако радоваться оставалось недолго.

Глава восьмая: сторонние игры

Перед переходом к плачевным событиям стоит упомянуть о еще двух играх, над которыми поработал Крис в сторонних студиях.

В 2001 году, между двумя частями Icewind Dale затесалась Baldurs Gate: Dark Alliance, разработанная Snowblind Studios совместно с небольшой командой из Black Isle. Авеллон же выступал вторым геймдизайнером, а игра представляла из себя экшен-RPG в уже известной вселенной.

Игра получила высокие оценки и хорошо продалась.

В 2003 году вышлаLionheart: Legacy of the Crusader. В ней Крис помогал со сценарием, однако игра с треском провалилась, получив неутешительные отзывы. В основном игру ругали за ее третьесортность:

PC Gamer: Я не могу рекомендовать эту игру ветеранам, но я мог бы дать половинчатую рекомендацию новичкам.

eToychest: Обеспечивает достойный опыт ролевых игр на ПК, но он просто не так совершенен, как игры Fallout, из которых он происходит.

Глава девятая: крах

В 2002 году BioWare ушла из-под крыла Interplay, однако права на серию Baldurs Gate остались за последней. Посему продолжение прибыльной франшизы было поручено делать Black Isle.

Разработка Baldurs Gate III: The Black Hound шла своим чередом, пока в 2003 году не случилось страшное: Interplay потеряла лицензию на игры по D&D. Для триквела Baldurs Gate это означало только одно отмену.

Снова игра, над которой студия работала полтора года, была отменена, и, в отличие от Torn, эта была закончена на 80%. Титанический труд ушел в небытие вместе со всеми наработками, и сейчас все, чем мы можем довольствоваться, это горстка концепт артов.

Все проблемы последних лет повлекли за собой три значимых события.

Во-первых, в 2002 году Interplay покинул ее основатель Брайан Фарго. Контрольный пакет акций компании скупила французская Titus Interactive и изжила Брайана из собственного детища. Но он не отчаялся и в том же году основал inXile Entertainment, которая здравствует и по сей день.

В тот момент, если бы у меня была такая возможность, я бы уволил всех остальных сотрудников компании и оставил бы только Black Isle Studios. Я бы оставил сорок человек, и всё бы продолжилось. Я бы построил Interplay заново. Но никто бы не дал мне такой возможности.

Во-вторых, сразу после отмены Baldurs Gate III Крис Авеллон покинул Black Isle.

Когда Baldurs Gate III: The Black Hound отменили, я осознал, что любая игра студии может оказаться последней. Так что как бы я ни любилFallout: Van Burenи сколько бы лет я на него ни потратил, я знал, что его скорее всего постигнет судьба Baldurs Gate III, так что я решил покинуть компанию. Это было трудное решение, но это был единственный возможный выход.

В-третьих, одновременно с Авеллоном студию покинул основатель Black Isle Фергюс Уркхарт. Недолго думая он основал Obsidian Entertainment, в которую, так или иначе, позже попадут почти все выходцы из Black Isle, включая, собственно, Криса (о чем мы и поговорим во второй части).

В целом я был в курсе того, что происходит в Interplay, и я как мог пытался поделиться этой информацией с командой. Я понял, что Black Isle больше не получит необходимой поддержки, когда Interplay потеряла лицензию на Dungeons & Dragons. Они могли бы найти способ сохранить её, но у них были в приоритете другие проблемы. Это означало, что игра, над которой работали два года, должна будет отправиться на помойку. Это послужило для меня предзнаменованием того, что и Black Isle, и Interplay, вероятно, скоро прикажут долго жить.

И они оказались правы. Всего через год Black Isle, в которой они проработали семь лет, создали столько игр, которые с теплотой вспоминают до сих пор, была расформирована.

Перед этим она успела выпустить посмертную Baldur's Gate: Dark Alliance II, хорошо принятую критиками, но не спасшую студию.

Весь остававшийся им год они потратили в том числе и на Fallout3: Van Buren, однако, как не сложно догадаться, и он был отменен.

Все гиперссылки ведут к файлу играбельного технического демо Fallout 3Все гиперссылки ведут к файлу играбельного технического демо Fallout 3

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

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

И за месяц до расформирования и он ушел из студии.

У нас в команде был замечательный художник по персонажам, и это был, по сути, последний художник, который у нас оставался. Руководство Interplay перевело его на другой проект, не от студии Black Isle. Я тогда и говорю: Знаете, что? Если вы отбираете нашего единственного художника, то, видимо, вы не заинтересованы в нашей работе.

Interplay же до сих пор влечет свое жалкое существование, время от времени продавая оставшиеся у нее активы

Эпилог

Interplay была огромной компанией. Благодаря таланту и удаче основателей, она возымела многомиллионные активы. Подобно Икару, она воспарила на высоту других игровых гигантов, но играть по их правилам не захотела. Она инвестировала в те проекты, которые были смелыми, а не прибыльными. Нанимала людей, которые хотят делать игры, а не деньги. И именно бизнес моделью опалила свои активы, и они сгорели дотла.

Современные игроки говорят: Ubisoft, ОСТАНОВИ КОНВЕЙЕР, Electronic Arts, ХВАТИТ КАЖДЙ ГОД ВПУСКАТЬ ОДНУ И ТУ ЖЕ ИГРУ С ДРУГОЙ ЦИФЕРКОЙ В НАЗВАНИИ, Activision, ХВАТИТ ПИХАТЬ ВЕЗДЕ ДОНАТ!!!

Interplay не следовала веяниям рынка, и где она сейчас? Да, многие ее игры стали классикой, время расставило все по местам, но как это помогло компании? Никак.

Так может Харви Дент прав?

Либо ты умираешь героем, либо живёшь до тех пор, пока не становишься негодяем

Подробнее..

Наши Twisted Metal, Rainbow Six и PUBG с конкурса разработчиков на Unreal

24.02.2021 00:19:22 | Автор: admin

Disgusting Men и Epic Games проводят конкурс разработчиков игр unrealcontest.disgustingmen.com/2020. Конкурс закончился,. Подобрал игры, которые меня порадовали и поразили.

DEFCORE

Пушки выглядят стильноПушки выглядят стильно

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

POSEIDON

Вроде деревья пластиковые, а вроде красивая картинкаВроде деревья пластиковые, а вроде красивая картинка

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

ОТРЯД ЕГО ВЕЛИЧЕСТВА

Обещают наш ВедьмакОбещают наш Ведьмак

Это RPG для ПК, PS 4 и Xbox One. Действия разворачиваются в Славомирской Империи аналоге Российской империи в начале XIX века. Разработчики хотят сделать сложный сюжет, который очень зависим от выбора игрока. Нужно будет решать сложные задачи и выбирать сторону: вот тут крестьянский бунт, вот тут разобраться в преступлении. Протагонист молодой офицер Имперской тайной разведки, и ему даны чрезвычайные полномочия. Обещают контраст бедности нищих деревень и богатой помпезности дворцов знати.

DIESEL GUNS

Тут совсем просто: мультиплеерная олдскульная игра про сражения на странных тачках, сделанных из хлама, в духе Twisted Metal и Vigilante 8. Кстати, игра уже год в раннем доступе в Steam.

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

Подробнее..

Категории

Последние комментарии

© 2006-2021, personeltest.ru