Почему возникает ошибка? 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 шт):
_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>();
Во-первых, не надо использовать поиск компонентов в children, проще перенести нужный скрипт в инспекторе. Во-вторых, не надо использовать поиск по тэгу потому что:
- Можно ошибиться в строке и написать слово с ошибкой.
- Если на объекте не будет нужного тэга, но будет коллайдер, то игрок будет стоять на объекте, но игра будет думать, что он в воздухе.
Вместо этого проверяйте нормаль поверхности, с которой соприкасается коллайдер.
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, если будут какие-то проблемы.
Таким образом, даже если на объекте, который должен быть землей, нет нужного тэга, объект будет понимать, находится он на земле или нет.