Unity движение работает не правильно

Пытаясь повторить движение по этому ролику https://www.youtube.com/watch?v=puPjNRJMmOc столкнулся с рядом проблем.

1.После того как клавишу отпустили игрок движется некоторое время.

2.При соприкосновением с стеной то либо по горизонтали или по вертикали игрок движется медленнее или не двигается.

Код:

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class MovePlayer : MonoBehaviour
{
    public float _speed = 5f;
    public Rigidbody rb;
    private Vector3 _normal;

    void FixedUpdate()
    {
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");

        Move(new Vector3(-v, 0, h));
    }

    public void Move(Vector3 direction)
    {
        Vector3 directionAlongSurface = Project(direction.normalized);
        Vector3 offset = directionAlongSurface * (_speed * Time.deltaTime);

        rb.MovePosition(rb.position + offset);
    }

    public Vector3 Project(Vector3 forward)
    {
        return forward - Vector3.Dot(forward, _normal) * _normal;
    }

    private void OnCollisionEnter(Collision collision)
    {
        _normal = collision.contacts[0].normal;
    }

    private void OnDrawGizmos()
    {
        Gizmos.color = Color.white;
        Gizmos.DrawLine(transform.position, transform.position + _normal * 3);
        Gizmos.color = Color.red;
        Gizmos.DrawLine(transform.position, transform.position + Project(transform.forward));
    }
}

Заранее спасибо


Ответы (1 шт):

Автор решения: KOTlK

Код из видео - упрощенная версия подобной системы передвижения. При столкновении с чем-либо, ваш контроллер берет нормаль, с которой произошло столкновение, и пытается двигать объект, относительно этой нормали. Когда он сталкивается с землей, нормаль равна Vector3(0,1,0), но если столкнуться с другим объектом, то нормаль будет другая и Y меньше единицы, соответственно движение будет либо заблокировано либо замедлено.

Если хотите использовать эту систему передвижения, то нужно ограничить возможную нормаль поверхности. Можете сделать что-то типа этого:

using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
public class PlayerMovement : MonoBehaviour
{
    [SerializeField] private float _speed = 5f;
    private Rigidbody _rb;
    private Vector3 _normal;
    private float _horizontalAxis;
    private float _verticalAxis;

    private void Awake()
    {
        _rb = GetComponent<Rigidbody>();
    }
    private void Update()
    {
        _horizontalAxis = Input.GetAxis("Horizontal");
        _verticalAxis = Input.GetAxis("Vertical");
    }

    void FixedUpdate()
    {
        Move(new Vector3(-_verticalAxis, 0, _horizontalAxis));
    }

    public void Move(Vector3 direction)
    {
        var directionAlongSurface = Project(direction.normalized);
        var offset = directionAlongSurface * (_speed * Time.deltaTime);

        _rb.MovePosition(_rb.position + offset);
    }

    public Vector3 Project(Vector3 forward)
    {
        return forward - Vector3.Dot(forward, _normal) * _normal;
    }

    private void OnCollisionEnter(Collision collision)
    {
        foreach (var contact in collision.contacts)
        {
            var normal = contact.normal;
            if (IsGround(normal))
            {
                _normal = normal;
                break;
            }
        }
    }

    private bool IsGround(Vector3 normal)
    {
        if (normal.x > 0.5) return false;
        if (normal.z > 0.5) return false;
        if (normal.y < 0.1) return false;

        return true;
    }

    private void OnDrawGizmos()
    {
        Gizmos.color = Color.white;
        Gizmos.DrawLine(transform.position, transform.position + _normal * 3);
        Gizmos.color = Color.red;
        Gizmos.DrawLine(transform.position, transform.position + Project(transform.forward));
    }
}


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

Это не самое лучшее решение, но оно должно работать. Можете поискать информацию о том, как делать то же самое с помощью каста коллайдера.

По поводу движения после отпускания клавиши. Во время нажатия на кнопку движения, вы вызываете метод MovePosition, он движет объект на основе ускорения, а т.к. ускорение не равно нулю и постепенно снижается, то объект продолжает двигаться. Можете двигать объект, изменяя velocity или уменьшать это ускорение после отпускания клавиши движения.

→ Ссылка