Почему возникает ошибка? Unity3D

Вот код

public class CamAndPlayerMovement : MonoBehaviour
{
public GameObject _mainCamera;

[SerializeField] private float _moveSpeed = 5f;
[SerializeField] private float _jumpforce = 8f;
public AnimationCurve _jumpCurve;
[SerializeField] private float _jumpTime = 1.5f;
private bool _isGrounded;

private Rigidbody _playerRb;
private Rigidbody _camRb;

private GroundCheck _groundCheckScript;

private void Start()
{
    _playerRb = GetComponent<Rigidbody>();
    _camRb = _mainCamera.GetComponent<Rigidbody>();
    _jumpCurve = GetComponent<AnimationCurve>();
    _groundCheckScript = GetComponentInChildren<GroundCheck>();
}

private void Update()
{
    Move(_moveSpeed);
    
    _isGrounded = _groundCheckScript._groundCheck;
    if (Input.GetKeyDown(KeyCode.Space) && _isGrounded)
    {
        StartCoroutine(Jump(_jumpTime, _jumpforce)); 
    }
}

// Движение игрока и следование камеры
private void Move(float moveSpeed)
{
    var directionX = Input.GetAxis("Horizontal");
    var directionZ = Input.GetAxis("Vertical");

    var playerVelocity = _playerRb.velocity;

    playerVelocity.x = directionX * moveSpeed;
    playerVelocity.z = directionZ * moveSpeed;

    _playerRb.velocity = new Vector3 (playerVelocity.x, playerVelocity.y, 
playerVelocity.z);
    _camRb.velocity = new Vector3(playerVelocity.x, playerVelocity.y, playerVelocity.z);
}

// Прыжок
IEnumerator Jump(float jumpTime, float jumpForce)
{
        var estimatedTime = 0f;
        var playerVelocity = _playerRb.velocity;

        while (estimatedTime < jumpTime)
        {
            playerVelocity.y = _jumpCurve.Evaluate(estimatedTime) * jumpForce;
            yield return new WaitForSeconds(Time.deltaTime);
            estimatedTime += Time.deltaTime;
        }
}

}   

Unity выдает NullReferenceException в этой строчке

_isGrounded = _groundCheckScript._groundCheck;

Вот код скрипта GroundCheck

public class GroundCheck : MonoBehaviour
{
public bool _groundCheck;

private void OnTriggerStay(Collider other)
{
    if (other.gameObject.CompareTag("Platform"))
    {
        _groundCheck = true;
    }
}
private void OnTriggerExit(Collider other)
{
    if (other.gameObject.CompareTag("Platform"))
    {
        _groundCheck = false;
    }
}
}

Что я сделал не так?


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

Автор решения: Olegator36
_groundCheckScript = GetComponentInChildren<GroundCheck>();

Видимо не получает скрипт с другого объекта. Но это можно сделать, создав статичную переменную в классе GroundCheck.

public class GroundCheck : MonoBehaviour
{
    public static GroundCheck gc;
    public bool _groundCheck;
    
    private void Start()
    {
        gc = this;
    }
    private void OnTriggerStay(Collider other)
    {
        if (other.gameObject.CompareTag("Platform"))
        {
            _groundCheck = true;
        }
    }
    private void OnTriggerExit(Collider other)
    {
        if (other.gameObject.CompareTag("Platform"))
        {
            _groundCheck = false;
        }
    }
}

Получить:

GroundCheck gc = GroundCheck.gc;

Ещё вариант:

GroundCheck gc = transform.GetChild(0).GetComponent<GroundCheck>();
→ Ссылка
Автор решения: KOTlK

Во-первых, не надо использовать поиск компонентов в children, проще перенести нужный скрипт в инспекторе. Во-вторых, не надо использовать поиск по тэгу потому что:

  1. Можно ошибиться в строке и написать слово с ошибкой.
  2. Если на объекте не будет нужного тэга, но будет коллайдер, то игрок будет стоять на объекте, но игра будет думать, что он в воздухе.

Вместо этого проверяйте нормаль поверхности, с которой соприкасается коллайдер.

private bool _isGrounded;

private void OnCollisionEnter(Collision collision)
{
    if (IsGround(collision.contacts))
    {
        _isGrounded = true;
    }

}


private void OnCollisionExit(Collision collision)
{
    _isGrounded = false;
}

private bool IsGround(ContactPoint[] points)
{
    foreach (var contact in points)
    {
        if (contact.normal.y >= 0.5f &&
            contact.normal.x <= 0.5f &&
            contact.normal.z <= 0.5f)
        {
            return true;
        }
    }
    return false;
}

IsGrounded принимает массив точек соприкосновения, проходится по ним, и, если хоть у одной из точек нормаль по оси Y больше нужного значения, то возвращает true. Нормаль - вектор, перпендикулярный касательной в точке поверхности. Изменяя максимальные значения для нормали, можно уменьшать/увеличивать угол, при котором поверхность будет считаться землей. введите сюда описание изображения

В OnCollisionEnter присваивается true _isGrounded если есть соприкосновение с землей. Код из этого метода можно перенести в OnCollisionStay, если будут какие-то проблемы.

Таким образом, даже если на объекте, который должен быть землей, нет нужного тэга, объект будет понимать, находится он на земле или нет.

→ Ссылка