Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Xcode

Введение в GameplayKit: часть 2

by
Difficulty:IntermediateLength:LongLanguages:
This post is part of a series called An Introduction to GameplayKit.
An Introduction to GameplayKit: Part 1
An Introduction to GameplayKit: Part 3

Russian (Pусский) translation by Alex Grigorovich (you can also view the original English article)

Это вторая часть Введения в GameplayKit. Если вы еще не прошли первую часть, я рекомендую сначала изучить его, прежде чем переходить сразу к шагу 2.

Введение

В этом уроке я расскажу вам еще о двух функциях системы GameplayKit, которыми вы можете воспользоваться:

  • агенты, цели и поведение
  • поиск пути

Используя агенты, цели и поведение, мы собираемся построить некоторый базовый искусственный интеллект (AI) в игру, который мы начали использовать в первой части уроков. ИИ позволит нацелиться нашим красным и желтым кружочкам противника и двигаться к точке голубого игрока. Мы также собираемся внедрить функцию поиска пути в ИИ для навигации вокруг препятствий.

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

1. Агенты, цели и поведение

В GameplayKit агенты, цели и поведение используются в сочетании друг с другом, чтобы определить, как разные объекты перемещаются по отношению друг к другу во всему полю. Для отдельного объекта (или SKShapeNode в нашей игре) вы начинаете с создания  agent, представленного классом GKAgent. Однако для 2D-игр, подобных нашей, нам нужно использовать конкретный класс GKAgent2D.

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

Агенты представляют собой положение, размер и скорость объекта.  Затем вы добавляете к этому агенту поведение, представленное классом GKBehaviour. Наконец, вы создаете набор целей, представленных классом GKGoal, и добавляете их в объект поведения. Цели могут использоваться для создания множества различных элементов геймплея, например:

  • переход к агенту 
  • переход от агента
  • группировка близко к другим агентам
  • мигрирование по определенной позиции

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

Откройте проект Xcode и перейдите к PlayerNode.swift. Сначала нам нужно убедиться, что класс PlayerNode соответствует протоколу GKAgentDelegate.

Затем добавьте следующий код в класс PlayerNode.

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

Метод agentWillUpdate (_ :) вызывается непосредственно перед тем, как GameplayKit просматривает поведение и цели этого агента, чтобы определить, куда он должен двигаться. Аналогично, метод agentDidUpdate(_ :) вызывается сразу после завершения GameplayKit этого процесса.

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

Затем откройте ContactNode.swift и замените содержимое файла следующим:

Внедряя протокол GKAgentDelegate в класс ContactNode, мы позволяем всем остальным точкам в нашей игре быть в соответствии с GameplayKit, а также нашей игровой точкой.

Настало время настроить поведение и цели.  Чтобы сделать это, нам нужно позаботиться о трех вещах:

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

Во-первых, откройте GameScene.swift и, в конце метода didMoveToView(_ :), добавьте следующие две строки кода:

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

Затем замените реализацию метода initialSpawn следующей реализацией:

Самый важный код, который мы добавили, находится в состоянии if, который следует за состоянием switch. Давайте рассмотрим этот код строку за строкой:

  • Сначала мы добавляем агента к объекту в качестве компонента и настраиваем его полномочия.
  • Затем мы назначаем позицию агента и добавляем агент к сохраненному массиву, agents. Мы добавим это свойство в класс GameScene за минуту.
  • Затем мы создаем объект GKBehavior с помощью одиночного GKGoal для того, чтобы направить агента текущего игрока. Параметр weight в этом инициализаторе используется для определения того, какие цели должны иметь приоритет над другими. Например, представьте, что у вас есть цель - конкретный агент и другая цель отойти от агента, но вы хотите, чтобы цель на агента была предпочтительной. В этом случае вы можете задать вес этой цели 1 и вес 0,5 для другой цели.  Затем это поведение прикрепляется к узлу вражеского агента.
  • Наконец, мы настраиваем свойства mass, maxSpeed и maxAcceleration для агента. Они влияют на то, как быстро объекты могут перемещаться и поворачиваться. Не стесняйтесь играть с этими свойствами и вы заметите, как это влияет на передвижение вражеских точек.

Затем добавьте следующие два свойства в класс GameScene:

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

Наконец, замените реализацию метода update(_ :) класса GameScene следующим:

В методе update(_ :) мы вычисляем время, прошедшее с момента последнего обновления сцены, и затем обновляем агенты с этим значением.

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

Targeting enemies

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

2. Поиск пути

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

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

  • Непрерывное пространство, содержащее препятствия: Эта модель графика позволяет плавно перемещаться по препятствиям из одного места в другое.  Для этой модели графа используется класс GKObstacleGraph, класс GKPolygonObstacleдля препятствий и класс GKGraphNode2D для узлов (местоположений).
  • Простая двумерная сетка: в этом случае допустимыми местоположениями могут быть только те, у которых есть координаты, выраженные целым числом. Эта графическая модель полезна, когда ваша сцена имеет четкую структуру сетки, и вам не нужны прямые пути. При использовании этой модели объекты могут перемещаться только горизонтально или вертикально в одном направлении в любой момент времени. Для этой модели класс GKGridGraph используется для графа и класс GKGridGraphNode для узлов.
  • Коллекция мест и связей между ними: Это наиболее общая графическая модель и рекомендуется для случаев, когда объекты перемещаются между различными пространствами, но их конкретное местоположение в этом пространстве не является существенным для игрового процесса. Для этой модели класс GKGraph используется для графа и класса GKGraphNode для узлов.

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

Массив spawnPoints изменит некоторые точки появления в этом уроке. Это связано с тем, что в настоящее время GameplayKit может только вычислять пути между объектами, которые относительно близки друг к другу.

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

Затем добавьте следующие две строки кода в начале метода didMoveToView(_ :):

В первой строке мы создаем массив препятствий от физических тел в сцене. Затем мы создаем объект графа, используя эти препятствия. Параметр bufferRadius в этом инициализаторе может использоваться, чтобы заставить объекты не находиться на определенном расстоянии от этих препятствий. Эти строки должны быть добавлены в начале метода didMoveToView(_ :), потому что создаваемый граф необходим к моменту вызова метода initialSpawn.

Наконец, замените метод initialSpawn следующей реализацией:

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

Большая часть метода initialSpawn остается неизменной. Я добавил несколько комментариев, чтобы показать вам, что поиск пути часть кода находится в первом операторе if. Давайте пройдем через этот код шаг за шагом:

  • Мы создаем другой экземпляр GKGraphNode2D и подключаем его к графику.
  • Мы создаем серию узлов, которые составляют путь, вызывая метод findPathFromNode(_:toNode:) на нашем графике.
  • Если серия узлов пути была успешно создана, мы создаем путь из них. Параметр radius работает аналогично параметру bufferRadius от before и определяет, насколько объект может отойти от созданного пути.
  • Мы создаем два объекта GKGoal: один для перехода по пути и другой для нахождения на пути. Параметр maxPredictionTime позволяет рассчитывать, каким наилучшим образом он может опережать время, будет ли что-то прерывать объект, следуя или оставаясь на этом конкретном пути.
  • Наконец, мы создаем новое поведение с этими двумя целями и назначаем их агенту.

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

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

Pathfinding enemies

Важно!

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

Для реальной работающей  игры было бы лучше реализовать эту функциональность, объединив фукнционал игрока с функцией обхода препятствий, которая была описана раьше в нашем уроке, созданной с помощью метода init(toAvoidObstacles: maxPredictionTime:), о котором вы можете прочитать больше в Справочник класса GKGoal.

Вывод

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

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

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

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

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.