Создание системы бонусов в Unity +12


image

На что бы была похожа игра Sonic The Hedgehog без золотых колец и скоростных ботинок, Super Mario без грибов или Pac-Man без мигающих точек? Все эти игры стали бы намного скучнее!

Бонусы (power-ups) — это важнейший компонент игрового процесса, потому что они добавляют новые уровни комплексности и стратегии, побуждая игрока к действию.

В этом туториале вы научитесь следующему:

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

Примечание: в этом туториале подразумевается, что вы знакомы с Unity и обладаете хотя бы средними знаниями C#. Если вам нужно освежить свои знания, то изучите другие наши туториалы по Unity.

Для повторения действий туториала вам потребуется Unity 2017.1 или более новой версии, поэтому обновите свою версию Unity, если ещё этого не сделали.

GIF на 4МБ
image

Приступаем к работе


Игра, над которой мы будем работать — это двухмерная аркада с видом сверху, где игрок пытается увернуться от врагов; она немного похожа на Geometry Wars, но без стрельбы (и без коммерческого успеха). Наш герой в шлеме должен уворачиваться от врагов, чтобы добраться до выхода; при столкновении с врагами его здоровье уменьшается. Когда здоровье заканчивается, наступает game over.

Скачайте заготовку проекта для этого туториала и извлеките её в нужную папку. Откройте проект в Unity и изучите папки проекта:


  • Audio: содержит файлы звуковых эффектов игры.
  • Materials: материалы игры.
  • Prefabs: содержит префабы (Prefabs) игры, в том числе игровое поле, игрока, врагов, частицы и бонусы.
  • Scenes: здесь находится основная сцена игры.
  • Scripts: содержит скрипты игры на C# с подробными комментариями. Можете исследовать эти скрипты, если хотите лучше освоиться в них перед началом работы.
  • Textures: исходные изображения, используемые для игры и экрана заставки.

Откройте сцену под названием main и нажмите на кнопку Play.


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

Цикл жизни бонуса


У бонуса есть цикл жизни, состоящий из нескольких отдельных состояний:


  • Первая стадия — это создание, которое выполняется в игровом процессе или на этапе разработки, когда вы вручную располагаете GameObject бонусов в сцене.
  • Далее идёт режим привлечения внимания, когда бонусы могут становиться анимированными или иным способом привлекать внимание игрока.
  • Стадия сбора — это действие по подбиранию бонуса, которое вызывает срабатывание звуков, систем частиц или других спецэффектов.
  • Подбор бонуса ведёт к выполнению полезной нагрузки, при которой бонус «делает своё дело». Полезной нагрузкой может быть всё, что угодно, от скромной прибавки здоровья до наделения игрока какими-то потрясающими сверхспособностями. Этап полезной нагрузки также приводит к срабатыванию проверки срока действия. Можно настроить бонус так, чтобы он переставал действовать после определённого времени, после того, как игрока заденет враг, после нескольких применений или после выполнения любого другого условия игрового процесса.
  • Проверка срока действия приводит к стадии завершения. Стадия завершения уничтожает бонус и становится концом цикла.

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

Создание простого бонуса-звёздочки



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

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

Создайте новый Sprite, назовите его PowerUpStar и расположите прямо над героем в точке (X:6, Y:-1.3). Чтобы сцена была упорядоченной, сделайте спрайт дочерним элементом пустого GameObject PowerUps в сцене:


Теперь зададим внешний вид спрайта. Введите для Transform Scale значения (X:0.7, Y:0.7), в компоненте Sprite Renderer назначьте слоту Sprite звезду, а для Color выберите бледно-коричневый цвет (R:211, G:221, B:200).


Добавьте компонент Box Collider 2D, поставьте флажок Is Trigger и измените Size на (X:0.2, Y:0.2):


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


Отделяем игровую логику от иерархии классов


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


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

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

