В данной статье мы разберём функцию прыжка, которая довольно часто используется в играх. Для начала нам необходимо объявить две переменные: jumpForce – хранит информацию о силе прыжка, rb – в ней будет храниться ссылка на компонент Rigidbody2D.
private float jumpForce;
private Rigidbody2D rb;
Обязательно перетащите компонент Rigidbody2D в поле rb, в окне Inspector.
Далее пишем код непосредственно самого прыжка, помещённый в метод Update():
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
rb.AddForce(new Vector2(rb.velocity.x, jumpForce), ForceMode2D.Impulse);
}
}
Для того чтобы наш персонаж осуществил прыжок, необходимо, чтобы игрок нажал кнопку Space(пробел). Сам прыжок осуществляется через метод rb.AddForce(). Так как прыжок не затрагивает координаты по оси X, мы оставляем координаты объекта неизменными, с помощью rb.velocity.x. Координаты по оси Y изменим на то значение, которое хранится в переменной jumpForce. ForceMode2D.Impulse является необязательным параметром, который изменяет тип прыжка. Его можно удалить.
Поздравляем, прыжок готов. Но в нём есть небольшая проблема. Мы можем прыгать многократно, летая над землёй бесконечно много. А ведь нам нужно осуществлять прыжки только тогда, когда персонаж находится на земле. Для этого мы должны написать код, который будет определять, находится ли наш персонаж на земле или нет, и заносить данную информацию в переменную, например, в isGrounded. Объявим её:
public bool isGrounded;
Существуют как минимум 4 способа определить, имеется ли под ногами персонажа земля, или её нет. Сейчас мы каждый из этих способов разберём.
1 Способ. Collision2D
В данном способе мы будем использовать класс Collision2D, который реагирует на взаимодействие двух объектов. Мы будем использовать метод OnCollisionEnter2D() – который срабатывает тогда, когда наш объект соприкасается с другим объектом:
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.collider.CompareTag("Ground"))
{
isGrounded = true;
}
}
Когда наш персонаж соприкасается с другим объектом, происходит проверка на тег данного объекта, с которым соприкаснулся персонаж. И если тег данного объекта будет равняться Ground, то в нашу переменную isGrounded занесётся значение true – что будет означать, что наш персонаж находится на земле. Поэтому, не забудьте создать новый тег Ground, и присвоить его тем объектам, с которых может прыгать наш персонаж.
Когда мы осуществим прыжок, и взаимодействие персонажа с землёй будет разорвано, то нам необходимо указать переменной isGrounded, что персонаж уже не находится на земле. Для этого используем метод OnCollisionExit2D () – который срабатывает тогда, когда соприкосновение двух объектов разрушается.
private void OnCollisionExit2D(Collision2D collision)
{
if (collision.collider.CompareTag("Ground"))
{
isGrounded = false;
}
}
Поздравляем, наш прыжок полностью готов, со всеми необходимыми проверками. Весь готовый код выглядит следующим образом:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Jump1 : MonoBehaviour
{
private float jumpForce;
private Rigidbody2D rb;
public bool isGrounded;
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.collider.CompareTag("Ground"))
{
isGrounded = true;
}
}
private void OnCollisionExit2D(Collision2D collision)
{
if (collision.collider.CompareTag("Ground"))
{
isGrounded = false;
}
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space) && isGrounded)
{
rb.AddForce(new Vector2(rb.velocity.x, jumpForce), ForceMode2D.Impulse);
}
}
}
Способ 2. Trigger2D
Данный способ очень схож на предыдущий, но с одним лишь отличием. Методы типа Trigger2D срабатывают тогда, когда объект входит в область другого объекта. Например, используя метод OnTriggerStay2D(), мы проверяем, вошёл ли наш персонаж в другой объект, имеющий тег Ground:
private void OnTriggerStay2D(Collider2D col)
{
if (col.CompareTag("Ground"))
{
Grounded = true;
}
}
Аналогичный код необходимо прописать и для выхода объекта из другого объекта:
private void OnTriggerExit2D(Collider2D col)
{
if (col.CompareTag("Ground"))
{
Grounded = false;
}
}
Как видите, метод аналогичен предыдущему. Но чтобы этот способ работал, необходимо проделать ещё несколько дополнительных действий, а именно:
- Создать новый тег, например, Ground, и присвоить его к земле, то-есть к тем объектам, с которых можно будет осуществлять прыжки.
- Создать новый объект-пустышку, и поместить его внутрь тех объектов, которые представляют собой землю. А сам объект разместить чуть выше объекта-земли для того, чтобы наш персонаж мог в этот объект войти.
- Добавить в объект-пустышку компонент Box Collider 2D, и поставить галочку в поле isTrigger.
В целом, этот способ не очень удобен, для него необходимо создавать новые объекты, и вкладывать его в объекты типа земли.
Итоговый код выглядит следующим образом:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Jump3 : MonoBehaviour
{
private float jumpForce;
private Rigidbody2D rb;
public bool Grounded;
private void OnTriggerStay2D(Collider2D col)
{
if (col.CompareTag("Ground"))
{
Grounded = true;
}
}
private void OnTriggerExit2D(Collider2D col)
{
if (col.CompareTag("Ground"))
{
Grounded = false;
}
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space) && isGrounded)
{
rb.AddForce(new Vector2(rb.velocity.x, jumpForce), ForceMode2D.Impulse);
}
}
}
Способ 3. OverlapCircle
Метод OverlapCircle() довольно часто используется по сравнению с другими способами. Суть данного способа заключается в том, что вокруг персонажа создаётся круговая область. И если в эту область попадает объект типа земля, то мы это записываем в нашу переменную isGrounded. Сейчас мы объявим несколько дополнительных переменных, которые нам понадобятся:
private float groundRadius = 0.3f;
public Transform groundCheck;
public LayerMask groundMask;
А теперь добавим строку, которая будет определять наличие персонажа на земле, и записывать в переменную isGrounded результат:
isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundRadius, groundMask);
Метод Physics2D.OverlapCircle() принимает 3 параметра. Все эти 3 параметра были объявлены нами чуть выше, в предыдущем коде.
groundCheck.position – это координата точки, которая будет являться центром создаваемой окружности. Обычно для неё создаётся новый игровой объект, и помещается под ноги персонажа.
groundRadius – радиус окружности.
groundMask – название слоя, с которым будет взаимодействовать ваш персонаж, например, слой Ground.
Не забудьте в окне Inspector присвоить игровой объект персонажа полю Transform, и новый слой Ground для LayerMask.
Данный способ очень распространён, возможно потому, что он самый короткий в написании кода. Но что самое главное, у данного способа очень большие перспективы не только для прыжка, но и для реализации других интересных идей. Так же стоит отметить, что и в этом и в предыдущих способах мы можем менять проверки как по тегу, так и по слою, на Ваше усмотрение.
Итоговый код выглядит следующим образом:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Jump3 : MonoBehaviour
{
private float jumpForce;
private Rigidbody2D rb;
public bool Grounded;
private float groundRadius = 0.3f;
public Transform groundCheck;
public LayerMask groundMask;
isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundRadius, groundMask);
void Update()
{
if (Input.GetKeyDown(KeyCode.Space) && isGrounded)
{
rb.AddForce(new Vector2(rb.velocity.x, jumpForce), ForceMode2D.Impulse);
}
}
}
Способ 4. Raycas
Данный способ используется довольно редко, но он так же имеет большие возможности в применении, не только в качестве прыжка. Суть данного способа в том, что наш персонаж выпускает луч себе под ноги, который ищет землю под ногами.
Для начала объявим дополнительное поле, которое будет отвечать за длину выпускаемого вниз луча:
public float rayDistance = 0.03f;
Далее выпустим луч от нашего объекта вниз, который будет искать объект на своём пути, имеющий слой Ground. Как мы писали выше, здесь поиск можно использовать как по слою, так и по тегу, как Вам удобно.
RaycastHit2D hit = Physics2D.Raycast(rb.position + Vector2.up * 0.2f, transform.localScale.x * Vector2.down, rayDistance, LayerMask.GetMask("Ground"));
В зависимости от ситуации, в переменную hit попадёт определённое значение, если луч встретит под собой землю. Если же луч земли под собой не обнаружит, то переменная hit получит значение null.
Сейчас мы определить, есть ли значение в переменной hit, или его нет. После чего в переменную isGrounded занесём true или false.
if (hit.collider != null)
{
isGrounded = true;
}
else
{
isGrounded = false;
}
Проверка завершена. Итоговый код выглядит следующим образом:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Jump4 : MonoBehaviour
{
private float jumpForce;
private Rigidbody2D rb;
public bool isGrounded;
public float rayDistance = 0.03f;
RaycastHit2D hit = Physics2D.Raycast(rb.position + Vector2.up * 0.2f, transform.localScale.x * Vector2.down, rayDistance, LayerMask.GetMask("Ground"));
if (hit.collider != null)
{
isGrounded = true;
}
else
{
isGrounded = false;
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space) && isGrounded)
{
rb.AddForce(new Vector2(rb.velocity.x, jumpForce), ForceMode2D.Impulse);
}
}
У меня 1 способ не работает. Я раздал каждому блоку тег Ground, но стоя только на одном из них я не могу прыгать, а когда стою на пересечении блоков моя переменная bool почему-то становится true и персонаж может делать прыжки. С чем это связано?