Unity3D: Создание трехмерной игры-платформера: Соперники

Соперники


Никакая игра не обходится без противников. В этой главе мы добавим врагов для Лерпза.

Антагонисты и конфликты

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

Лерпз сталкивается с двумя противниками: роботы-охранники и лазерные барьеры.

Лазерные ловушки

Лазерные ловушки располагаются в лазерных проходах, и будут вредить игроку, если он будет касаться луча. На изображении ниже показано два из них. Мы расположим их в коротких проходах-строениях по обе стороны арены, на дальнем конце уровня от «тюрьмы».

Лерпз сталкивается с двумя лазерными ловушками
Лерпз сталкивается с двумя лазерными ловушками.

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

Реализация лазерных ловушек

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

Луч лазера сам по себе создается при помощи компонента Line Renderer, содержащийся в своем собственном объекте GameObject. Его движение и логика, которые управляют им — полностью содержатся в скрипте LaserTrap. Поэтому давайте создадим первую лазерную ловушку.

  • Создайте пустой GameObject.
  • Переименуйте его в «Laser».
  • Добавьте компонент Line Renderer (Component­>Miscellaneous­>Line Renderer).
Примечание Пока что не пытайтесь позиционировать лазер! Сначала нужно будет отключить пункт «Use World Space», вскоре мы это сделаем.
  • Добавьте скрипт LaserTrap.
  • Отрегулируйте настройки компонента Line Renderer, как показано ниже.
Настройки Line Renderer
Настройки Line Renderer.
  • Переместите полученный объект в лазерные туннели (крытые
    строения-коридоры по обе стороны арены, на дальнем конце
    уровня от «тюрьмы»).
  • Добавьте игровой объект Point Light (точечный источник
    света) в качестве ребенка объекта Laser.
  • Установите объект Point Light, как показано ниже:
Настройки объекта Laser Trap Point Light
Настройки объекта Laser Trap Point Light.

Объект Point Light ведет себя как источник лазерного света, и будет подниматься и опускаться вместе с лазером. Это придает иллюзию того, что луч, рисуемый компонентом Line Renderer, излучает свет.

Компонент Line Renderer

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

  • Далее установите настройки скрипта LaserTrap, которые должны быть следующими:
Настройки скрипта LaserTrap
Настройки скрипта LaserTrap.
Подсказка Ресурс LaserHit можно найти в папке Props в окне Project.
  • И наконец, скопируйте (CMD+D на Mac, или Ctrl+D), (или создайте префаб) лазерной ловушки и поместите несколько в
    уровне, как считаете нужным. Предлагаю разместить четыре в
    общей сложности, с двумя в каждом туннеле.
  • Можете изменить свойства скрипта LaserTrap и поэкспериментировать пока не получите результат, которым будете довольны.

Скрипт Laser Trap

Ключ к лазерной ловушке — скрипт LaserTrap, поэтому давайте взглянем поближе…

Обзор

Лазерная ловушка это компонент Line Renderer, который двигается вверх и вниз при помощи скрипта. Этот же скрипт также проверяет столкновения и запускает необходимые визуальные эффекты.

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

  • height определяет амплитуду колебания, диктуя как высоко и низко начальной точки лазерный луч будет двигаться;
  • speed определяет, насколько быстро двигается луч;
  • timingOffset позволяет устанавливать для каждой лазерной ловушки начинать с другой точки в своем колебательном цикле;
  • laserWidth определяет ширину лазерного луча с одного конца до другого;
  • damage определяет, сколько повреждений получает игрок, если проходит через луч;
  • hitEffect, может использоваться для связи с произвольным игровым объектом, который будет создан, когда что-то соприкоснется с лазерной ловушкой. Это более гибкий метод, чем жестко прописывать эффект в объекте Laser Trap и облегчает повторное использование этого ресурса в других проектах.

Скрипт определяет две функции, поэтому давайте рассмотрим их:

Функция Start() инициализирует компонент Line Renderer, сохраняя его начальное местоположение (по оси Y) для последующего использования, и устанавливает вторую вершину компонента Line Renderer, чтобы она соответствовала позиции, определяемой параметром laserWidth. Это позволяет с легкостью настроить длину луча, чтобы она соответствовала ширине коридора.

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

Теперь переходим к главной функции Update(). Именно здесь происходит самое интересное.

Сначала вычисляется движение луча лазера. Мы просто используем для этого функцию Mathf.Sin(). Мы захватываем текущее время при помощи функции Time.time (которая возвращает время в секундах с момента начала игры), умножаем ее на нужную скорость анимации и добавляем значение в timingOffset. Это дает положение по кривой синуса. Наконец, модулируем это нужным параметром height (высотой) и используем результат в качестве смещения от базового уровня.

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

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