Проверочный список кодирования бонусов


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

  1. Реализовать PowerUpPayload, чтобы запустить полезную нагрузку.
  2. Опционально: реализовать PowerUpHasExpired, чтобы убрать полезную нагрузку из предыдущего этапа.
  3. Вызвать PowerUpHasExpired при истечении срока действия бонуса. Если срок действия истекает сразу же, поставьте в инспекторе флажок ExpiresImmediately, потому что в этом случае нет необходимости вызывать PowerUpHasExpired.

Давайте подумаем о том, что делает бонус-звезда: он просто даёт небольшую прибавку к здоровью. Чтобы это сделать, достаточно небольшого скрипта.

Создаём первый скрипт для бонуса


Добавьте новый скрипт к GameObject PowerUpStar, назовите его PowerUpStar и откройте в редакторе.

GIF

Добавьте следующий код, замените бОльшую часть начального boilerplate-кода Unity, оставив только конструкции using в начале.

class PowerUpStar : PowerUp
{
    public int healthBonus = 20;

    protected override void PowerUpPayload()  // Пункт 1 контрольного списка
    {
        base.PowerUpPayload();

        // Полезная нагрузка заключается в добавлении здоровья
        playerBrain.SetHealthAdjustment(healthBonus);      
    }
}

Код довольно короткий, но его достаточно, чтобы реализовать логику звезды! Скрипт соответствует всем пунктам контрольного списка:

  1. PowerUpPayload даёт игроку немного здоровья,
    вызывая playerBrain.SetHealthAdjustment. Родительский класс PowerUp уже позаботился о получении ссылки на playerBrain. То, что у нас есть родительский класс, означает, что нам придётся вызывать base.PowerUpPayload, чтобы обеспечить выполнение всей базовой логики перед нашим кодом.
  2. Нам не нужно реализовывать PowerUpHasExpired, потому что прибавление здоровья не отменяется.
  3. Срок действия этого бонуса завершается сразу же, поэтому нам снова не нужно ничего писать; достаточно поставить флажок ExpiresImmediately в инспекторе. Настало подходящее время, чтобы сохранить метод и вернуться в Unity для внесения изменений в инспекторе.

Создание первого бонуса в сцене


После сохранения и возврата в Unity GameObject StarPowerUp будет выглядеть в инспекторе следующим образом:


Введите значения инспектора следующим образом:

  • Power Up Name: Star
  • Explanation: Recovered some health…
  • Power Up Quote: (I will become more powerful than you can possibly imagine)
  • Expires Immediately: флажок поставлен
  • Special Effect: перетащите префаб из папки проекта Prefabs/Power Ups/ParticlesCollected
  • Sound Effect: перетащите аудиоклип из папки проекта Audio/power_up_collect_01
  • Health Bonus: 40

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


Завершив с параметрами PowerUpStar, перетащите его в папку дерева проекта Prefabs/Power Ups, чтобы создать префаб.

GIF

Используйте новый префаб, чтобы добавить несколько звёзд в правой части сцены.

Запустите сцену и проведите героя к первому бонусу. Подбор бонуса сопроводят замечательные звуковые эффекты и частицы. Отлично!


Связь на основе сообщений


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

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

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

Мы можем сделать GameObjects на передающими сообщения, получающими сообщения, или и тем, и другим:


В левой части представленной выше схемы представлены передатчики сообщений. Можно считать их объектами, «кричащими», когда происходит что-то интересное. Например, если игрок передаёт сообщение «Меня задели». В правой части схемы показаны слушатели сообщений. Как понятно из названия, они слушают сообщения. Слушатели не обязаны слушать все сообщения; они слушают только те сообщения, на которые хотят реагировать.

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

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


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

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

Этапы создания связи на основе сообщений


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

  • Передатчики определяют сообщение, которое они хотят передать, как интерфейс C#.
  • Затем передатчики передают сообщения слушателям, хранящимся в списке слушателей.

См. это видео про интерфейсы C#, если вам нужно освежить свои знания.

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

Более чётко это можно увидеть на примере. Посмотрите на код в файле IPlayerEvents.cs:

