Создание гравитационной и портальной пушки. Как правильно убить персонажа в машине и создать теплый вязаный интерфейс
Эта статья продолжает цикл статей об игровом движке StalinGrad.
Кода практически не будет, поэтому, если вы не web разработчик, можете
просто почитать про архитектуру и ООП. И таки да — про HTML5 тут снова
ничего не будет, только DHTML-хардкор :)
Сразу короткое демо: Пример 1 — паралельные миры Пример 2 — один мир с разных камер Пример 3 — боты Пример 4 — нянкэт, облака и портальная пушка
Логика работы
Как-то мне захотелось написать квест, где мужик ходил бы по бару и мог
разговаривать с разными людьми. А события и люди в этом баре —
генерировались случайно, и каждый раз перед игроком появлялась новая
цель.
Как сделать это хождение на JavaScript? В большинстве случаев это
выглядит так: есть экран и персонаж, который ходит от одного конца
экрана в другой.
Реализовать довольно просто. Но что если бар будет больше чем экран?
Тогда можно придумать такую схему: есть экран — это DIV1 c
позиционированием. Внутри этого лежит другой DIV2, который больше чем
DIV1 и позиционируется относительного родителя. DIV2 — это и есть наш
уровень. Все люди и объекты в баре лежат внутри DIV2 и позиционируются
от его левого края.
Реализовать схему в принципе просто, но не нужно. Это абсолютно
неправильный подход. Что если у нас в баре будет тысяча объектов? Мы
получаем не масштабируемую основу, которая со временем может заставить
нас страдать.
Я начал искать информацию о том, как делают 2D игры и перебрал кучу
статей на Хабре про игры на HTML5. К сожалению, во всех найденных
статьях, которые прочитал, не нашлось ни одного алгоритма или отсылки к
архитектуре — только скрипты одноразовых игр. Этот факт меня опечалил, и
я спросил парней на работе о том, что они думают. Мой коллега, Виталий
Никитин, посоветовал прочитать книжку «Секреты разработки игр в
macromedia Flash MX» (автор Jobe Makar). С этого все и началось.
Книга оказалась фантастической. Если пропускать все моменты про Flash,
можно узнать очень много про алгоритмы и логику. По крайней мере, для
меня, как человека ничего не знающего о движках и опыте разработчиков
игр, книга была просто откровением.
С полученными знаниями, представление об играх расширилось и возникло
острое желание написать игру с проработанным миром. Сначала я посмотрел
на игровые движки на JavaScript, которые уже были на рынке. Обратил
внимание на то, что у многих отсутствовала нормальная документация.
Хардкорные варианты — тоже не нашлись. А ведь следуя логике Джоба Макара
и опыту игростроя, уровень хардкора можно сделать запредельным.
Что я хотел? Марио, в котором можно будет убивать/насиловать/грабить караваны. Что нужно было сделать:
Добавить погоду
Возможность телепортироваться
Портальную пушку
Гравитационную пушку
Вообще много разных пушек и холодного оружия
Возможность ездить на транспорте
Чтобы у транспорта было ограничено топливо и его можно было заправлять
Погоду
Смену дня и ночи
Нарко и алко трипы
Чтобы можно было строить как в Майнкрафт
Карму каждому игроку
и много других веселых и забавных вещей.
И что характерно — весь этот список уже реализован, и в данный момент
возникло желание добавить ещё много фишек и реализовать их.
В какой-то момент, я осознал, что не могу написать игру. Эта штука
становиться все больше и больше, но логической концовки ей — я не вижу.
Тогда было решено описать все, выложить в интернет и посмотреть кому это
интересно. Начав писать документацию — осознал, что многое работает не
так, как надо. Да и API можно и нужно упрощать. Но давайте вернемся к
сути и основным идеям.
Любая игра состоит из трех объектов.
Первый объект — это мир, в котором происходит действие. Мы можем
помещать и удалять объекты из мира. Мир в свою очередь следит за всеми
столкновениями, силами и событиями. Он также может оказывать
дополнительное воздействие на объекты (например, задавать гравитацию).
Второй объект — это персонаж или персонажи, которые находятся в этом
самом мире. И тут вовсе не обязательно, чтобы это был именно живой
объект. Это может быть коробка, бочка, стенка, пуля, какой либо камень,
либо что-то ещё.
В-третьих, камера. Камера нужна, чтобы смотреть на мир, т.к. предыдущие
два объекта являются исключительно мат. абстракцией и никак не дают о
себе знать.
Какие проблемы решает создание разных миров? Мы можем вынести код на
сервер и доделать режим игры по сети. По сути, мир — это массив и
парочка методов, которые его обслуживают. Он не работает с DOM, а
значит, ему безразлично, где существовать. Кроме того, генерируя миры,
мы можем создавать игры с параллельными мирами, которые существуют
одновременно.
Имея разные миры и карму, можно после обнуления жизней перекидывать
персонажей из основного мира, в мир «Рай» или мир «Ад» и создать
нелинейный сюжет. Кроме того, т.к. миры будут существовать паралельно,
то возможно варианты развития событий, при которых персонажи одного мира
телепортируются в другой мир и начинают там войну.
Архитектура, описанная выше — это ООП в чистом виде. Поэтому ядро движка
это прототип на прототипе, повсеместная инкапсуляция и модульность.
Если начать думать в таком формате, архитектура игр представляется
совсем с другой стороны и возникает куча дополнительных вопросов.
Расширяемость
Идет парень, подбирает автомат и садится в машину. Другой парень
стреляет в него из базуки. Кто от чего умрет? Как вообще они будут
умирать?
Ракета подлетает к машине, происходит столкновение. Рекурсией идет
перебор вложенных объектов (перебор пассажиров и вещей у этих
пассажиров) и все объекты внутри машины уничтожаются. Потом уничтожается
сама машина. Эта схема первой приходит в голову.
Ракета подлетает к машине, происходит столкновение. Создается зона
повышенной температуры/ущерба. Всем объектам наносится ущерб. Если
объект сильнее нанесенного ему ущерба — он остается в мире. Эта схема
кажется более логичной, да и возможностей к масштабированию больше
(например, можно расширить зону ущерба и получим взрывную волну).
Схема два, только для вложенных объектов учитывается степень
вложенности. Получается, чем более вложен объект, тем он больше защищен.
Схема два, только учитывается дополнительный урон от топлива в
машине. Получается ущерб от ракеты + ущерб от взрыва топлива в машине.
Такая схема более реалистична.
Что такое погода?
Погода — это то, что крутится на заднем плане. Влияет ли она на игрока?
Можно ли сказать что зона с ветром — это зона с гравитацией, которая
тянет не вниз, а в бок? Стоит ли нам задавать свойство парусности разным
объектам и домножать на них силу ветра? Как останавливать ветер
препятствиями на его пути (например, стенкой), если он гравитация — то
стенка его явно не остановит?
Правильный ответ на каждый из этих вопросов, может дать очень большую
выгоду в перспективе. Решение должно быть максимально простым и
универсальным, и тогда на его основе можно будет реализовать ещё
несколько решений или добавить дополнительный функционал, о котором
раньше вы даже не задумывались.
Например, оружие разбито на типы и по функционалу. Используя уже готовые
методы и функции, на основе режима строительства была добавлена функция
стрельбы патронами и снарядами. По сути, мы строим снаряд вместо
кирпича перед персонажем и задаем снаряду начальную скорость.
На основе того, что мы можем задать размер камеры (не размер экрана, на
который выводится изображение, а именно области отображения мира) очень
легко была реализована функция масштабирования. А ссылка для таймера на
отдельную переменную mdash; дала возможность добавить камере кнопку стоп/запустить для остановки отрисовки мира.
Проектируем объекты мира
Любой объект, который добавляется в мир, должен иметь обязательные
стандартные свойства. Например: координаты, размер, ID, класс и тип,
способ отрисовки. Это так называемые, стандартные свойства и они есть
абсолютно у всех объектов. Далее, в зависимости от класса и типа объекта
на него навешиваются дополнительные свойства и методы.
Например:
Фон ничего не получает, т.к. ему больше ничего и не надо. С фоном никто не взаимодействует.
Персонаж получает физические параметры (массу, ускорение, силы и
т.д.), рюкзак (пустой массив, куда в последствии возможно добавлять ID
вещей), характеристики живого объекта (тип мозгов, жизни, рейтинг, карму
и т.д.) и т.п.
Машина получает почти тот же комплект свойств и методов, что и
персонаж, но есть и особые характеристики транспорта (запас топлива,
расход, пассажировместимость и т.д.)
Чтобы создавать огромное число объектов с одинаковыми свойствами и
прототипами — внутри движка есть фабрика объектов. Каждый объект, в
зависимости от типа и имеет набор конфигов. Далее есть два массива —
массив свойств и массив прототипов (на самом деле это не массивы, а тоже
объекты, но это уже по части ООП в JavaScript).
Когда мы создаем объект, фабрика (согласно конфигу) наследует ему его
пакет свойств и пакет прототипов. Таким образом, мы имеем что-то типа
фабрики фабрик. Которая может собрать любой объект, задав ему любой
комплект свойств и прототипов.
Проектируем оружие
Так получилось, что оружие разделилось на три вида по типу стрельбы:
С зоной поражения.
В зависимости от дальности стрельбы, перед персонажем создается зона
поражения. Далее находятся все объекты, которые попали в зону поражения.
Из них выбирается объект, который ближе всего находится к персонажу.
Когда жертва найдена — ей наносится ущерб, в зависимости от типа оружия.
Подобный алгоритм использует кирка. Пример можно посмотреть тут
С созданием объекта снаряда.
Перед персонажем создается объект снаряда, у которого отключено
воздействие гравитации. Снаряд летит и проверяет наличие столкновений с
объектами. Если он столкнулся с персонажем — снаряд исчезает, а
персонажу наносится урон.
Подобный алгоритм использует пушка. Пример можно посмотреть тут
С проверкой свободного места.
Это особый вид стрельбы, необходим для режима строительства. При
стрельбе создается блок материала и ищется свободное место около
персонажа, куда можно его поставить.
Подобный алгоритм используется при строительстве. Пример можно посмотреть тут
Как сделать оружие ближнего боя
Тут все просто — любые мечи, ножи, кирки работают по первому принципу (с зоной поражения)
Как сделать скорострельное и медленное оружие
Скорострельное оружие работает по первому принципу и наносит урон сразу.
Медленное оружие (ракеты и т.п.) — по второму, и ждет столкновение
снаряда.
Как сделать гравитационную пушку
Стрельба делается по первому типу, только вместо урона на все объекты
накладывается дополнительное ускорение. Его направление зависит от
режима стрельбы (либо к себе, либо от себя).
Точно так же, как и строительство блоков — по третьему принципу. Отличие
только одно — пушка сохраняет ссылку на созданный портал, чтобы
привязать его ко второму созданному порталу.
Т.к. у нас возможно прокидывать порталы между двумя разными мирами,
можно создать пушку, которая бы прокидывала свою жертву в другой мир,
или на другой конец карты. Получится уже не совсем портальная пушка, а
скорее пушка телепорт
Как реализована камера
Какие проблемы решает объект камеры? Т.к. Canvas работает не везде
(перспектива прокачать играми электронные книги меня все ещё манит), я
решил делать камеру, которая сможет отрисовать мир с помощью верстки.
Наркомания скажете вы? Возможно, зато работает везде. В данный момент
камера может рисовать картинками и div`ами с background`ом. Перевести
все на canvas если заставит нужна — не проблема, т.к. для этого нужно
всего лишь переписать несколько функций рендеринга (на самом деле тут,
шутки ради, можно вообще сделать отрисовку текстом). Мой друг постоянно
говорил мне о том, что без canvas все будет виснуть, т.к. шейдеры… бла…
бла… бла… HTML5… и т.д. — но оказалось, что нет. В начале камера конечно
висла, но проблема каждый раз была не в ней, а в алгоритмах обсчета
мира.
Когда я показал схему работы одному коллеге на работе — он сказал, что
камера должна сразу отрисовать весь мир, а не создавать и удалять
постоянно DOM элементы. Это абсолютно неправильное понимание.
Предположим, что в мире 40 объектов, а значит, у нас есть 40 DOM
элементов для работы. Ну и ладно, это не много и браузер сможет их
вывести без тормозов. А если у нас в мире тысяча объектов? Следуя такой
логике, мы должны создать тысячу DOM элементов и страдать от тормозов
(хотя на экране, в данный момент, все равно будет лишь штук 30, т.к.
больше не влезет).
Камера — это объект мира. Она имеет размеры и координаты, и постоянно
ищет столкновение других объектов с собой. Все кто с ней сталкиваются —
попадают в массив видимых объектов и создаются на дисплее. Если объект
выходит за рамки камеры — его представление уничтожается.
Рендеринг мира осуществляется в данный момент двумя способами —
картинками и div`ами. Для обоих методов подойдет один дисплей в виде
родительского div`а, относительно которого позиционируется вся верстка. В
будущем можно добавить ещё рендеринг через canvas, но в данный момент
особой необходимости в этом не вижу, т.к. есть более интересные задачи.
Ещё один интересный момент. Поскольку вся отрисовка игры mdash; это
верстка, то любой верстальщик (даже с малым опытом работы) сможет легко
исправить CSS файл и заменить картинки, тем самым придать объектам
совершенно иной внешний вид. Например можно создать вязанный мир (для сравнения обычный).
Как выбрать между картинкой и div`ом?
Ну тут зависит от ситуации.
Если у вас большой экран и какой-нибудь очень длинный объект — то
следует отрисовать его div`ом с background`ом (например кирпичную
стенку). Она будет хорошо выглядеть и это будет всего лишь один DOM
элемент (чем меньше DOM элементов на дисплее, тем меньше тормозит
отрисовка).
Если у вас маленький экран или объект с анимацией — то следует
отрисовать его через img, т.к. через смену адреса картинки можно
воссоздать анимацию, а кроме того картинки при сжатии выглядят
адекватно.
Все элементы на экране задаются строго в процентах, поэтому экран
резиновый и натягивает куда угодно, в каком угодно виде. Соотношение
отображаемой области, пропорций экрана и т.д. можно задать при создании
объекта камеры (см. документацию).
Надо также понять, что камера это отдельный объект сам по себе, и он
никак не связан ни с одним персонажем в мире. Но у каждой камеры, есть
метод bind, который привязывает её к другому объекту в мире (не
обязательно к персонажу, можно и к кирпичу её привязать). Такой
интерфейс объекта позволяет легко реализовать переключение камеры между
разными игроками (аналог вы уже видели в Counter-Strike, когда после
смерти можно посмотреть бой от лица ещё живых игроков).
Какие выгоды нам дает объект камеры сам по себе?
Очевидно, что мы можем просто получить div в полный экран с
фиксированным соотношением сторон. Вовсе не обязательно использовать
камеру по основному назначению, этот объект может помочь нам в
восстановление старых игр (например, получать идеально квадратную доску
для нард, при любом соотношение сторон экрана)
Как использовать движок
Данный игровой движок можно использовать двумя способами:
Делать новую игру на его основе
Реставрировать старую игру используя общие модули
Для создания новой игры вам понадобятся четыре основных компонента:
Мир. В мире находятся все объекты, и развивается сюжет. Мир следит
за столкновениями и действиями объектов. Мир управляет все физикой.
Камера. Камера показывает вам мир или следит за персонажем. Именно благодаря камере, мы можем знать, что происходит в мире.
Персонаж. Персонаж, это один из объектов, который мы поместим в мир.
Так же мы добавим в мир его окружение, врагов, оружие и т.п.
Джойстик. Чтобы управлять персонажем, мы должны привязать к нему
джойстик. Если отдать джойстик компьютеру, то он сможет управлять
персонажем.
Т.к. все описанные выше компоненты мы получаем на так называемых
«Фабриках», то мы можем создавать их в неограниченном количестве.
Например, можно создать три мира, которые не будут никак пересекаться
между собой. Или пять камер нацеленных на один мир, чтобы смотреть
глазами разных персонажей.
Но есть и некоторые ограничения — например, объект может быть помещен
только в один мир, иначе его поведение будет неадекватным (он будет
реагировать на события в разных мирах).
Общая схема движка
Пояснение к схеме
Ядро — файл, который содержит типовые функции
(создать/удалить/заменить класс у DOM элемента, сгенерировать ID,
назначить событие и т.д.)
Фабрика миров — создает миры, где все происходит
Фабрика камер — создает камеры, которые показывают нам, что происходит в мирах
Фабрика объектов — создает персонажей/вещи/оружие/окружение, которое
добавляется в миры. Также фабрика может восстанавливать объекты, но об
этом ниже.
Конфигурации объектов — у фабрики объектов так много настроек, что
часть из них вынесена в отдельный файл. Почему вынесены не все
настройки? Потому что если поломать/изменить базовые настройки для всех
объектов, объекты могут стать не валидными и миры не будут их добавлять в
себя.
Далее все что создается на фабриках — попадает в единый реестр. Он
нужен для того, чтобы убрать ссылки между объектами. Если кто-то кому-то
нужен — он запрашивает жертву у реестра, указывая его ID. Так,
например, персонажи хранят в сумках только ID вещей, а сами вещи
приезжают из реестра по мере необходимости.
Далее идет группа модулей, которые работают с конкретными объектами фабрик. Так например для миров:
Погода — обрабатывает настройки погоды
Обработка карт — строит карту по заданным параметрам. Получить строку таких параметров вы можете в редакторе карт.
Для объектов в мирах (вещи/персонажи/препятствия):
Менеджер миссий — следит за персонажем и его миссиями. При успешном завершении миссии может делать какие-либо действия.
Рейтинг — составляет рейтинг игроков в мире.
Кланы — отвечает за все, что связанно с кланами. Может добалять игроку клан, выдавать список кланов и т.д.
Фабрика сумок — создает объект, способный работать с вещами персонажа.
Фабрика джойстиков — создает контроллеры для персонажей.
Мозг — управляет персонажами. Если вбросить туда персонажа — ему
будет создан джойстик, и в зависимости от его типа мозгов компьютер
будет управлять им.
Также есть модуль загрузки и сохранения. При сохранении он преобразует в
JSON массив объектов в мире, а при загрузке — восстанавливает все
объекты на фабрике и обновляет реестр.
Т.к. статью писал в течение нескольких дней — могут быть логические
нестыковки, так что извините, если что не так. Чуть больше можно узнать тут. В следующей статье будет больше практики, разбор API, а также ресурсы, необходимые для разработки игр.
Также рекомендую ознакомиться с трудами этих товарищей:
БЭМ: «Организация файловой системы» и «История создания» Выступление Вадима Макеева о CSS фреймворках Канал Андрея Короткова о том, как нормальные мужики пилят архитектуру Выступление Миши Давыдова в Челябинске о том как надо брать и масштабировать
Книгу Джоба Макара «Секреты разработки игр в macromedia Flash MX» Серию статей Миши Кадикова «Дизайн уровней. Теория и практика»
Зарег. на сайте Всего: 3672 Новых за месяц: 4 Новых за неделю: 1 Новых вчера: 0 Новых сегодня: 0 Из них Админинистраторов:1 Модераторов:0 Проверенных:0 Обычных юзеров:3671 Из них Парней:3192 Девушек:480
Здравствуйте уважаемые любители игростроя! Этот сайт создан специально для вас! Здесь есть всё что нужно для редактирования и созданию игр, в том числе и без программирования... На этом сайте вы найдёте очень много файлов статей для редактирования игор. Также на сайте есть очень много различных файлов к играм, программ, патчей, трейнеров и др., для таких известных игор как GTASA, NFS, CS1.6, Crysis и др. Также есть различные программы связаны с играми, для создания 3D-моделей, для извлечения ресурсов из разных игор. На сайте очень много картинок, скриншотов и обоев на рабочий стол из игор. Здесь вы найдёте всё необходимое для создания модификаций к вашим любимым играм, а так же создания собственных игр. Для игроделов на сайте можно скачать необходимые для этого инструменты (конструкторы игр, движки), изучить необходимую литературу (статьи, журналы, книги) и т.д. На нашем форуме вы всегда сможете спросить совет или поделиться своими наработками. Если у вас имеются интересные файлы, то вы так же можете разместить их на нашем сайте.Ващей фантизии нет границ? Тогда вперёд, покажите всем на, что вы способны! А наш сайт вам в этом поможет. Играйте, создавайте, модифицируйте, удивляйте людей! И может быть через какое то время весь мир узнает о Вас!