Найти в Дзене

Unity 2D. Ruby's Adventure. Part 22

В предыдущих частях мы добавили сломанного робота, настроенного враждебно по отношению к Ruby, при столкновении у Ruby снимается здоровье. Теперь наша задача реализовать "стрельбу" шестеренками для персонажа, чтобы при попадании шестеренки сломанный робот становился исправным и больше не наносил вреда Ruby. Для начала создайте игровой объект снаряда для "стрельбы". Используйте спрайт в папке Art > Sprites > VFX под названием CogBullet. Измените для спрайта параметр Pixels Per Unit на 300 (либо на свое значение) и перетащите спрайт в окно иерархии. Созданному игровому объекту добавьте компоненты Rigidbody2D и BoxCollider2D, чтобы шестеренка могла двигаться и врезаться в препятствия. Значение параметра Gravity Scale у компонента Rigidbody2D установите равным 0, а также зафиксируйте вращение по оси Z. Добавьте компонент скрипта с именем Projectile. Вместо того, чтобы перемещать снаряд самостоятельно, каждый раз определяя новую позицию, как вы это делали для главного героя или врагов, на э

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

Для начала создайте игровой объект снаряда для "стрельбы". Используйте спрайт в папке Art > Sprites > VFX под названием CogBullet.

Измените для спрайта параметр Pixels Per Unit на 300 (либо на свое значение) и перетащите спрайт в окно иерархии.

Созданному игровому объекту добавьте компоненты Rigidbody2D и BoxCollider2D, чтобы шестеренка могла двигаться и врезаться в препятствия.

Значение параметра Gravity Scale у компонента Rigidbody2D установите равным 0, а также зафиксируйте вращение по оси Z.

Добавьте компонент скрипта с именем Projectile.

Настройка шестеренки
Настройка шестеренки

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

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

Для этого как обычно создадим переменную типа Rigidbody2D и в методе Start привяжем ее к компоненту Rigidbody2D:

Скрипт объекта
Скрипт объекта

Затем создайте функцию под названием Launch, с помощью которой мы будем запускать снаряд в нужный момент. Чтобы запустить снаряд необходимо знать направление, в котором снаряд полетит и силу, с которой этот снаряд будет запущен (быстрее или медленнее). Поэтому у метода Launch будет два входных параметра: Vector2 direction (направление) и float force (сила).

Чтобы запустить снаряд будем использовать метод AddForce, которая толкает Rigidbody с силой, равной направлению, умноженному на величину силы:

rb.AddForce(direction * force);

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

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

Проверьте скрипт:

Скрипт объекта
Скрипт объекта

Создайте префаб из возданного объекта шестеренки (перетащите объект из окна ИЕРАРХИИ в окно проекта в папку Prefabs). И теперь удалите шестеренку со сцены, потому что при запуске игры ее там быть не должно.

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

Для этого в скрипте RubyController создайте публичную переменную с именем projectilePrefab типа GameObject:

public GameObject projectilePrefab;

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

Перетащите созданный префаб снаряда в это свойство:

-4

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

В этом методе мы воспользуемся новым методом для создания игрового объекта прямо в коде.

Выглядеть это будет так:

GameObject projectileObject =

Instantiate(projectilePrefab, rb.position + Vector2.up * 0.5f, Quaternion.identity);

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

Получается, что Unity создаст объект, который является копией префаба шестеренки, и разместит его в положение чуть выше вашего Rigidbody, чтобы объект находился рядом с руками Ruby, а не с его ногами. Угол поворота шестеренки при создании задается через Quaternion.identity.

Кватернионы — это математические операторы, при помощи которых можно задавать вращение. Это достаточно сложные операторы, поэтому пока что нам достаточно знать, что Quaternion.identity означает отсутствие вращения. Поэтому объект шестеренки создастся в таком же виде, как и префаб.

После создания объекта шестеренки прямо в коде нам нужно запустить ее. Поэтому нам надо получить скрипт Projectile из этого нового объекта и вызвать метод Launch, которую мы написали ранее, с направлением, в котором смотрит ваш персонаж, и величиной силы, установленной на 300:

Projectile projectile = projectileObject.GetComponent<Projectile>(); projectile.Launch(lookDirection, 300);

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

