using UnityEngine; [RequireComponent(typeof(Rigidbody))] public class SpringyButtonPhysics : MonoBehaviour { public Transform buttonDirectionRoot; public float minX = 0f; public float maxX = 0f; public float minY = 0f; public float maxY = 0f; public float minZ = 0.005f; public float maxZ = 0f; [Header("Spring Settings")] public float springForce = 300f; // how strong it returns public float damping = 8f; // prevents vibration private Rigidbody rb; private Vector3 restLocalPos; void Start() { rb = GetComponent(); rb.useGravity = false; // initial local rest position restLocalPos = buttonDirectionRoot.InverseTransformPoint(transform.position); } void FixedUpdate() { // 1. Local pos Vector3 localPos = buttonDirectionRoot.InverseTransformPoint(transform.position); // 2. Spring in LOCAL space Vector3 spring = (restLocalPos - localPos) * springForce; // 3. Damping also must be in LOCAL space!! Vector3 localVelocity = buttonDirectionRoot.InverseTransformDirection(rb.velocity); localVelocity *= damping; spring -= localVelocity; // 4. Convert spring to WORLD space Vector3 worldSpring = buttonDirectionRoot.TransformDirection(spring); // 5. Apply rb.AddForce(worldSpring, ForceMode.Acceleration); // --- 3. Compute new local pos after physics --- Vector3 newLocalPos = buttonDirectionRoot.InverseTransformPoint(transform.position); //Debug.Log(spring); // --- 4. Clamp to allowed ranges --- newLocalPos.x = Mathf.Clamp(newLocalPos.x, minX, maxX); newLocalPos.y = Mathf.Clamp(newLocalPos.y, minY, maxY); newLocalPos.z = Mathf.Clamp(newLocalPos.z, minZ, maxZ); // --- 5. Convert back to world and move via Rigidbody --- Vector3 worldPos = buttonDirectionRoot.TransformPoint(newLocalPos); rb.MovePosition(worldPos); } }