using UnityEngine; namespace KitsuneCafe.Entity { public class Spring : MonoBehaviour { [SerializeField] private new Rigidbody rigidbody; [SerializeField] private float rayLength = 1.5f; [SerializeField] private float rideHeight = 1f; [SerializeField] private float rideSpringStrength; [SerializeField] private float rideSpringDamper; [SerializeField] private Vector3 rayOffset = Vector3.zero; [SerializeField] private Vector3 down = Vector3.down; [SerializeField] private LayerMask layerMask; private bool didRayHit; private Vector3 relativeDown; private float relativeVelocity; private float targetDistance; private float springForce; public bool IsSpringColliding => didRayHit; private void Reset() { rigidbody = GetComponent(); layerMask = LayerMask.NameToLayer("Default"); } private void FixedUpdate() { relativeDown = transform.TransformDirection(this.down); didRayHit = Raycast(relativeDown, out var hit); if (didRayHit) { relativeVelocity = RelativeVelocity(down, rigidbody, hit.rigidbody); targetDistance = hit.distance - rideHeight; springForce = (targetDistance * rideSpringStrength) - (relativeVelocity * rideSpringDamper); rigidbody.AddForce(relativeDown * springForce); } } private bool Raycast(Vector3 direction, out RaycastHit hit) { return Physics.Raycast( transform.position + rayOffset, direction, out hit, rayLength, layerMask ); } private static float RelativeVelocity(Vector3 direction, Rigidbody a, Rigidbody b) { var aVelocity = GetVelocity(a); var bVelocity = GetVelocity(b); var aDirVelocity = Vector3.Dot(direction, aVelocity); var bDirVelocity = Vector3.Dot(direction, bVelocity); return aDirVelocity - bDirVelocity; } private static Vector3 GetVelocity(Rigidbody rb) { return rb == null ? Vector3.zero : rb.linearVelocity; } private void OnDrawGizmos() { var origin = transform.position + rayOffset; Gizmos.color = didRayHit ? Color.yellow : Color.red; Gizmos.DrawRay(origin, relativeDown * rayLength); if (didRayHit) { Gizmos.color = Color.green; Gizmos.DrawRay(origin, relativeDown * rideHeight); } } } }