public interface IPlayerEvents : IEventSystemHandler
{
   void OnPlayerHurt(int newHealth); 
   void OnPlayerReachedExit(GameObject exit); 
}

У этого интерфейса C# есть методы для OnPlayerHurt и OnPlayerReachedExit. Это сообщения, которые может отправить игрок. Теперь посмотрите на метод SendPlayerHurtMessages в файле PlayerBrain.cs. Строки, помеченные числами в следующем фрагменте кода, описаны ниже:

   private void SendPlayerHurtMessages()
   {
      // Отправка сообщения всем слушателям
      foreach (GameObject go in EventSystemListeners.main.listeners)  // 1
      {
         ExecuteEvents.Execute<IPlayerEvents>                   // 2
             (go, null,                                               // 3
              (x, y) => x.OnPlayerHurt(playerHitPoints)            // 4
             );
      }
   }

Представленный выше метод обрабатывает отправку сообщения OnPlayerHurt. Цикл foreach обходит всех слушателей, хранящихся в списке EventSystemListeners.main.listeners и вызывает для каждого слушателя ExecuteEvents.Execute, который отправляет сообщения.

Пройдёмся по комментариям с числами:

  1. EventSystemListeners.main.listeners — это список GameObjects, глобально видимый в объекте синглтона EventSystemListeners. Любой GameObject, который хочет слушать все сообщения, должен находиться в этом списке. Добавлять GameObjects в этот список можно, присваивая GameObject метку Listener в инспекторе или вызывая EventSystemListeners.main.AddListener.
  2. ExecuteEvents.Execute — это предоставляемый Unity метод, отправляющий сообщение GameObject. Тип в угловых скобках — это имя интерфейса, содержащего сообщение, которое мы хотим отправить.
  3. Здесь определяется GameObject, которому нужно отправить сообщение и null для дополнительной информации события в соответствии с примером синтаксиса из руководства Unity.
  4. Лямбда-выражение. Это сложная концепция C#, которую мы не будем рассматривать в этом туториале. Если вкратце, то лямбда-выражение позволяет передавать методу код как параметр. В нашем случае код содержит сообщение, которое мы хотим отправить (OnPlayerHurt) вместе с необходимыми ему параметрами (playerHitPoints).

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


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

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

Бонус увеличения скорости



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

Мы создадим бонус, дающий игроку дополнительную скорость до момента, пока он с чем-нибудь не столкнётся. Бонус будет распознавать столкновение игрока «прослушивая» игровой процесс. А конкретнее, бонус будет слушать игрока, передающего сообщение «I am hurt».

Чтобы слушать сообщение, нам нужно выполнить следующие шаги:

  1. Реализовать соответствующий интерфейс C#, чтобы указать, что должен слушать слушающий GameObject.
  2. Сделать так, чтобы сам слушающий GameObjects находился в списке EventSystemListeners.main.listeners.

Создайте новый Sprite, назовите его PowerUpSpeed и расположите где-нибудь в левом верхнем углу арены над игроком. Задайте для его Scale значения (X:0.6, Y:0.6). Этот GameObject будет слушателем, поэтому укажите ему в инспекторе метку Listener.

Добавьте Box Collider 2D и измените его Size на (X:0.2, Y:0.2). В компоненте Sprite Renderer назначьте для Sprite значение fast и измените его цвет так, как мы делали со звездой. Не забудьте также поставить флажок Is Trigger. После этого GameObject должен выглядеть примерно так:


Добавьте этому GameObject новый скрипт PowerUpSpeed и вставьте в скрипт следующий код:


class PowerUpSpeed : PowerUp 
{
    [Range(1.0f, 4.0f)]
    public float speedMultiplier = 2.0f;

    protected override void PowerUpPayload()          // Пункт 1 контрольного списка
    {
        base.PowerUpPayload();
        playerBrain.SetSpeedBoostOn(speedMultiplier);
    }