Если происходит соприкосновение с чем-то, то мы проверяем игрок это или враг, если так, то отправляем сообщение «выполнить повреждение». В то же самое время, мы также создаем игровой объект hitEffect так, чтобы он мог выполнить свою магию. Это игровой объект LaserHit, который производит эффект энергетического взрыва:

Находиться под лазером вредно для здоровья игрока
Находиться под лазером вредно для здоровья игрока.

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

Шок и трепет

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

Роботы-стражники

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

Робот-стражник
Робот-стражник.

Враги Лерпза в игре это роботы-стражники. Это конкретная модель сейчас считается «классической», достойна сохранения. Собиратели и энтузиасты сравнивают их, неблагоприятно, с такими другими классическими драгоценными камнями прошлых лет, как Ford Edsel, Apple Lisa и Sinclair C5.

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

Из вышеперечисленного мы определяем, что:

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

Найти и уничтожить

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

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

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

Threaten — если нарушитель подходит в пределы установленного диапазона стражника, то стражник выходит из режима ожидания и объявляет о своих намерениях, вращая своей дубинкой, как в брошюре продаж в «угрожающей манере».

Seek & Destroy — после активации роботы-охранники нацеливаются на нарушителя и пытаются атаковать его. Если робот-стражник попадает в пределы диапазона удара по нарушителю, то пытается ударить его дубинкой.

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

Struck — если игрок ударяет по роботу-охраннику, то воспроизводится эта анимация (“gothit” в модели и скрипте).

Если робот перемещается за пределы своего ограниченного диапазона сканирования, то он возвращается обратно в режим «Idle».

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

Добавление роботов-стражников

Нужно только несколько роботов на уровне, так что…

  • Откройте панель Project и найдите префаб CopperNew внутри папки Enemies.
  • Найдите подходящее место на уровне и поместите префаб на сцену, убедившись в том, что он располагается на поверхности (префаб включает в себя компонент Character Controller, убедитесь, что нижний конец капсулы-коллайдера немного касается поверхности, или находится чуть выше нее).
  • Добавьте еще несколько роботов на уровень точно также.
    Области с ограждениями или те, которые как-либо замкнуты, являются лучшими, поскольку будет менее вероятно, что робот упадет со сцены.

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

Имеется два скрипта для роботов, и мы добавим их напрямую к исходному префабу:

  • Выберите префаб CopperNew, чтобы перенести его в инспектор.
  • Перетащите скрипты EnemyDamage и EnemyPoliceGuy на компонент Character Controller в инспекторе.
  • Если вы еще этого не сделали, то перетащите скрипт ThirdPersonCharacterAttack на объект Player. Он обрабатывает для игрока завершение движения удара кулаком (без этого скрипта Лерпз не сможет ударять кулаком роботов!).

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

  • Нажмите на префаб CopperNew в панели Project, чтобы раскрыть его детали в инспекторе.
  • Перетащите игровой объект Player на целевую ссылку в слоте компонента (скрипта) робота Enemy Police Guy.

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

Губительные синие вспышки

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

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

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

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

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

Робот-стражник повержен
Робот-стражник повержен.

Изображение выше показывает этот замененный префаб в действии.

Скрипт, который проделывает эти действия — это EnemyDamage, поэтому давайте рассмотрим его поближе…

Когда посылается сообщение ApplyDamage игровому объекту, то вызывается функция ApplyDamage(). Если робот получил слишком много ударов, как в примере, то его хитпоинты устанавливаются на значение «3» — это вызывает функцию Die(), где и начинается действие.

Функция Die() сначала помечает игровой объект Copper на уничтожение (уничтожение не происходит, пока не завершится функция Update()).

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

Потом создается светящийся взрыв и устанавливается как дочерний объект нового игрового объекта робота.

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

Наконец, создаем до двух бонусов — каждый из которых будет иметь 50% шанс быть либо здоровьем, либо топливом — и запускаем их в воздух в случайном направлении.

Вы можете реализовать все вышеперечисленное выбрав игровой объект Copper и изменив значения в компоненте Enemy Damage. Мы рекомендуем попробовать различные варианты.

Падающие бонусы и физика

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

Скрипт необходим, так как коллайдер устанавливается в качестве спускового механизма для бонуса и, таким образом, игнорируется физическим движком. Бонус получает начальную скорость, а скрипт затем использует технологию «raycasting» для проверки соприкоснулся ли с чем-нибудь бонус. Когда это происходит, скрипт просто отключается. (Этот код имеет один существенный недостаток: он проверяет только сверху вниз по оси Y. Таким образом, падающие бонусы могут застревать в стене.)

