Skip to content

Instantly share code, notes, and snippets.

@grilme99
Last active May 18, 2023 18:20
Show Gist options
  • Save grilme99/6742672cbb3547d99cccee026af4588d to your computer and use it in GitHub Desktop.
Save grilme99/6742672cbb3547d99cccee026af4588d to your computer and use it in GitHub Desktop.
Spring implementation for Unity based on Nevermore
// A physical model of a spring, based off:
// https://github.com/Quenty/NevermoreEngine/blob/2ad8cea7dd3ad79a39afd7d7b785b489b90553fd/src/spring/src/Shared/Spring.lua
//
// A spring is an object that will compute based upon Hooke's law. Properties only evaluate upon index making this class
// good for lazy applications.
using System;
using UnityEngine;
public class Spring
{
private float _damper = 1;
private float _speed = 1;
private float _time0 = Time.time;
private Vector3 _position0;
private Vector3 _velocity0;
private Vector3 _target;
public Spring(Vector3 initial)
{
_position0 = initial;
_velocity0 = 0 * initial;
_target = initial;
}
public void Impulse(Vector3 velocity)
{
Velocity += velocity;
}
public void TimeSkip(float delta)
{
var now = Time.time;
var (position, velocity) = _positionVelocity(now + delta);
_position0 = position;
_velocity0 = velocity;
_time0 = now;
}
public Vector3 Position
{
get
{
var (position, _) = _positionVelocity(Time.time);
return position;
}
set
{
var now = Time.time;
var (_, velocity) = _positionVelocity(now);
_position0 = value;
_velocity0 = velocity;
_time0 = now;
}
}
public Vector3 Velocity
{
get
{
var now = Time.time;
var (_, velocity) = _positionVelocity(now);
return velocity;
}
set
{
var now = Time.time;
var (position, _) = _positionVelocity(now);
_position0 = position;
_velocity0 = value;
_time0 = now;
}
}
public Vector3 Target
{
get { return _target; }
set
{
_target = value;
_resolvePropertyUpdate();
}
}
public float Damper
{
get { return _damper; }
set
{
_damper = value;
_resolvePropertyUpdate();
}
}
public float Speed
{
get { return _speed; }
set
{
_speed = Mathf.Max(0, value);
_resolvePropertyUpdate();
}
}
private void _resolvePropertyUpdate()
{
var now = Time.time;
var (position, velocity) = _positionVelocity(now);
_position0 = position;
_velocity0 = velocity;
_time0 = now;
}
// https://github.com/Quenty/NevermoreEngine/blob/main/src/spring/src/Shared/Spring.lua#L232
private (Vector3, Vector3) _positionVelocity(float now)
{
var p0 = _position0;
var v0 = _velocity0;
var p1 = _target;
var d = _damper;
var s = _speed;
var t = s * (now - _time0);
var d2 = d * d;
float h, si, co;
if (d2 < 1)
{
h = MathF.Sqrt(1 - d2);
var ep = Mathf.Exp(-d * t) / h;
co = ep * Mathf.Cos(h * t);
si = ep * Mathf.Sin(h * t);
}
else if (d2 > 0.999 && d2 < 1.001)
{
h = 1;
var ep = Mathf.Exp(-d * t) / h;
co = ep;
si = ep * t;
}
else
{
h = Mathf.Sqrt(d2 - 1);
var u = Mathf.Exp((-d + h) * t) / (2 * h);
var v = Mathf.Exp((-d - h) * t) / (2 * h);
co = u + v;
si = u - v;
}
var a0 = h * co + d * si;
var a1 = 1 - a0;
var a2 = si / s;
var b0 = -s * si;
var b1 = s * si;
var b2 = h * co - d * si;
return (a0 * p0 + a1 * p1 + a2 * v0,
b0 * p0 + b1 * p1 + b2 * v0);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment