This repository has been archived on 2024-11-16. You can view files and clone it, but cannot push or open issues or pull requests.

156 lines
3.9 KiB
C#
Raw Permalink Normal View History

2021-02-02 15:10:30 +08:00
using UnityEngine;
public class MovingSphere : MonoBehaviour
{
[SerializeField, Range(0f, 100f)]
private float maxSpeed = 10f;
[SerializeField, Range(0f, 100f)]
2021-02-02 17:30:13 +08:00
private float maxAcceleration = 10f, maxAirAcceleration = 1f;
[SerializeField, Range(0f, 10f)]
2021-02-02 22:49:43 +08:00
private float jumpHeight = 2f;
2021-02-02 17:30:13 +08:00
[SerializeField, Range(0, 5)]
2021-02-02 22:49:43 +08:00
private int maxAirJumps = 0;
[SerializeField, Range(0f, 90f)]
private float maxGroundAngle = 25f;
2021-02-02 17:30:13 +08:00
2021-02-02 16:28:38 +08:00
private Rigidbody body;
2021-02-02 15:10:30 +08:00
2021-02-02 17:30:13 +08:00
private Vector3 desiredVelocity = new Vector3(0f, 0f, 0f);
private bool desiredJump = false;
2021-02-02 22:49:43 +08:00
private int groundContactCount;
private bool onGround => groundContactCount > 0;
2021-02-02 17:30:13 +08:00
private int jumpPhase;
private Vector3 velocity;
2021-02-02 22:49:43 +08:00
private Vector3 contactNormal;
private float minGroundDotProduct;
private void OnValidate()
{
minGroundDotProduct = Mathf.Cos(maxGroundAngle * Mathf.Deg2Rad);
}
2021-02-02 15:10:30 +08:00
2021-02-02 16:28:38 +08:00
private void Awake()
{
body = GetComponent<Rigidbody>();
2021-02-02 22:49:43 +08:00
OnValidate();
2021-02-02 16:28:38 +08:00
}
2021-02-02 15:10:30 +08:00
private void Update()
{
Vector2 playerInput;
playerInput.x = Input.GetAxis("Horizontal");
playerInput.y = Input.GetAxis("Vertical");
playerInput = Vector2.ClampMagnitude(playerInput, 1f);
2021-02-02 16:28:38 +08:00
desiredVelocity = new Vector3(playerInput.x, 0f, playerInput.y) * maxSpeed;
2021-02-02 17:30:13 +08:00
desiredJump |= Input.GetButtonDown("Jump");
2021-02-02 16:28:38 +08:00
}
private void FixedUpdate()
{
2021-02-02 17:30:13 +08:00
UpdateState();
2021-02-02 22:49:43 +08:00
AdjustVelocity();
2021-02-02 17:30:13 +08:00
if (desiredJump)
{
desiredJump = false;
Jump();
}
2021-02-02 16:28:38 +08:00
body.velocity = velocity;
2021-02-02 17:30:13 +08:00
2021-02-02 22:49:43 +08:00
ClearState();
2021-02-02 17:30:13 +08:00
}
private void UpdateState()
{
velocity = body.velocity;
if (onGround)
{
jumpPhase = 0;
2021-02-02 22:49:43 +08:00
if (groundContactCount > 1)
{
contactNormal.Normalize();
}
}
else
{
contactNormal = Vector3.up;
2021-02-02 17:30:13 +08:00
}
2021-02-02 15:10:30 +08:00
}
2021-02-02 17:30:13 +08:00
private void OnCollisionEnter(Collision collision)
{
EvaluateCollision(collision);
}
private void OnCollisionStay(Collision collision)
{
EvaluateCollision(collision);
}
private void EvaluateCollision(Collision collision)
{
for (int i = 0; i < collision.contactCount; i++)
{
var normal = collision.GetContact(i).normal;
2021-02-02 22:49:43 +08:00
if (normal.y >= minGroundDotProduct)
{
groundContactCount += 1;
contactNormal += normal;
}
2021-02-02 17:30:13 +08:00
}
}
private void Jump()
{
if (onGround || jumpPhase < maxAirJumps)
{
jumpPhase++;
float jumpSpeed = Mathf.Sqrt(-2f * Physics.gravity.y * jumpHeight);
2021-02-02 22:49:43 +08:00
float alignedSpeed = Vector3.Dot(velocity, contactNormal);
if (alignedSpeed > 0f)
2021-02-02 17:30:13 +08:00
{
2021-02-02 22:49:43 +08:00
jumpSpeed = Mathf.Max(jumpSpeed - alignedSpeed, 0f);
2021-02-02 17:30:13 +08:00
}
2021-02-02 22:49:43 +08:00
velocity += contactNormal * jumpSpeed;
2021-02-02 17:30:13 +08:00
}
}
2021-02-02 22:49:43 +08:00
private void AdjustVelocity()
{
Vector3 xAxis = ProjectOnContactPlane(Vector3.right).normalized;
Vector3 zAxis = ProjectOnContactPlane(Vector3.forward).normalized;
float currentX = Vector3.Dot(velocity, xAxis);
float currentZ = Vector3.Dot(velocity, zAxis);
float acceleration = onGround ? maxAcceleration : maxAirAcceleration;
float maxSpeedChange = acceleration * Time.deltaTime;
float newX =
Mathf.MoveTowards(currentX, desiredVelocity.x, maxSpeedChange);
float newZ =
Mathf.MoveTowards(currentZ, desiredVelocity.z, maxSpeedChange);
velocity += xAxis * (newX - currentX) + zAxis * (newZ - currentZ);
}
private Vector3 ProjectOnContactPlane(Vector3 vector)
{
return vector - contactNormal * Vector3.Dot(vector, contactNormal);
}
void ClearState()
{
groundContactCount = 0;
contactNormal = Vector3.zero;
}
2021-02-02 15:10:30 +08:00
}