203 lines
7.4 KiB
C#
203 lines
7.4 KiB
C#
using KitsuneCafe.Extension;
|
|
using KitsuneCafe.SOAP;
|
|
using R3;
|
|
using UnityEngine;
|
|
|
|
namespace KitsuneCafe.Entity
|
|
{
|
|
public class Motor : MonoBehaviour
|
|
{
|
|
[SerializeField]
|
|
private new Rigidbody rigidbody;
|
|
|
|
[SerializeField]
|
|
private ReactiveSource<Vector2> direction;
|
|
|
|
[SerializeField]
|
|
private float maxSpeed = 25f;
|
|
public float DefaultMaxSpeed => maxSpeed;
|
|
|
|
[SerializeField]
|
|
private float accelerationTime = 1f;
|
|
public float AccelerationTime => accelerationTime;
|
|
|
|
[SerializeField]
|
|
private AnimationCurve accelerationCurve = AnimationCurve.EaseInOut(0, 0, 1, 1);
|
|
|
|
[SerializeField]
|
|
private float decelerationTime = 0.5f;
|
|
public float Decelerationtime => decelerationTime;
|
|
|
|
[SerializeField]
|
|
private AnimationCurve decelerationCurve = AnimationCurve.EaseInOut(0, 0, 1, 1);
|
|
|
|
[SerializeField]
|
|
private float rotationSpeed = 25f;
|
|
public float RotationSpeed => rotationSpeed;
|
|
|
|
[SerializeField]
|
|
private float epsilon = 0.001f;
|
|
private float sqEpsilon;
|
|
|
|
private ReactiveProperty<float> currentMaxSpeed = new ReactiveProperty<float>();
|
|
public float MaxSpeed => currentMaxSpeed.Value;
|
|
public ReactiveProperty<float> MaxSpeedSource => currentMaxSpeed;
|
|
|
|
//private readonly ReactiveProperty<Vector3> direction = new ReactiveProperty<Vector3>(Vector3.zero);
|
|
|
|
private void Reset()
|
|
{
|
|
rigidbody = GetComponent<Rigidbody>();
|
|
}
|
|
|
|
private void Awake()
|
|
{
|
|
var directionSource = direction.AsObservable().Select(v2 => new Vector3(v2.x, 0, v2.y));
|
|
|
|
currentMaxSpeed.Value = maxSpeed;
|
|
|
|
sqEpsilon = epsilon * epsilon;
|
|
|
|
var d = Disposable.CreateBuilder();
|
|
|
|
CalculateVelocityStream(directionSource, currentMaxSpeed, UnityFrameProvider.FixedUpdate)
|
|
.Subscribe(force =>
|
|
{
|
|
rigidbody.AddForce(force, ForceMode.Acceleration);
|
|
})
|
|
.AddTo(ref d);
|
|
|
|
CalculateRotationStream(directionSource, UnityFrameProvider.FixedUpdate)
|
|
.Subscribe(targetRotation =>
|
|
{
|
|
if (Quaternion.Angle(transform.rotation, targetRotation) > 0.1f)
|
|
{
|
|
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.fixedDeltaTime * rotationSpeed);
|
|
}
|
|
})
|
|
.AddTo(ref d);
|
|
|
|
d.RegisterTo(destroyCancellationToken);
|
|
}
|
|
|
|
private Observable<Quaternion> CalculateRotationStream(Observable<Vector3> directionSource, FrameProvider provider)
|
|
{
|
|
var targetRotationStream = directionSource
|
|
.Select(inputDir =>
|
|
{
|
|
if (inputDir.sqrMagnitude > sqEpsilon)
|
|
{
|
|
return Quaternion.LookRotation(inputDir.normalized);
|
|
}
|
|
else
|
|
{
|
|
return transform.rotation;
|
|
}
|
|
})
|
|
.DistinctUntilChanged();
|
|
|
|
return Observable.EveryUpdate(provider)
|
|
.WithLatestFrom(targetRotationStream, (_, targetRotation) => targetRotation);
|
|
}
|
|
|
|
private Observable<Vector3> CalculateVelocityStream(Observable<Vector3> directionSource, Observable<float> maxSpeedSource, FrameProvider provider)
|
|
{
|
|
return directionSource
|
|
.DistinctUntilChanged()
|
|
.CombineLatest(maxSpeedSource, (direction, currentMaxSpeed) => (direction, currentMaxSpeed))
|
|
.Select(data =>
|
|
{
|
|
var (direction, maxSpeed) = data;
|
|
var start = rigidbody.linearVelocity;
|
|
var targetMagnitude = direction.sqrMagnitude > sqEpsilon ? maxSpeed : 0f;
|
|
var targetVelocity = direction.normalized * targetMagnitude;
|
|
|
|
if (start.SqrDistance(targetVelocity) < sqEpsilon)
|
|
{
|
|
if (targetVelocity.sqrMagnitude < sqEpsilon && start.sqrMagnitude < sqEpsilon)
|
|
{
|
|
return Observable.Return(Vector3.zero);
|
|
}
|
|
else
|
|
{
|
|
return Observable.Return((targetVelocity - start) / Time.fixedDeltaTime);
|
|
}
|
|
}
|
|
|
|
float lerpDuration;
|
|
AnimationCurve curve;
|
|
|
|
if (targetMagnitude > epsilon)
|
|
{
|
|
var ratio = start.magnitude / maxSpeed;
|
|
lerpDuration = accelerationTime * (1f - ratio);
|
|
lerpDuration = Mathf.Max(lerpDuration, 0.05f);
|
|
curve = accelerationCurve;
|
|
}
|
|
else
|
|
{
|
|
lerpDuration = decelerationTime;
|
|
curve = decelerationCurve;
|
|
}
|
|
|
|
return Interpolate(start, targetVelocity, lerpDuration, provider, curve)
|
|
.Select(targetVelocity =>
|
|
{
|
|
var currentVelocity = rigidbody.linearVelocity;
|
|
return (targetVelocity - currentVelocity) / Time.fixedDeltaTime;
|
|
})
|
|
.Where(_ => targetVelocity.sqrMagnitude > sqEpsilon || rigidbody.linearVelocity.sqrMagnitude > sqEpsilon)
|
|
.DefaultIfEmpty(Vector3.zero);
|
|
})
|
|
.Switch();
|
|
}
|
|
|
|
private static Observable<Vector3> Interpolate(Vector3 start, Vector3 target, float duration)
|
|
{
|
|
return Interpolate(start, target, duration, UnityFrameProvider.FixedUpdate, AnimationCurve.Linear(0, 0, 1, 1));
|
|
}
|
|
|
|
private static Observable<Vector3> Interpolate(Vector3 start, Vector3 target, float duration, AnimationCurve curve)
|
|
{
|
|
return Interpolate(start, target, duration, UnityFrameProvider.FixedUpdate, curve);
|
|
}
|
|
|
|
private static Observable<Vector3> Interpolate(Vector3 start, Vector3 target, float duration, FrameProvider provider)
|
|
{
|
|
return Interpolate(start, target, duration, provider, AnimationCurve.Linear(0, 0, 1, 1));
|
|
}
|
|
|
|
private static Observable<Vector3> Interpolate(Vector3 start, Vector3 target, float duration, FrameProvider provider, AnimationCurve curve)
|
|
{
|
|
return Observable.EveryUpdate(provider)
|
|
.Scan(0f, (elapsedTime, _) => elapsedTime + Time.fixedDeltaTime)
|
|
.Select(elapsedTime =>
|
|
{
|
|
var factor = 0f;
|
|
if (duration > float.Epsilon)
|
|
{
|
|
factor = Mathf.Clamp01(elapsedTime / duration);
|
|
}
|
|
else
|
|
{
|
|
factor = 1f;
|
|
}
|
|
|
|
var evaluatedFactor = curve.Evaluate(factor);
|
|
|
|
return Vector3.Lerp(start, target, evaluatedFactor);
|
|
});
|
|
}
|
|
|
|
|
|
public void ChangeMaxSpeed(float value)
|
|
{
|
|
currentMaxSpeed.Value = value;
|
|
}
|
|
|
|
public void ResetMaxSpeed()
|
|
{
|
|
currentMaxSpeed.Value = maxSpeed;
|
|
}
|
|
}
|
|
}
|