Дело в том, что я не понимаю, как они работают, и мои алгоритмы не работали. Если кто-то делал что-то подобное и успешно создал контроллер, расскажите, пожалуйста, как вы реализовали свой контроллер.
Теперь немного о том, как я пытался это реализовать сам:
1. У меня есть две системы — конечный автомат игрока и сервис «Мотор игрока». В конечном автомате я просто вызываю методы сервиса.
Код: Выделить всё
// Grounded state
public override void FixedTick()
{
base.FixedTick();
Vector3 inputMove = _inputReader.Move;
(Vector3 forward, Vector3 right) = _motor.GetPlayerDirs();
Vector3 desiredMove = forward * inputMove.y + right * inputMove.x;
_motor.Move(desiredMove * SPEED * Time.fixedDeltaTime);
if (!_motor.IsGrounded || _inputReader.IsJumping)
{
_stateMachine.SwitchState
();
return;
}
}
// Airborne state
public override void FixedTick()
{
base.FixedTick();
_verticalVelocity += GRAVITY * Time.fixedDeltaTime;
Vector3 inputMove = _inputReader.Move;
(Vector3 forward, Vector3 right) = _motor.GetPlayerDirs();
Vector3 offsetMove = (forward * inputMove.y + right * inputMove.x).normalized;
Vector3 downMove = Vector3.down;
_motor.Move(downMove * _verticalVelocity * Time.fixedDeltaTime + offsetMove * 2f * Time.fixedDeltaTime);
if (_motor.IsGrounded)
{
_stateMachine.SwitchState();
return;
}
}
Код: Выделить всё
public void Move(Vector3 desiredMove)
{
CheckGround();
Vector3 position = _playerRigidbody.position;
position = MoveWithSlide(position, desiredMove);
position = SnapToGround(position);
_playerRigidbody.MovePosition(position);
}
Код: Выделить всё
private void CheckGround()
{
float halfHeight = _playerCollider.height * 0.5f;
float radius = _playerCollider.radius;
float dist = halfHeight - radius + PROBE_DOWN;
Vector3 center = _playerCollider.transform.TransformPoint(_playerCollider.center);
Ray sphereRay = new Ray(center, Vector3.down);
bool isSupported = Physics.SphereCast(sphereRay, radius, dist,
_worldMask, QTI);
_isGrounded = isSupported;
}
Код: Выделить всё
private Vector3 MoveWithSlide(Vector3 position, Vector3 desiredMove)
{
float desiredDist = desiredMove.magnitude;
Vector3 desiredDir = desiredMove / desiredDist;
GetCapsulePoints(position, out Vector3 p1, out Vector3 p2, out float radius);
if(!Physics.CapsuleCast(p1, p2, radius, desiredDir,
out RaycastHit hit,
desiredDist + SKIN,
_worldMask, QTI))
{
return position + desiredMove;
}
float travel = Mathf.Max(0f, hit.distance - SKIN);
position += desiredDir * travel;
Vector3 left = desiredMove - desiredDir * travel;
Vector3 slide;
if(CheckSlopeAngle(hit.normal))
{
slide = Vector3.ProjectOnPlane(left, hit.normal);
}
else
{
Vector3 horiz = Vector3.ProjectOnPlane(left, Vector3.up);
Vector3 wallNormal = Vector3.ProjectOnPlane(hit.normal, Vector3.up);
if (wallNormal.sqrMagnitude < EPS)
return position;
wallNormal.Normalize();
slide = Vector3.ProjectOnPlane(horiz, wallNormal);
}
float slideDist = slide.magnitude;
if (slideDist < EPS)
return position;
Vector3 slideDir = slide / slideDist;
GetCapsulePoints(position, out Vector3 sp1, out Vector3 sp2, out float sradius);
if (Physics.CapsuleCast(sp1, sp2, sradius, slideDir,
out RaycastHit shit,
slideDist + SKIN, _worldMask, QTI))
{
float md2 = Mathf.Max(0f, shit.distance - SKIN);
return position + slideDir * md2;
}
return position + slide;
}
Код: Выделить всё
private Vector3 SnapToGround(Vector3 position)
{
GetCapsulePoints(position, out Vector3 p1, out Vector3 p2, out float radius);
if (Physics.CapsuleCast(p1, p2, radius, Vector3.down,
out RaycastHit hit,
SNAP_DOWN_DIST + SKIN,
_worldMask, QTI)
&& CheckSlopeAngle(hit.normal))
{
float down = hit.distance - SKIN;
position += Vector3.down * down;
_isGrounded = true;
}
return position;
}

Я знаю, что проблема в приведениях. Если бы я сделал обычный Raycast, нормаль к поверхности была бы возвращена правильно, и тогда столкновение с поверхностью было бы как в обычных играх. Но у меня были некоторые проблемы, о которых я уже, честно говоря, не помню (извините, я уже неделю работаю над этим контроллером).
Была идея заменить CapsuleCast и SphereCast на лучи, идущие по кругу, с радиусом капсулы игрока, вниз, и таким образом искать нормаль для проекции. Но я не уверен, что это хорошая идея.
Подробнее здесь: https://stackoverflow.com/questions/798 ... -rigidbody
Мобильная версия