    protected override void PowerUpHasExpired()       // Пункт 2 контрольного списка
    {
        playerBrain.SetSpeedBoostOff();
        base.PowerUpHasExpired();
    }
}

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

  1. PowerUpPayload. Вызывает метод base, чтобы обеспечить вызов родительского класса, затем придаёт игроку ускорение. Заметьте, что родительский класс определяет playerBrain, в котором содержится ссылка на игрока, собравшего бонус.
  2. PowerUpHasExpired. Нам нужно убрать данное игроку ускорение, а затем вызвать метод base.
  3. Последний пункт контрольного списка — вызов PowerUpHasExpired после завершения срока действия бонуса. Позже мы реализуем это с помощью прислушивания к сообщениям игрока.

Изменим объявление класса, чтобы реализовать интерфейс для сообщений игрока:

class PowerUpSpeed : PowerUp, IPlayerEvents

Примечание: если вы работаете в Visual Studio, то можете навести мышь на элемент IPlayerEvents после его ввода и выбрать опцию меню Implement interface explicitly. При этом создастся заготовка метода.


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


    void IPlayerEvents.OnPlayerHurt(int newHealth)
    {
        // Мы хотим реагировать только при подборе бонуса
        if (powerUpState != PowerUpState.IsCollected)
        {
            return;
        }

        // Срок действия истекает, когда игрока задевают
        PowerUpHasExpired();                 // Пункт 3 контрольного списка
    }

    /// <summary>
    /// Нам нужно реализовать весь интерфейс IPlayerEvents, но нам не нужно реагировать на это сообщение
    /// </summary>
    void IPlayerEvents.OnPlayerReachedExit(GameObject exit)
    {
        
    }

Метод IPlayerEvents.OnPlayerHurt вызывается каждый раз, когда игрок получает урон. Это часть «прислушивания к вещаемым сообщениям». В этом методе мы сначала делаем так, чтобы бонус реагировал только после подбора. Затем код вызывает PowerUpHasExpired в родительском классе, который будет обрабатывать логику истечения срока действия.

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

Создание бонуса увеличения скорости в сцене


GameObject SpeedPowerUp теперь будет выглядеть в инспекторе следующим образом:


Введите в инспекторе следующие значения:

  • Power Up Name: Speed
  • Explanation: Super fast movement until enemy contact
  • Power Up Quote: (Make the Kessel run in less than 12 parsecs)
  • Expires Immediately: снять флажок
  • Special Effect: ParticlesCollected (тот же, что и для звезды)
  • Sound Effect power_up_collect_01 (тот же, что и для звезды)
  • Speed Multiplier: 2

После этого бонус будет выглядеть следующим образом:


Настроив параметры бонуса Speed нужным вам образом, перетащите его в папку дерева проекта Prefabs/Power Ups, чтобы создать префаб. Мы не будем использовать его в демо-проекте, но для завершённости стоит это сделать.

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



Отталкивающий бонус



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

Найдите и изучите в иерархии проекта префаб PowerUpPush. Откройте его скрипт под названием PowerUpPush и изучите код.


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

    private void Update ()
    {
        if (powerUpState == PowerUpState.IsCollected &&     //1
            numberOfUsesRemaining > 0)                      //2
        {
            if (Input.GetKeyDown ("p"))                     //3
            {
                PushSpecialEffects ();                      //4
                PushPhysics ();                             //5
                numberOfUsesRemaining--;
                if (numberOfUsesRemaining <= 0)
                {
                    PowerUpHasExpired ();                   //7
                }
            }
        }
    }

Вот, что происходит в этом коде:

  1. Скрипт должен выполняться только для собранных бонусов.
  2. Скрипт должен выполняться, только если осталось количество использований.
  3. Скрипт должен выполняться, только когда игрок нажимает на P.
  4. Скрипт запускает выполнение красивого эффекта частиц вокруг игрока.
  5. Скрипт отталкивает врагов от игрока.
  6. Бонус используется ещё раз.
  7. Если число использований закончилось, то срок действия бонуса заканчивается.

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


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



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

