Как добавить физику объекту в unity
Перейти к содержимому

Как добавить физику объекту в unity

  • автор:

Physics

Unity helps you simulate physics in your Project to ensure that the objects correctly accelerate and respond to collisions A collision occurs when the physics engine detects that the colliders of two GameObjects make contact or overlap, when at least one has a Rigidbody component and is in motion. More info
See in Glossary , gravity, and various other forces. Unity provides different physics engine implementations which you can use according to your Project needs: 3D, 2D, object-oriented, or data-oriented. This page provides the links to their documentation.

You can achieve some basic physics goals with the user interface, but for more control over the simulation, you need some familiarity with C#. To develop your C# skills, see the Unity Learn Junior Programmer course.

Built-in physics engines for object-oriented projects

If your project is object-oriented, use the Unity’s built-in physics engine that corresponds to your needs:

  • Built-in 3D physics (Nvidia PhysX engine integration)
  • Built-in 2D physics (Box2D engine integration)

Physics engine packages for data-oriented projects

If your project uses Unity’s Data-Oriented Technology Stack (DOTS), you need to install a dedicated DOTS physics package. The available packages are:

Введение в физику в Unity

Физикой в ​​Unity занимается встроенный Physics Engine. Встроенный движок Physics Engine в Unity обрабатывает физику взаимодействия игровых объектов и различных эффектов, таких как гравитация, ускорение, столкновения и т. Д.

Коллайдер

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

Триггер

При включении Is Trigger превращает коллайдер в триггерный том, который позволяет другим игровым объектам проходить через него, и вместо записи столкновения он отправляет сообщение OnTriggerEnter, OnTriggerStay или OnTriggerExit, когда Rigidbody входит, остается или выходит из триггерного объема.

Материал

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

Жесткое тело

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

Масса

Масса твердого тела игрового объекта, измеренная в килограммах с помощью Physics Engine. Как и в реальной жизни, игровой объект с большей массой больше реагирует на объект с меньшей массой.

Тащить, тянуть

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

Угловое перетаскивание

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

Используйте гравитацию

При включении игровой объект будет притягиваться силой тяжести, как определено в настройках Unity. Настройки по умолчанию — -9,81 по оси Y, и их можно изменить в настройках проекта в разделе «Физика».

Кинематический

Is Kinematic контролирует, влияет ли физика на игровой объект. Когда игровой объект выключен, на него влияет физика, а когда он включен, игровой объект может быть перемещен только путем изменения преобразования с помощью кода или анимации. Если игровой объект должен быть статическим большую часть времени, но будет перемещаться в анимации или сценарии, обязательно добавьте Rigidbody и включите Is Kinematic, чтобы движок знал, что объект может перемещаться, и не тратить ресурсы при перемещении. .

Интерполировать

Поскольку физика в Unity обрабатывается в FixedUpdate, который запускается только каждые 0,02 секунды или 50 раз в секунду, Interpolate может помочь исправить любое дрожание движения, с которым вы можете столкнуться, вызванное фиксированной частотой кадров. Интерполяция может быть очень ресурсоемкой, поэтому, если вы не видите проблем, ее следует оставить по умолчанию. Три параметра интерполяции: «Нет», «Интерполировать» и «Экстраполировать».

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

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

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

Обнаружение столкновений

Это режим обнаружения столкновений Rigidbody, который имеет 4 настройки: дискретный, непрерывный, непрерывный динамический и непрерывный спекулятивный.

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

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

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

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

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

Ограничения

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

Работа с физикой Unity3D

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

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

В этом уроке мы будем использовать физический движок в Unity для создания трехмерной игры, похожей на BoomBlox и Angry Birds. Мы научимся придавать объектам разные физические свойства, делать их способными к столкновению и даже позволять им разрушаться, если столкновения достаточно сильны.

Финальная игра

Что такое физический движок?

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

Настройка проекта

  • Нажмите Файл> Новый проект
  • Перейдите в подходящую папку и назовите проект CannonBowl
  • Нажмите Создать
  • Нажмите GameObject> Создать другой> Направленный свет
  • Нажмите Файл> Сохранить сцену
  • Назовите сцену Главная
  • Нажмите Сохранить

Коллайдеры

Коллайдеры – это то, что физические движки используют для обнаружения попаданий. В отличие от объектов сетки, они знают, когда они вступили в контакт друг с другом. Это простые формы, такие как коробки, сферы или капсулы, которые назначаются вашим GameObjects и следуют за ними. Вы можете думать о них как о чем-то вроде «силового поля».

Удобно, когда бы ни создавался GameObject, ему автоматически назначается соответствующий коллайдер. Куб получает BoxCollider, Сфера получает SphereCollider, Цилиндр получает CapsuleCollider и так далее.

Коллайдеры

В конечном итоге нам понадобится несколько блоков, чтобы сбить:

  • Нажмите GameObject> Создать другое> Куб
  • Переименовать куб в WoodBlock
  • Установите позицию блока в 0, 0, 0. Это будет центрировать блок в мире
  • Скачать Wood.jpg
  • Перетащите Wood.jpg на панель Project, чтобы сделать его текстурой.
  • Перетащите текстуру дерева на блок в окне сцены, это автоматически создаст для нас материал дерева и примените его к блоку.
  • Перетащите блок с панели «Иерархия» на панель «Проект», чтобы превратить его в сборный.

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

Rigidbodies

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

  • Выберите блок «Заблокировать» на панели «Проект».
  • Нажмите Компонент> Физика> Жесткое тело

Компонент твердого тела

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

Построить структуру

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

  • Нажмите GameObject> Создать другое> Плоскость
  • Переименовать самолет в землю
  • Установите позицию Земли на 0, 0, 0
  • Скачать Grass.jpg
  • Перетащите Grass.jpg на панель «Проект», чтобы сделать ее текстурой.
  • Перетащите текстуру травы на землю в режиме просмотра сцены

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