Создание и оптимизация

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

Давайте начнем:

  • Создайте простой игровой объект поверх уровня панели в Hierarchy.
  • Переименуйте его в CopperSpawn.
  • Перетащите скрипт EnemyRespawn на объект (для него будет автоматически добавлен компонент коллайдер-триггер).
  • Поместите объект CopperSpawn там, где хотели бы чтобы появился робот (вы должны будете увидеть значок маленького робота, отображаемый как gizmo, а также сферу для отображения радиуса создания роботов-стражников. Все это рисуется скриптом EnemyRespawn).
  • Настройте параметры объекта, как показано ниже.
Установки CopperSpawn
Установки CopperSpawn.

Нам понадобятся несколько этих игровых объектов, поэтому давайте установим родительский игровой объект и сделаем объект CopperSpawn дочерним для него…

  • Создайте пустой игровой объект вверху уровня в панели Hierarchy.
  • Переименуйте его в новый объект Enemies.
  • Сделайте игровой объект CopperSpawn дочерним для Enemies, переместив первый объект во второй.
  • Теперь скопируем и вставим, чтобы дублировать объект CopperSpawn, убедившись в том, что находимся над объектом Enemies. Поместите их на уровне, как считаете нужным.
Примечание Одним из побочных эффектов этого метода в том, что, если вы уничтожите робота и затем выйдите за пределы диапазона, то он снова будет выглядеть как новый, если вы вернетесь на его местоположение.

Как это работает

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

Сценарий, который это делает — EnemyRespawn. Это скрипт слишком закомментирован, кроме двух функций:

Start() — просто кэширует ссылку на преобразование объекта игрока, так как нам нужно разобраться с этим позже.

Update() — сначала проверяет, находится ли игрок в диапазоне и создает робота, если это так. Если нет, то игрок только что вышел из диапазона, префаб робота уничтожается.

Имеется также две функции Gizmo в редакторе:

EnemyRespawn также использует свойство редактора Юнити «Gizmos». Gizmo, как правило, визуальная помощь, отображаемая только в окне Scene View, но не в игре, такие как, в данном случае, сфера, показывающая диапазон создания роботов. В этом скрипте есть два типа gizmo: первый рисует значок робота, который позволяет выбирать объект создания роботов, нажав на его значок на сцене кнопкой мыши:

Примечание Значок с изображением в настоящее время сохраняется в папке /Applications/Unity/Unity.app/Contents/Resources, хотя это может измениться в будущих версиях.
Функция OnDrawGizmos, отображающая значок робота в редакторе Unity3D
Функция OnDrawGizmos, отображающая значок робота в редакторе Unity3D.

Код Gizmo для значка показан ниже:

var 
function OnDrawGizmos ()
{
Gizmos.color = Color(1, 1, 1, 1);
Gizmos.DrawIcon(transform.position, type + ".psd"); 
}

Функция OnDrawGizmos() вызывается каждый раз, когда обновляется графический интерфейс редактора Unity3D, поэтому значок всегда будет виден. Для того, чтобы эта функция знала какое изображение значка использовать, нужно…

  • …установить свойство Gizmo Name в инспекторе на «Copper».

С другой стороны функция OnDrawGizmosSelected() вызывается графическим интерфейсом редактора Unity только когда выбирается объект. До тех пор, пока он выбран, будет происходить вызов, а GUI редактора Unity будет обновляться.

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

var 
function OnDrawGizmosSelected ()
{
Gizmos.color = Color(0, 1, 1);
Gizmos.DrawWireSphere(transform.position, spawnRange);
}
Функция OnDrawGizmosSelected(), отображающая сферу, которая определена переменной Spawn Range
Функция OnDrawGizmosSelected(), отображающая сферу, которая определена переменной Spawn Range.

Альтернативные оптимизации

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

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

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

Комментариев: 5 на “Unity3D: Создание трехмерной игры-платформера: Соперники

  1. Такой вопрос не связанный с уроком.
    Что сейчас актуально учить, если flash доживает свое.
    HTML5 или что-то другое?

    1. Здравствуйте! Все зависит от поставленных целей — какое программное обеспечение писать (игры, приложения), для чего, для кого и для какой платформы. Действительно, многие разработчики уже переходят с AS3 на HTML5, но это скорее не потому что первый хуже, чем второй, возможно даже наоборот. Все потому что браузеры и мобильные ОС не хотят использовать технологию AdobeFlash.

        1. Если планируете разрабатывать игры для браузеров и мобильных платформ, то лучше использовать js-технологии.

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

Комментировать

Почта не публикуется.Обязательные поля отмечены *