Добавлен Msey,
опубликован
Принцип написания кода: от плохого к приемлемому, чтобы вам было понятно, как это работает
Буду признателен отзывам / критике и в принципе обратной связи, чтобы понимать: а нужно ли это здесь?
Буду признателен отзывам / критике и в принципе обратной связи, чтобы понимать: а нужно ли это здесь?
Без метровых вступлений опишу задачу на этом этапе/шаге:
- Нужно добавить базовую физику нашему персонажу и примитивные обьекты для проверки коллизий
- Добавим движение на клавишах: влево вправо и прыжок
- Оптимизируем это все
1. Добавляем физику
Здесь все просто, через "Add Component" добавляем circleCollider2D и rigidbody2D
Должно быть так в действии:
Должно быть так в действии:
2. Движение
Пока сделаем базовое движение с "кривым и нерегулируемым прыжком"
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
private Rigidbody2D playerRb;
private float speedRatio = 100; // регулируем для умеренно быстрого ускорения
private void Start()
{
playerRb = this.GetComponent<Rigidbody2D>();
}
private void Update()
{
Vector2 resultSpeed = Vector2.zero;
if (Input.GetKey(KeyCode.A))
resultSpeed += Vector2.left;
if (Input.GetKey(KeyCode.D))
resultSpeed += Vector2.right;
if (Input.GetKey(KeyCode.W)
|| Input.GetKey(KeyCode.Space))
resultSpeed += Vector2.up;
playerRb.AddForce(resultSpeed * speedRatio); // применяем результирующий вектор
}
}
Результат:
Теперь мы делаем прыжок. Здесь будет чуть сложнее:
Для начала нужно определить момент приземления мяча. Если мяч на земле то разрешим ему совершить прыжок. Прыжок не должен происходить с ускореним, а именно: сделали импульс и все - запретили дальше прыгать.
Для начала нужно определить момент приземления мяча. Если мяч на земле то разрешим ему совершить прыжок. Прыжок не должен происходить с ускореним, а именно: сделали импульс и все - запретили дальше прыгать.
Рассчитаем позицию точки, где будем проверять приземление мяча через:
private void OnDrawGizmos()
{
Gizmos.DrawLine(transform.position, (Vector2)transform.position + Vector2.down / 3);
// идем вниз по оси Y на 0.33 (я подобрал таким образом - радиус окружности коллайдера мяча + 0.03). Это значение в следующем коде будет напрямую зависеть от радиуса мяча и размера Scale,
// поэтому не зацикливаемся на реализации и идем дальше
}
Сделаем мяч алым и протестируем:
Результат пока устраивает. Сделаем отлов приземления через Raycast? IsTouching? Physics2D.OverlapCircleAll. Почему через окружность? Бывают ситуации, когда мяч приземляется не строго по центру на край блока, ближе к боковой части - таким образом мяч приземлится, но ничего в коде не выявится.
Перепишем отладку:
Добавим коллайдер в код для получения радиуса мяча
private void OnDrawGizmos()
{
Vector2 globalPosition = (Vector2)transform.position + (Vector2.down * playerCl.radius * transform.lossyScale.y); // позиция игрока + его физ. радиус
Gizmos.DrawLine(transform.position, globalPosition);
Gizmos.DrawWireSphere(globalPosition, (playerCl.radius * transform.lossyScale.y) / 3);
// overlap будем делать по трети его физ. радиуса
}
Так как наш игрок круглый, то физ. радиус считаем по оси ординат, предполагая, что его Scale по двум осям меняется пропорционально
Очевидно это все делается это для того, чтобы при увеличении радиуса коллайдера или scale'а объекта радиус Overlap'a в свое время также увеличивался или уменьшался
Overlap создает коллекцию игровых объектов, которые оказались в поле зрения заданной нами окружности. Мы все сделали правильно и все что нам остается - найти объект, на который приземлился мяч. Проблема заключается в том, что могут в коллекции оказаться лишние объекты и даже сам игрок. Тогда нужен идентификатор, что это "земля, на которую приземлился мяч". Это решается добавлением тэга "ground" к блокам и поиску по этому тэгу в коллекции.
Теперь наш некрасивый код выглядит так:
using System.Linq;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
private Rigidbody2D playerRb;
private CircleCollider2D playerCl;
private float speedRatio = 100;
private bool isLanded;
private void Start()
{
playerRb = this.GetComponent<Rigidbody2D>();
playerCl = this.GetComponent<CircleCollider2D>();
}
private void Update()
{
Vector2 resultSpeed = Vector2.zero;
Vector2 groundHittingPoint = (Vector2)transform.position
+ (Vector2.down * playerCl.radius * transform.lossyScale.y);
float overlapRadius = playerCl.radius * transform.lossyScale.y / 3;
isLanded = Physics2D.OverlapCircleAll(groundHittingPoint, overlapRadius).Any(x => x.tag.Equals("ground"));
if (Input.GetKey(KeyCode.A))
resultSpeed += Vector2.left;
if (Input.GetKey(KeyCode.D))
resultSpeed += Vector2.right;
if (isLanded && (Input.GetKey(KeyCode.W)
|| Input.GetKey(KeyCode.Space)))
playerRb.velocity = new Vector2(playerRb.velocity.x, 5);
playerRb.AddForce(resultSpeed * speedRatio, ForceMode2D.Force);
}
Реализация вышеприведенного кода приведена ниже:
Возникает несколько проблемных ситуаций:
- Мы зависим от DeltaTime. При слабой машине мяч будет двигаться очень медленно, а при мощной он будет мгновенно улетать в разные стороны (хотя второе фиксится ограничением кадров в настройках юнити)
- Силы по X и Y зависимы, а мы бы хотели например ускорять мяч по X в зависимости от массы, а по Y прыгать с одинаковой скоростью и в то же время не дергать AddForce Impulse/Force, которые в свою очередь связаны с проблемой 1.
Теперь приступим к самому интересному:
3. Оптимизация
using System.Linq;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
private Rigidbody2D playerRb;
private CircleCollider2D playerCl;
private bool isLanded;
private const float FIXED_SECOND = 1.0f;
private const string GROUND_TAG = "ground";
private void Start()
{
playerRb = this.GetComponent<Rigidbody2D>();
playerCl = this.GetComponent<CircleCollider2D>();
}
private void Update()
{
Vector2 resultVector = Vector2.zero;
Vector2 groundHittingPoint = (Vector2)transform.position
+ (Vector2.down * playerCl.radius * transform.lossyScale.y);
float overlapRadius = playerCl.radius * transform.lossyScale.y / 3;
isLanded = Physics2D.OverlapCircleAll(groundHittingPoint, overlapRadius).Any(x => x.tag.Equals(GROUND_TAG));
/*
https://ru.wikipedia.org/wiki/Второй_закон_Ньютона
F = m*a,
a = dv / dt,
dv = (v - v0),
a = (v - v0) / dt,
dt = (t - t0),
a = (v - v0) / (t - t0),
a = (v - 0) / (t - 0),
a = v/t,
F = m*(v/t)
v = F/mt
*/
resultVector = AddForceX() / (playerRb.mass * FIXED_SECOND);
// FIXED_SECOND можно заменить на Time.DeltaTime, но тогда скорость мяча будет зависеть от FPS
resultVector += AddForceY();
// прикладываем константную силу по Y отдельно, потому что не хотим, чтобы прыжок зависел от массы мяча
// потом мб поменяем
playerRb.velocity += resultVector;
}
private Vector2 AddForceX()
{
Vector2 force = Vector2.zero;
if (Input.GetKey(KeyCode.A))
force += Vector2.left;
if (Input.GetKey(KeyCode.D))
force += Vector2.right;
return force;
}
private Vector2 AddForceY()
{
Vector2 force = Vector2.zero;
if (isLanded && (Input.GetKey(KeyCode.W)
|| Input.GetKey(KeyCode.Space)))
force += Vector2.up * 2;
// прыжок сделаем вдвое выше
return force;
}
}
Добавляем PhysicsMaterial2d с коэффициентом Bounciness = 0.25, применяем к коллайдеру мяча через перетаскивание и радуемся конечному результату.
Да данном шаге мы все сделали. Следующий шаг: 2, как ни странно. Понравилось? Напиши об этом
`
ОЖИДАНИЕ РЕКЛАМЫ...
Чтобы оставить комментарий, пожалуйста, войдите на сайт.
Отредактирован Msey
Отредактирован ledoed
пс. а если нужен обьект с 0 массой
Отредактирован Msey