Сила тяжести

Теперь нам нужна структура, чтобы сбить с ног. Выберите блок и нажмите Ctrl + D в Windows или Cmd + D в OSX, чтобы дублировать блок несколько раз. Используйте инструменты масштабирования и перемещения, чтобы растянуть и расположить блоки примерно в той же конфигурации, что и на рисунке ниже.

Структура

ПРИМЕЧАНИЕ. Рекомендуется использовать точные числа для ваших преобразований. Блоки должны опираться друг на друга, но не перекрываться. Перекрытия приведут к тому, что физический движок выйдет из строя и сделает непредсказуемые вещи.

Управление камерой

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

  • Нажмите Активы> Создать> C # Script
  • Переименуйте сценарий в Cannon (потому что в конечном итоге наша камера будет снимать)
  • Перетащите сценарий на основную камеру
  • Дважды щелкните скрипт, чтобы отредактировать его

Следующий скрипт заставит камеру вращаться вокруг центра мира, а также наклонится вверх и вниз:

Основы физики 2D платформера, часть 1

Хорошо, задача описывается так: мы хотим создать 2D платформер c простой, устойчивой, отзывчивой, точной и предсказуемой физикой. Мы не хотим использовать тяжеловесный 2D физический движок в этой задаче, по нескольким причинам:

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

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

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

Ограничения персонажа

Начнем с определения типа форм которые будут использоваться в нашей физике. Одна из наиболее базовых форм которую мы можем использовать для представления физического объекта в игре это Axis Aligned Bounding Box (AABB). По сути AABB это не повернутый прямоугольник.

Example of an AABBExample of an AABB Example of an AABB

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

Без дополнительных объяснений, создадим структуру нашего AABB.

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

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

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

На картинке показана эта проверка на оси x; ось y проверяется аналогично

Demonstrating a check on the X-AxisDemonstrating a check on the X-Axis Demonstrating a check on the X-Axis

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

Перемещение объекта

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

Теперь заполним этот класс данными. Нам потребуется достаточно много информации для данного объекта:

  • Положение и положеиние на предыдущем кадре
  • скорость и скорость на предыдущем кадре
  • масштаб
  • AABB и его смещение (так мы можем выровнять его по спрайту)
  • объект находится на земле и был ли он на земле последний кадр
  • объект находится близко к стене слева, и находился ли близко к ней последний кадр
  • объект находится близко к стене справа, и находился ли близко к ней последний кадр
  • объект находится на потолке и находился ли на потолке последний кадр

Положение, скорость и масштаб двумерных векторов.

Теперь, давайте добавим AABB и смещение. Смещение нужно для того чтобы мы могли свободно установить AABB на спрайта объекта.

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

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

Первое что мы хотим сделать это сохранить данные предыдущего кадра в соответствующие переменные

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

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

После этого, нам также нужно обновить положение центра AABB, как это соответствует новому положению.

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

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

Управление персонажем

Данные

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

Во-первых, создадим класс Character, и отделим его от класса MovingObject.

Тут нам необходимо кое-что разобрать. Прежде всего, это нажатия клавиш — зададим их перечислением которое охватит все управление для персонажа. Создадим его в другом файле, и назовем его KeyInput.

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

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

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

Тут нет ничего особенного — мы хотим видеть что клавиша была нажата, отпущена, или она включена или выключена.

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

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

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

Цикл обновления

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

Персонаж стоит на месте

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

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

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

Если клавиши GoLeft или GoRight нажаты, тогда нам нужно изменить наше состояние на ходьбу.

В случае нажатия клавиши Jump, мы хотим установить вертикальную скорость на скорость прыжка и изменить состояние на прыжок.

Это происходит именно для этого состояния, по крайней мере сейчас.

Состояние ходьбы

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

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

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

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

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

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

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

Это для ходьбы. Перейдем к состоянию прыжка.

Состояние прыжка

Начнем с настройки соответствующей анимации для спрайта.

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

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

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

В конце, мы собираемся сделать прыжок выше, если кнопка прыжка нажата долшье. Сделав это, мы сделаем прыжок ниже, если кнопка прыжка не зажата.

Как вы можете видеть, если кнопка прыжка не зажата и вертикальная скорость положительна, тогда мы можем установить скорость на максимальное значение cMinJumpSpeed (200 пикселей в секунду). Это значит что если мы выполним кратковременное нажатие кнопки прыжка, вместо принятой скорости mJumpSpeed (410 по умолчанию), мы получим сниженную до 200, и следовательно прыжок будет короче.

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

Обновление предыдущих нажатий клавиш.

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

В самом конце функции CharacterUpdate, нам нужно будет сделать пару вещей. Во-первых это обновить физику.

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

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

В конце, обновим предыдущие данные нажатий клавиш.

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

Инициализация персонажа

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

  • задать масштаб
  • задать скорость прыжка
  • задать скорость ходьбы
  • задать начальное положение
  • установить AABB

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

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

Для AABB нам нужно установить смещение и половинный размер. Для демонстрации смещение спрайта должно быть просто половинным размером.

Сейчас мы можем заняться остальными переменными.

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

Дополнительно, при обновлении менеджера, нам нужно обновить игрока, и его вводимые данные.

Заметьте, что мы обновляем физику персонажа в функции FixedUpdate(). Это дает нам возможность убедиться что прыжки всегда будут на одинаковую высоту, независимо от того, с какой частотой кадров игра работает. Тут отличная статья Гленна Фидлера о том как исправить шаг времени, в случае если вы не используете Unity.

Тестирование управления персонажем

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

Выводы

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

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *