Проблема со столкновениями Unity3d. Передвижение по типу "Tomb Of The Mask". Объект перестаёт двигаться

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

Скрипт, отвечающий за передвижение:

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


public class PlayerMovement : MonoBehaviour
{

enum Direction
{
    North,
    South,
    East, 
    West
}

[SerializeField]
float speed;

[SerializeField]
LayerMask obstacleMask;

Direction MovingDir;

Rigidbody rb;
bool movingHorizontally, canCheck;

void Start ()
{
    rb = GetComponent<Rigidbody>();
}

void Update ()
{
    if (movingHorizontally)
        canCheck = Physics.Raycast(transform.position, Vector3.left, .6f, obstacleMask) || Physics.Raycast(transform.position, Vector3.right, .6f, obstacleMask);
    else
        canCheck = Physics.Raycast(transform.position, Vector3.forward, .6f, obstacleMask) || Physics.Raycast(transform.position, Vector3.back, .6f, obstacleMask);

    if (canCheck)
    {
        if (Input.GetAxisRaw("Horizontal") != 0)
        {
            rb.constraints = RigidbodyConstraints.FreezePositionZ;
            rb.freezeRotation = true;

            movingHorizontally = true;

            if (Input.GetAxisRaw("Horizontal") > 0)
            {
                MovingDir = Direction.East;
            }
            else
            {
                MovingDir = Direction.West;
            }
        }
        
        else if (Input.GetAxisRaw("Vertical") != 0)
        {
            rb.constraints = RigidbodyConstraints.FreezePositionX;
            rb.freezeRotation = true;

            movingHorizontally = false;
            
            if (Input.GetAxisRaw("Vertical") > 0)
            {
                MovingDir = Direction.North;
            }
            else
            {
                MovingDir = Direction.South;
            }
        }
    }
}

void FixedUpdate()
{
    switch(MovingDir)
    {
        case Direction.North:
            rb.velocity = new Vector3(0, 0, speed * Time.fixedDeltaTime);
            break;
        case Direction.South:
            rb.velocity = new Vector3(0, 0, -speed * Time.fixedDeltaTime);
            break;
        case Direction.East:
            rb.velocity = new Vector3(speed * Time.fixedDeltaTime, 0, 0);
            break;
        case Direction.West:
            rb.velocity = new Vector3(-speed * Time.fixedDeltaTime, 0, 0);
            break;
    }
}
}

Объект игрока Столкновение со стеной


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

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

В общих словах, проблема у вас заключается в том, что движение вы делаете физическое, а вот обработку коллизий - в Update(). Все расчеты, связанные с физикой нужно выполнять в FixedUpdate(). В противном случае, возможны рассинхронизации в ваших вычислениях и ожиданиях.

Если чуть конкретней, то проблемы из разряда "застрял в стене" происходят как раз по причине того, что движение происходит по физике, а коллизии считаются вне физике. Update() вызывается с частотой обновления кадров (FPS) в вашей игре, тогда как FixedUpdate() фиксированное количество раз в секунду. И этим двум интервалам обновления никак не обязательно быть синхронизированными между собой, в частности, FPS - совсем не константная величина. Как раз из-за этого вы можете получить информацию о коллизии уже в тот момент, когда она перестала быть актуальна, и объект прошел сквозь стену.

Для большего понимания почитайте, пожалуйста, вопрос про разницу между Update и FixedUpdate.

→ Ссылка