canto/Assets/Scripts/Entity/Spring.cs
2025-10-02 15:28:03 -04:00

123 lines
2.9 KiB
C#

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using KitsuneCafe.Sys;
using UnityEngine;
namespace KitsuneCafe.Entities
{
public class Spring : MonoBehaviour, INotifyPropertyChanged
{
[SerializeField]
private new Rigidbody rigidbody;
[SerializeField]
private Raycaster raycaster;
// [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 isColliding = false;
public bool IsColliding
{
get => isColliding;
private set
{
if (isColliding != value)
{
isColliding = value;
Notify();
}
}
}
private bool isAtRideHeight = false;
public bool IsAtRideHeight
{
get => isAtRideHeight;
private set
{
if (isAtRideHeight != value)
{
isAtRideHeight = value;
Notify();
}
}
}
private float relativeVelocity;
private float targetDistance;
private float springForce;
public event PropertyChangedEventHandler PropertyChanged;
private void Reset()
{
rigidbody = GetComponent<Rigidbody>();
}
private void FixedUpdate()
{
IsColliding = raycaster.Raycast(out var hit);
if (isColliding)
{
relativeVelocity = RelativeVelocity(raycaster.Direction, rigidbody, hit.rigidbody);
targetDistance = hit.distance - rideHeight;
springForce = (targetDistance * rideSpringStrength) - (relativeVelocity * rideSpringDamper);
IsAtRideHeight = Approximately(springForce, Physics.gravity.y, 0.001f);
rigidbody.AddForce(raycaster.Direction * springForce);
}
}
private static bool Approximately(float a, float b, float e = float.Epsilon) =>
Math.Abs(a - b) < e;
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) =>
rb == null ? Vector3.zero : rb.linearVelocity;
private void Notify([CallerMemberName] string name = default)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
private void OnDrawGizmos()
{
if (raycaster == null) { return; }
var origin = raycaster.transform.position;
Gizmos.color = Color.white;
Gizmos.DrawRay(origin, raycaster.Direction * rideHeight);
}
}
}