И нам нужно активировать триггер с именем Launch, чтобы сообщить аниматору о необходимости воспроизвести анимацию броска:

animator.SetTrigger("Launch");

Последнее, что нужно сделать — определить, когда игрок нажимает клавишу стрельбы, и вызвать в этот момент метод Launch. Определение нажатия клавиши осуществляется ТОЛЬКО в методе Update, он единственный вызывается каждый кадр. Для определения нажатия клавиши воспользуемся методом Input.GetKeyDown, который определяет единичное нажатие клавиши, которая указана в качестве входного параметра, в нашем случае это клавиша "С":

if(Input.GetKeyDown(KeyCode.C))

{

Launch();

}

Проверьте скрипт:

Скрипт объекта
Скрипт объекта
Скрипт объекта
Скрипт объекта

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

Ошибки
Ошибки

Ошибки все одинаковые, поскольку это одна и та же ошибка, которая возникает каждый кадр. Она ссылается на 23 строку скрипта Projectile. Кликните два раза мышкой на любой строке с описанием ошибки и откроется редактор кода с курсором на той строке, где возникла ошибка (на скрине эта строка выделена):

Строка с ошибкой
Строка с ошибкой

В описании ошибки сказано:

NullReferenceException: Object reference not set to an instance of an object...

Это означает, что ссылка на объект является пустой, нулевой. Объект в этой строке всего один - это rb, компонент Rigidbody2D. Но почему он стал пустым?

Это произошло потому, что Unity запускает метод Start не прямо при создании объекта, а в следующем кадре. Для создания и запуска шестеренки мы используем следующий код:

GameObject projectileObject = Instantiate(....);

Projectile projectile = projectileObject.GetComponent<Projectile>();
projectile.Launch(lookDirection, 300);

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

Поэтому, когда вызывается Launch для шестернки, оказывается, что переменная rb еще пустая. Чтобы это исправить, переименуйте функцию void Start() в скрипте Projectile в void Awake() .

В отличие от Start, метод Awake вызывается сразу при создании объекта (когда вызывается Instantiate), поэтому rb правильно инициализируется перед вызовом Launch.

Скрипт объекта шестеренки
Скрипт объекта шестеренки

Можете запустить игру и проверить, что ошибка пропала.

Теперь давайте разберемся, куда шестеренка сразу же пропадает. Дело в том, что ваш снаряд сталкивается с Ruby. Мы создали шестеренку, при этом ее коллайдер автоматически сразу же соприкоснулся с коллайдером Ruby. Естественно сработал метод OnCollisitonEnter для шестеренки в скрипте Projectile, в котором мы вызываем Destroy и уничтожаем объект шестеренки.

Правильный способ убрать это ненужное столкновение — использовать Layers (слои), которые позволяют группировать игровые объекты вместе, чтобы их можно было фильтровать.

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

Чтобы увидеть, в каком слое находится игровой объект, щелкните раскрывающийся список «Layer» в правом верхнем углу инспектора. Все объекты располагаются по умолчанию на слое "Default" (номер слоя 0). В игре может быть до тридцати двух различных слоев.

Слой персонажа
Слой персонажа

Нам нужно создать свой собственный слой, поэтому выберите «Add Layer», чтобы открыть диспетчер слоев.

Диспетчер слоев
Диспетчер слоев

Слои с 0 по 5 уже заняты (третий слой свободен, но мы его пропустим), и вы не можете их изменить. Поэтому в поле Layer 6 введите название Character, а в Layer 7 - Projectile:

Новые слои
Новые слои

Теперь выберите объект Ruby и измените его Layer на Character. А у префаба шестеренки установите слой на Projectile.

Слой персонажа
Слой персонажа
Слой снаряда
Слой снаряда

Затем откройте меню Edit > Project Settings > Physics 2D и посмотрите на матрицу столкновений слоев в нижней части открытого окна, чтобы увидеть, какие слои с какими могут сталкиваться:

Столкновения слоев
Столкновения слоев

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

Изменение параметра
Изменение параметра

Просто закройте окно и запустите игру. Теперь шестеренка больше не сталкивается с Ruby, но будет сталкиваться с другими объектами, такими как ящики или здания.