Советы и рекомендации для выполнения этого задания:

  1. Sprite: найдите спрайт в папке проекта Textures/shield
  2. Power Up Name: Invulnerable
  3. Power Up Explanation: You are Invulnerable for a time
  4. Power Up Quote: (Great kid, don't get cocky)
  5. Coding: пройдитесь по тому же контрольному списку, который мы использовали для бонуса-звезды и увеличения скорости. Вам понадобится таймер, контролирующий срок действия бонуса. Метод SetInvulnerability в PlayerBrain будет включать и отключать неуязвимость.
  6. Effects: проект содержит эффект частиц для красивого эффекта пульсации вокруг игрока, пока он остаётся неуязвимым. Префаб находится в Prefabs/Power Ups/ParticlesInvuln. Можно сделать его дочерним экземпляром игрока, пока он неуязвим.

Вам нужно полное решение или вы хотите сравнить своё решение с нашим? Тогда вот оно:

Решение внутри
Создайте новый Sprite, назовите его PowerUpInvuln и поместите в (X:-0.76, Y:1.29). Для Scale задайте значения X:0.7, Y:0.7. Этот GameObject не будет никого слушать, срок его действия будет просто истекать после заданного времени, поэтому нет необходимости давать ему метку в инспекторе.

Добавьте Box Collider 2D и измените Size на X = 0.2, Y = 0.2. В компоненте Sprite Renderer назначьте Sprite значение shield и выберите цвет, как мы это раньше делали с другими бонусами. Убедитесь, что флажок Is Trigger поставлен. После этого GameObject должен выглядеть примерно так:


Добавьте к этому GameObject новый скрипт PowerUpInvuln и вставьте в него следующий код:

class PowerUpInvuln : PowerUp
{
    public float invulnDurationSeconds = 5f;
    public GameObject invulnParticles;
    private GameObject invulnParticlesInstance;

    protected override void PowerUpPayload ()     // Пункт 1 контрольного списка
    {
        base.PowerUpPayload ();
        playerBrain.SetInvulnerability (true);
        if (invulnParticles != null)
        {
            invulnParticlesInstance = Instantiate (invulnParticles, playerBrain.gameObject.transform.position, playerBrain.gameObject.transform.rotation, transform);
        }
    }

    protected override void PowerUpHasExpired ()     // Пункт 2 контрольного списка
    {
        if (powerUpState == PowerUpState.IsExpiring)
        {
            return;
        }
        playerBrain.SetInvulnerability (false);
        if (invulnParticlesInstance != null)
        {
            Destroy (invulnParticlesInstance);
        }
        base.PowerUpHasExpired ();
    }

    private void Update ()                            // Пункт 3 контрольного списка
    {
        if (powerUpState == PowerUpState.IsCollected)
        {
            invulnDurationSeconds -= Time.deltaTime;
            if (invulnDurationSeconds < 0)
            {
                PowerUpHasExpired ();
            }
        }
    }
}

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

  1. PowerUpPayload: вызывает метод base для вызова родительского класса, затем даёт игроку бонус неуязвимости. Также добавляет пульсирующий эффект частиц.
  2. PowerUpHasExpired: необходимо удалить полученный бонус неуязвимости, а затем вызвать метод base.
  3. Последний пункт контрольного списка — вызов PowerUpHasExpired при завершении срока действия бонуса. В нашем случае мы используем Update() для обратного отсчёта прошедшего времени.

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


Надеюсь, вы попробовали решить задание, прежде чем посмотрели решение!

Куда двигаться дальше?



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

Если вы хотите дальше развивать проект, то можно сделать следующее:

  • Добавить больше бонусов. Как насчёт бонуса, стреляющего убивающими врагов лучами?
  • Создать класс-фабрику для случайного создания бонусов на арене в процессе игры.
  • Если вы хотите дальше изучать Unity, то оцените Unity Games by Tutorials book из нашего магазина.

Вы всё ещё не поняли, из какой серии фильмов эти фразы? Ответ под спойлером:

Ответ
Ура! Наверно, вы единственный человек в Интернете, нажавший сюда! Ответ: это цитаты из серии «Звёздные войны».




К сожалению, не доступен сервер mySQL