Files
2026-03-04 10:03:45 +08:00

203 lines
5.3 KiB
C#

using UnityEngine;
namespace Obi
{
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(CapsuleCollider))]
[RequireComponent(typeof(Animator))]
public class ObiCharacter : MonoBehaviour
{
[SerializeField]
private float m_MovingTurnSpeed = 360f;
[SerializeField]
private float m_StationaryTurnSpeed = 180f;
[SerializeField]
private float m_JumpPower = 12f;
[Range(1f, 4f)]
[SerializeField]
private float m_GravityMultiplier = 2f;
[SerializeField]
private float m_RunCycleLegOffset = 0.2f;
[SerializeField]
private float m_MoveSpeedMultiplier = 1f;
[SerializeField]
private float m_AnimSpeedMultiplier = 1f;
[SerializeField]
private float m_GroundCheckDistance = 0.1f;
private Rigidbody m_Rigidbody;
private Animator m_Animator;
private bool m_IsGrounded;
private float m_OrigGroundCheckDistance;
private const float k_Half = 0.5f;
private float m_TurnAmount;
private float m_ForwardAmount;
private Vector3 m_GroundNormal;
private float m_CapsuleHeight;
private Vector3 m_CapsuleCenter;
private CapsuleCollider m_Capsule;
private bool m_Crouching;
private void Start()
{
Physics.IgnoreLayerCollision(LayerMask.NameToLayer("Default"), LayerMask.NameToLayer("Ignore Raycast"), ignore: true);
m_Animator = GetComponent<Animator>();
m_Rigidbody = GetComponent<Rigidbody>();
m_Capsule = GetComponent<CapsuleCollider>();
m_CapsuleHeight = m_Capsule.height;
m_CapsuleCenter = m_Capsule.center;
m_Rigidbody.constraints = RigidbodyConstraints.FreezeRotation;
m_OrigGroundCheckDistance = m_GroundCheckDistance;
}
public void Move(Vector3 move, bool crouch, bool jump)
{
if (move.magnitude > 1f)
{
move.Normalize();
}
move = base.transform.InverseTransformDirection(move);
CheckGroundStatus();
move = Vector3.ProjectOnPlane(move, m_GroundNormal);
m_TurnAmount = Mathf.Atan2(move.x, move.z);
m_ForwardAmount = move.z;
ApplyExtraTurnRotation();
if (m_IsGrounded)
{
HandleGroundedMovement(crouch, jump);
}
else
{
HandleAirborneMovement();
}
ScaleCapsuleForCrouching(crouch);
PreventStandingInLowHeadroom();
UpdateAnimator(move);
}
private void ScaleCapsuleForCrouching(bool crouch)
{
if (m_IsGrounded && crouch)
{
if (!m_Crouching)
{
m_Capsule.height /= 2f;
m_Capsule.center /= 2f;
m_Crouching = true;
}
}
else if (Physics.SphereCast(new Ray(m_Rigidbody.position + Vector3.up * m_Capsule.radius * 0.5f, Vector3.up), maxDistance: m_CapsuleHeight - m_Capsule.radius * 0.5f, radius: m_Capsule.radius * 0.5f, layerMask: -5, queryTriggerInteraction: QueryTriggerInteraction.Ignore))
{
m_Crouching = true;
}
else
{
m_Capsule.height = m_CapsuleHeight;
m_Capsule.center = m_CapsuleCenter;
m_Crouching = false;
}
}
private void PreventStandingInLowHeadroom()
{
if (!m_Crouching && Physics.SphereCast(new Ray(m_Rigidbody.position + Vector3.up * m_Capsule.radius * 0.5f, Vector3.up), maxDistance: m_CapsuleHeight - m_Capsule.radius * 0.5f, radius: m_Capsule.radius * 0.5f, layerMask: -5, queryTriggerInteraction: QueryTriggerInteraction.Ignore))
{
m_Crouching = true;
}
}
private void UpdateAnimator(Vector3 move)
{
m_Animator.SetFloat("Forward", m_ForwardAmount, 0.1f, Time.deltaTime);
m_Animator.SetFloat("Turn", m_TurnAmount, 0.1f, Time.deltaTime);
m_Animator.SetBool("Crouch", m_Crouching);
m_Animator.SetBool("OnGround", m_IsGrounded);
if (!m_IsGrounded)
{
m_Animator.SetFloat("Jump", m_Rigidbody.velocity.y);
}
float value = (float)((Mathf.Repeat(m_Animator.GetCurrentAnimatorStateInfo(0).normalizedTime + m_RunCycleLegOffset, 1f) < 0.5f) ? 1 : (-1)) * m_ForwardAmount;
if (m_IsGrounded)
{
m_Animator.SetFloat("JumpLeg", value);
}
if (m_IsGrounded && move.magnitude > 0f)
{
m_Animator.speed = m_AnimSpeedMultiplier;
}
else
{
m_Animator.speed = 1f;
}
}
private void HandleAirborneMovement()
{
Vector3 force = Physics.gravity * m_GravityMultiplier - Physics.gravity;
m_Rigidbody.AddForce(force);
m_GroundCheckDistance = ((m_Rigidbody.velocity.y < 0f) ? m_OrigGroundCheckDistance : 0.01f);
}
private void HandleGroundedMovement(bool crouch, bool jump)
{
if (jump && !crouch && m_Animator.GetCurrentAnimatorStateInfo(0).IsName("Grounded"))
{
m_Rigidbody.velocity = new Vector3(m_Rigidbody.velocity.x, m_JumpPower, m_Rigidbody.velocity.z);
m_IsGrounded = false;
m_Animator.applyRootMotion = false;
m_GroundCheckDistance = 0.1f;
}
}
private void ApplyExtraTurnRotation()
{
float num = Mathf.Lerp(m_StationaryTurnSpeed, m_MovingTurnSpeed, m_ForwardAmount);
base.transform.Rotate(0f, m_TurnAmount * num * Time.deltaTime, 0f);
}
public void OnAnimatorMove()
{
if (m_IsGrounded)
{
Vector3 velocity = m_Animator.deltaPosition * m_MoveSpeedMultiplier / Time.deltaTime;
velocity.y = m_Rigidbody.velocity.y;
m_Rigidbody.velocity = velocity;
}
}
private void CheckGroundStatus()
{
if (Physics.Raycast(base.transform.position + Vector3.up * 0.1f, Vector3.down, out var hitInfo, m_GroundCheckDistance, -5))
{
m_GroundNormal = hitInfo.normal;
m_IsGrounded = true;
m_Animator.applyRootMotion = true;
}
else
{
m_IsGrounded = false;
m_GroundNormal = Vector3.up;
m_Animator.applyRootMotion = false;
}
}
}
}