Before 优化 机场
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1c7d9229e5994163a5595aaa1d7ee59c
|
||||
timeCreated: 1695198318
|
@@ -0,0 +1,427 @@
|
||||
// Designed by KINEMATION, 2024.
|
||||
|
||||
using KINEMATION.MotionWarping.Runtime.Core;
|
||||
using Kinemation.MotionWarping.Runtime.Utility;
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using Debug = UnityEngine.Debug;
|
||||
using Quaternion = UnityEngine.Quaternion;
|
||||
using Vector3 = UnityEngine.Vector3;
|
||||
|
||||
namespace Kinemation.MotionWarping.Runtime.Core
|
||||
{
|
||||
public class MotionWarping : MonoBehaviour
|
||||
{
|
||||
[Header("Warping")]
|
||||
[SerializeField] private bool scalePlayRate = true;
|
||||
|
||||
[Header("Animator")]
|
||||
[SerializeField, Tooltip("Will try to play the animation.")] private bool playAnimator;
|
||||
[SerializeField, Range(0f, 1f)] private float blendTime;
|
||||
|
||||
[Header("Events")]
|
||||
public UnityEvent onWarpStarted;
|
||||
public UnityEvent onWarpEnded;
|
||||
|
||||
[SerializeField]
|
||||
private Animator animator;
|
||||
public WarpPhase[] warpPhases;
|
||||
|
||||
private Vector3 _endCurveValue;
|
||||
private Vector3 _startCurveValue;
|
||||
|
||||
private int _phaseIndex;
|
||||
|
||||
private MotionWarpingAsset _asset;
|
||||
private WarpPhase _warpPhase;
|
||||
private float _nextPhaseTime;
|
||||
|
||||
private Vector3 _originPosition;
|
||||
private Quaternion _originRotation;
|
||||
|
||||
private bool _bUpdateWarping;
|
||||
private float _warpPlayback;
|
||||
|
||||
private float _rateScale = 1f;
|
||||
private float _warpLength;
|
||||
|
||||
private Vector3 _accumRootMotion;
|
||||
private Vector3 _rootMotion;
|
||||
|
||||
private bool _hasActivePhase;
|
||||
private static readonly int WarpRate = Animator.StringToHash("WarpRate");
|
||||
|
||||
private MotionWarpingIk _motionWarpingIk;
|
||||
|
||||
|
||||
private void Start()
|
||||
{
|
||||
animator = GetComponentInChildren<Animator>();
|
||||
_motionWarpingIk = GetComponent<MotionWarpingIk>();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
onWarpEnded = onWarpStarted = null;
|
||||
}
|
||||
|
||||
private float InvLerp(float startValue, float targetValue, float curveValue)
|
||||
{
|
||||
if (Mathf.Approximately(startValue, targetValue)) return 0f;
|
||||
|
||||
float numerator = curveValue - startValue;
|
||||
float denominator = targetValue - startValue;
|
||||
|
||||
return Mathf.Approximately(denominator, 0f) ? 0f : numerator / denominator;
|
||||
}
|
||||
|
||||
private float SafeDivide(float a, float b)
|
||||
{
|
||||
if (Mathf.Approximately(b, 0f)) return 0f;
|
||||
return a / b;
|
||||
}
|
||||
|
||||
private float GetNormalizedPlayback()
|
||||
{
|
||||
return _warpPlayback / _warpLength;
|
||||
}
|
||||
|
||||
private float GetPhaseProgress()
|
||||
{
|
||||
float alpha = InvLerp(_warpPhase.startTime, _warpPhase.endTime, _warpPlayback);
|
||||
return Mathf.Clamp01(alpha);
|
||||
}
|
||||
|
||||
private void EnterNewPhase()
|
||||
{
|
||||
_originPosition = transform.position;
|
||||
_originRotation = transform.rotation;
|
||||
|
||||
_accumRootMotion = _rootMotion = Vector3.zero;
|
||||
|
||||
_warpPhase = warpPhases[_phaseIndex];
|
||||
_nextPhaseTime = _phaseIndex == warpPhases.Length - 1 ? _warpLength : warpPhases[_phaseIndex + 1].startTime;
|
||||
_hasActivePhase = true;
|
||||
|
||||
_startCurveValue = _asset.GetVectorValue(_warpPhase.startTime);
|
||||
_endCurveValue = _asset.GetVectorValue(_warpPhase.endTime);
|
||||
|
||||
_phaseIndex++;
|
||||
|
||||
if (scalePlayRate && animator != null)
|
||||
{
|
||||
float curveVec = (_endCurveValue - _startCurveValue).magnitude;
|
||||
float realVec = (_warpPhase.Target.GetPosition() - _originPosition).magnitude;
|
||||
realVec = Mathf.Max(0.001f, realVec);
|
||||
|
||||
_rateScale = Mathf.Clamp(curveVec / realVec, _warpPhase.minRate, _warpPhase.maxRate);
|
||||
_rateScale *= _asset.playRateBasis;
|
||||
|
||||
animator.SetFloat(WarpRate, _rateScale);
|
||||
}
|
||||
else
|
||||
{
|
||||
_rateScale = 1f;
|
||||
}
|
||||
|
||||
if (_warpPhase.Target.transform != null)
|
||||
{
|
||||
_originPosition = _warpPhase.Target.transform.InverseTransformPoint(transform.position);
|
||||
_originRotation = Quaternion.Inverse(_warpPhase.Target.transform.rotation) * transform.rotation;
|
||||
}
|
||||
}
|
||||
|
||||
private void ExitCurrentPhase()
|
||||
{
|
||||
_hasActivePhase = false;
|
||||
|
||||
if (_warpPhase.Target.transform == null)
|
||||
{
|
||||
_originPosition = transform.position;
|
||||
_originRotation = transform.rotation;
|
||||
}
|
||||
else
|
||||
{
|
||||
_originPosition = _warpPhase.Target.transform.InverseTransformPoint(transform.position);
|
||||
_originRotation = Quaternion.Inverse(_warpPhase.Target.transform.rotation) * transform.rotation;
|
||||
}
|
||||
|
||||
_startCurveValue = _endCurveValue = _asset.GetVectorValue(_warpPlayback);
|
||||
}
|
||||
|
||||
private Quaternion WarpRotation()
|
||||
{
|
||||
float alpha = _hasActivePhase ? GetPhaseProgress() : 0f;
|
||||
return Quaternion.Slerp(transform.rotation, _warpPhase.Target.GetRotation(), alpha);
|
||||
}
|
||||
|
||||
private Vector3 WarpTranslation()
|
||||
{
|
||||
// 1. Compute the original additive curve value
|
||||
Vector3 prevRootMotion = _rootMotion;
|
||||
_rootMotion = _asset.GetVectorValue(_warpPlayback) - _startCurveValue;
|
||||
|
||||
if (!_hasActivePhase)
|
||||
{
|
||||
// 2. If not in the segment - play the animation itself.
|
||||
|
||||
Vector3 modifiedRootMotion = _rootMotion;
|
||||
modifiedRootMotion.x = _asset.useAnimation.x ? modifiedRootMotion.x : 0f;
|
||||
modifiedRootMotion.y = _asset.useAnimation.y ? modifiedRootMotion.y : 0f;
|
||||
modifiedRootMotion.z = _asset.useAnimation.z ? modifiedRootMotion.z : 0f;
|
||||
return modifiedRootMotion;
|
||||
}
|
||||
|
||||
// 3. Compute the target in the origin space
|
||||
Vector3 localTarget = transform.InverseTransformPoint(_warpPhase.Target.GetPosition());
|
||||
|
||||
// 4. Compute the deltas.
|
||||
Vector3 animationTarget = _endCurveValue - _startCurveValue;
|
||||
animationTarget.x = _asset.useAnimation.x ? animationTarget.x : 0f;
|
||||
animationTarget.y = _asset.useAnimation.y ? animationTarget.y : 0f;
|
||||
animationTarget.z = _asset.useAnimation.z ? animationTarget.z : 0f;
|
||||
|
||||
Vector3 targetDelta = localTarget - animationTarget;
|
||||
|
||||
Vector3 rootMotionDelta = _rootMotion - prevRootMotion;
|
||||
_accumRootMotion.x += Mathf.Abs(rootMotionDelta.x);
|
||||
_accumRootMotion.y += Mathf.Abs(rootMotionDelta.y);
|
||||
_accumRootMotion.z += Mathf.Abs(rootMotionDelta.z);
|
||||
|
||||
// 5. Finally warp the motion.
|
||||
targetDelta.x *= _asset.useLinear.x
|
||||
? GetPhaseProgress()
|
||||
: Mathf.Clamp01(SafeDivide(_accumRootMotion.x, _warpPhase.totalRootMotion.x));
|
||||
|
||||
targetDelta.y *= _asset.useLinear.y
|
||||
? GetPhaseProgress()
|
||||
: Mathf.Clamp01(SafeDivide(_accumRootMotion.y, _warpPhase.totalRootMotion.y));
|
||||
|
||||
targetDelta.z *= _asset.useLinear.z
|
||||
? GetPhaseProgress()
|
||||
: Mathf.Clamp01(SafeDivide(_accumRootMotion.z, _warpPhase.totalRootMotion.z));
|
||||
|
||||
Vector3 rootAnimation = Vector3.zero;
|
||||
rootAnimation.x = _asset.useAnimation.x ? _rootMotion.x : 0f;
|
||||
rootAnimation.y = _asset.useAnimation.y ? _rootMotion.y : 0f;
|
||||
rootAnimation.z = _asset.useAnimation.z ? _rootMotion.z : 0f;
|
||||
|
||||
return rootAnimation + targetDelta;
|
||||
}
|
||||
|
||||
private void WarpAnimation()
|
||||
{
|
||||
Vector3 cachedPosition = transform.position;
|
||||
|
||||
if (_warpPhase.Target.transform == null)
|
||||
{
|
||||
transform.position = _originPosition;
|
||||
transform.rotation = _originRotation;
|
||||
}
|
||||
else
|
||||
{
|
||||
transform.position = _warpPhase.Target.transform.TransformPoint(_originPosition);
|
||||
transform.rotation = _warpPhase.Target.transform.rotation * _originRotation;
|
||||
}
|
||||
|
||||
Vector3 warpedTranslation = WarpTranslation();
|
||||
Quaternion warpedRotation = WarpRotation();
|
||||
|
||||
warpedTranslation = transform.TransformPoint(warpedTranslation);
|
||||
|
||||
if (_asset.useCollision)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
transform.position = warpedTranslation;
|
||||
}
|
||||
|
||||
transform.rotation = warpedRotation;
|
||||
}
|
||||
|
||||
private void UpdateWarping()
|
||||
{
|
||||
if (_warpPlayback > _warpPhase.endTime && _hasActivePhase)
|
||||
{
|
||||
ExitCurrentPhase();
|
||||
}
|
||||
|
||||
if (!_hasActivePhase && _warpPlayback > _nextPhaseTime)
|
||||
{
|
||||
EnterNewPhase();
|
||||
}
|
||||
|
||||
WarpAnimation();
|
||||
|
||||
// Update playback
|
||||
_warpPlayback += Time.deltaTime * _rateScale;
|
||||
_warpPlayback = Mathf.Clamp(_warpPlayback, 0f, _warpLength);
|
||||
|
||||
if (Mathf.Approximately(GetNormalizedPlayback(), 1f))
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (!_bUpdateWarping) return;
|
||||
|
||||
UpdateWarping();
|
||||
|
||||
if (_motionWarpingIk == null) return;
|
||||
_motionWarpingIk.ApplyIK();
|
||||
}
|
||||
|
||||
private void Play_Internal(MotionWarpingAsset motionWarpingAsset)
|
||||
{
|
||||
if (playAnimator && animator != null)
|
||||
{
|
||||
animator.CrossFade(motionWarpingAsset.animation.name, blendTime);
|
||||
}
|
||||
|
||||
_startCurveValue = _endCurveValue = _accumRootMotion = _rootMotion = Vector3.zero;
|
||||
|
||||
_warpPhase.Target.transform = null;
|
||||
_originPosition = transform.position;
|
||||
_originRotation = transform.rotation;
|
||||
|
||||
_asset = motionWarpingAsset;
|
||||
|
||||
_phaseIndex = 0;
|
||||
_nextPhaseTime = warpPhases[0].startTime;
|
||||
|
||||
_bUpdateWarping = true;
|
||||
_hasActivePhase = false;
|
||||
|
||||
_rateScale = 1f;
|
||||
_warpLength = motionWarpingAsset.GetLength();
|
||||
|
||||
onWarpStarted.Invoke();
|
||||
}
|
||||
|
||||
public bool Interact(GameObject target)
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Interact(target.GetComponent<IWarpPointProvider>());
|
||||
}
|
||||
|
||||
public bool Interact(IWarpPointProvider target)
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var result = target.Interact(gameObject);
|
||||
if (!result.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Play(result.asset, result.points);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Play(MotionWarpingAsset motionWarpingAsset, WarpPoint[] warpPoints)
|
||||
{
|
||||
if (motionWarpingAsset == null)
|
||||
{
|
||||
Debug.LogError("MotionWarping: WarpPoint[] warpPoints is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (warpPoints == null)
|
||||
{
|
||||
Debug.LogError("MotionWarping: Warp Points array is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
warpPhases = motionWarpingAsset.warpPhases.ToArray();
|
||||
|
||||
if (warpPhases.Length != warpPoints.Length)
|
||||
{
|
||||
Debug.LogError("MotionWarping: Warp Phases and Warp Points array do not match!");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < warpPhases.Length; i++)
|
||||
{
|
||||
WarpPhase phase = warpPhases[i];
|
||||
WarpPoint target = warpPoints[i];
|
||||
|
||||
if (target.transform == null)
|
||||
{
|
||||
phase.Target.position = WarpingUtility.ToWorld(target.position, target.rotation,
|
||||
phase.tOffset);
|
||||
phase.Target.rotation = target.rotation * Quaternion.Euler(phase.rOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
phase.Target.transform = target.transform;
|
||||
|
||||
phase.Target.position = target.position;
|
||||
phase.Target.rotation = target.rotation;
|
||||
|
||||
phase.Target.localPosition = phase.tOffset;
|
||||
phase.Target.localRotation = phase.rOffset;
|
||||
}
|
||||
|
||||
warpPhases[i] = phase;
|
||||
}
|
||||
|
||||
Play_Internal(motionWarpingAsset);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_bUpdateWarping = false;
|
||||
_warpPlayback = 0f;
|
||||
onWarpEnded.Invoke();
|
||||
}
|
||||
|
||||
public bool IsActive()
|
||||
{
|
||||
return _bUpdateWarping;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private List<WarpDebugData> _warpDebugData = new List<WarpDebugData>();
|
||||
|
||||
public static void AddWarpDebugData(MotionWarping target, WarpDebugData warpDebugData)
|
||||
{
|
||||
if (target == null) return;
|
||||
target._warpDebugData.Add(warpDebugData);
|
||||
}
|
||||
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
for (int i = 0; i < _warpDebugData.Count; i++)
|
||||
{
|
||||
var debugData = _warpDebugData[i];
|
||||
debugData.onDrawGizmos?.Invoke();
|
||||
if(debugData.duration < 0f) continue;
|
||||
|
||||
// Progress the timer.
|
||||
debugData.timer = Mathf.Clamp(debugData.timer + Time.deltaTime, 0f, debugData.duration);
|
||||
_warpDebugData[i] = debugData;
|
||||
|
||||
if (Mathf.Approximately(debugData.timer, debugData.duration))
|
||||
{
|
||||
_warpDebugData.RemoveAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a3164a26014538e45b7dbfa103dabef0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 100
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,53 @@
|
||||
// Designed by KINEMATION, 2024.
|
||||
|
||||
using Kinemation.MotionWarping.Runtime.Utility;
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Kinemation.MotionWarping.Runtime.Core
|
||||
{
|
||||
[CreateAssetMenu(menuName = "KINEMATION/MotionWarping/WarpingAsset", fileName = "NewWarpingAsset", order = 0)]
|
||||
public class MotionWarpingAsset : ScriptableObject
|
||||
{
|
||||
[Header("Animation")]
|
||||
public AnimationClip animation;
|
||||
|
||||
public AnimationCurve rootX;
|
||||
public AnimationCurve rootY;
|
||||
public AnimationCurve rootZ;
|
||||
|
||||
public VectorBool useLinear = VectorBool.Enabled;
|
||||
public VectorBool useAnimation = VectorBool.Enabled;
|
||||
public VectorBool useWarping = VectorBool.Enabled;
|
||||
|
||||
[Tooltip("If true, CharacterController or a RigidBody will be moved with physics enabled.")]
|
||||
public bool useCollision = false;
|
||||
|
||||
[Range(0f, 2f)] public float playRateBasis = 1f;
|
||||
|
||||
[Range(1, 10)] public int phasesAmount = 1;
|
||||
public List<WarpPhase> warpPhases = new List<WarpPhase>();
|
||||
|
||||
public Vector3 GetVectorValue(float time)
|
||||
{
|
||||
if (rootX == null || rootY == null || rootZ == null)
|
||||
{
|
||||
Debug.LogError(name + ": null reference curve!");
|
||||
return Vector3.zero;
|
||||
}
|
||||
|
||||
return new Vector3(rootX.Evaluate(time), rootY.Evaluate(time), rootZ.Evaluate(time));
|
||||
}
|
||||
|
||||
public float GetLength()
|
||||
{
|
||||
if (animation == null)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
return animation.length;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 928e2ecfbd1ec0f469e1838b49a4dda9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,96 @@
|
||||
// Designed by KINEMATION, 2024.
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace KINEMATION.MotionWarping.Runtime.Core
|
||||
{
|
||||
[Serializable]
|
||||
public struct LockState
|
||||
{
|
||||
public string controlCurveName;
|
||||
public Transform boneReference;
|
||||
[Min(0f)] public float interpSpeed;
|
||||
[NonSerialized] public float Weight;
|
||||
[NonSerialized] public Quaternion LockRotation;
|
||||
[NonSerialized] public Vector3 LockPosition;
|
||||
[NonSerialized] public bool IsLocked;
|
||||
[NonSerialized] public bool Interpolate;
|
||||
}
|
||||
|
||||
public class MotionWarpingIk : MonoBehaviour
|
||||
{
|
||||
[SerializeField] [Range(0f, 1f)] private float componentWeight = 1f;
|
||||
|
||||
[SerializeField] private LockState rightHandLockState;
|
||||
[SerializeField] private LockState leftHandLockState;
|
||||
[SerializeField] private LockState rightFootLockState;
|
||||
[SerializeField] private LockState leftFootLockState;
|
||||
|
||||
private Animator _animator;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_animator = GetComponent<Animator>();
|
||||
}
|
||||
|
||||
// Updated the current bone lock state.
|
||||
private void UpdateBoneLock(ref LockState lockState)
|
||||
{
|
||||
Transform boneReference = lockState.boneReference;
|
||||
if (boneReference == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float weight = _animator.GetFloat(lockState.controlCurveName);
|
||||
|
||||
if (!lockState.IsLocked && Mathf.Approximately(weight, 1f))
|
||||
{
|
||||
lockState.LockPosition = boneReference.position;
|
||||
lockState.LockRotation = boneReference.rotation;
|
||||
lockState.IsLocked = true;
|
||||
lockState.Interpolate = false;
|
||||
lockState.Weight = 1f;
|
||||
return;
|
||||
}
|
||||
|
||||
if (lockState.IsLocked && Mathf.Approximately(weight, 0f))
|
||||
{
|
||||
lockState.IsLocked = false;
|
||||
lockState.Interpolate = true;
|
||||
}
|
||||
|
||||
if (!lockState.Interpolate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float alpha = 1f - Mathf.Exp(-lockState.interpSpeed * Time.deltaTime);
|
||||
lockState.Weight = Mathf.Lerp(lockState.Weight, 0f, alpha);
|
||||
}
|
||||
|
||||
private void ApplyLock(ref LockState lockState)
|
||||
{
|
||||
Transform tip = lockState.boneReference;
|
||||
if (tip == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Transform mid = tip.parent;
|
||||
TwoBoneIk.SolveTwoBoneIK(mid.parent, mid, tip, (lockState.LockPosition, lockState.LockRotation),
|
||||
mid, lockState.Weight * componentWeight, 1f);
|
||||
|
||||
UpdateBoneLock(ref lockState);
|
||||
}
|
||||
|
||||
public void ApplyIK()
|
||||
{
|
||||
ApplyLock(ref rightHandLockState);
|
||||
ApplyLock(ref leftHandLockState);
|
||||
ApplyLock(ref rightFootLockState);
|
||||
ApplyLock(ref leftFootLockState);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f2c56b0df3354ea4a3ab76ef0923d868
|
||||
timeCreated: 1708838078
|
@@ -0,0 +1,142 @@
|
||||
// Designed by KINEMATION, 2024.
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace KINEMATION.MotionWarping.Runtime.Core
|
||||
{
|
||||
public class TwoBoneIk
|
||||
{
|
||||
private const float FloatMin = 1e-10f;
|
||||
private const float SqrEpsilon = 1e-8f;
|
||||
|
||||
public static float TriangleAngle(float aLen, float aLen1, float aLen2)
|
||||
{
|
||||
float c = Mathf.Clamp((aLen1 * aLen1 + aLen2 * aLen2 - aLen * aLen) / (aLen1 * aLen2) / 2.0f, -1.0f, 1.0f);
|
||||
return Mathf.Acos(c);
|
||||
}
|
||||
|
||||
public static Quaternion FromToRotation(Vector3 from, Vector3 to)
|
||||
{
|
||||
float theta = Vector3.Dot(from.normalized, to.normalized);
|
||||
if (theta >= 1f)
|
||||
return Quaternion.identity;
|
||||
|
||||
if (theta <= -1f)
|
||||
{
|
||||
Vector3 axis = Vector3.Cross(from, Vector3.right);
|
||||
if (axis.sqrMagnitude == 0f)
|
||||
axis = Vector3.Cross(from, Vector3.up);
|
||||
|
||||
return Quaternion.AngleAxis(180f, axis);
|
||||
}
|
||||
|
||||
return Quaternion.AngleAxis(Mathf.Acos(theta) * Mathf.Rad2Deg, Vector3.Cross(from, to).normalized);
|
||||
}
|
||||
|
||||
public static Quaternion NormalizeSafe(Quaternion q)
|
||||
{
|
||||
float dot = Quaternion.Dot(q, q);
|
||||
if (dot > FloatMin)
|
||||
{
|
||||
float rsqrt = 1.0f / Mathf.Sqrt(dot);
|
||||
return new Quaternion(q.x * rsqrt, q.y * rsqrt, q.z * rsqrt, q.w * rsqrt);
|
||||
}
|
||||
|
||||
return Quaternion.identity;
|
||||
}
|
||||
|
||||
public static void SolveTwoBoneIK(
|
||||
Transform root,
|
||||
Transform mid,
|
||||
Transform tip,
|
||||
(Vector3, Quaternion) target,
|
||||
Transform hint,
|
||||
float targetWeight,
|
||||
float hintWeight
|
||||
)
|
||||
{
|
||||
if (Mathf.Approximately(targetWeight, 0f))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 aPosition = root.position;
|
||||
Vector3 bPosition = mid.position;
|
||||
Vector3 cPosition = tip.position;
|
||||
Vector3 tPosition = Vector3.Lerp(cPosition, target.Item1, targetWeight);
|
||||
Quaternion tRotation = Quaternion.Lerp(tip.rotation, target.Item2, targetWeight);
|
||||
bool hasHint = hint != null && hintWeight > 0f;
|
||||
|
||||
Vector3 ab = bPosition - aPosition;
|
||||
Vector3 bc = cPosition - bPosition;
|
||||
Vector3 ac = cPosition - aPosition;
|
||||
Vector3 at = tPosition - aPosition;
|
||||
|
||||
float abLen = ab.magnitude;
|
||||
float bcLen = bc.magnitude;
|
||||
float acLen = ac.magnitude;
|
||||
float atLen = at.magnitude;
|
||||
|
||||
float oldAbcAngle = TriangleAngle(acLen, abLen, bcLen);
|
||||
float newAbcAngle = TriangleAngle(atLen, abLen, bcLen);
|
||||
|
||||
// Bend normal strategy is to take whatever has been provided in the animation
|
||||
// stream to minimize configuration changes, however if this is collinear
|
||||
// try computing a bend normal given the desired target position.
|
||||
// If this also fails, try resolving axis using hint if provided.
|
||||
Vector3 axis = Vector3.Cross(ab, bc);
|
||||
if (axis.sqrMagnitude < SqrEpsilon)
|
||||
{
|
||||
axis = hasHint ? Vector3.Cross(hint.position - aPosition, bc) : Vector3.zero;
|
||||
|
||||
if (axis.sqrMagnitude < SqrEpsilon)
|
||||
axis = Vector3.Cross(at, bc);
|
||||
|
||||
if (axis.sqrMagnitude < SqrEpsilon)
|
||||
axis = Vector3.up;
|
||||
}
|
||||
|
||||
axis = Vector3.Normalize(axis);
|
||||
|
||||
float a = 0.5f * (oldAbcAngle - newAbcAngle);
|
||||
float sin = Mathf.Sin(a);
|
||||
float cos = Mathf.Cos(a);
|
||||
Quaternion deltaR = new Quaternion(axis.x * sin, axis.y * sin, axis.z * sin, cos);
|
||||
mid.rotation = deltaR * mid.rotation;
|
||||
|
||||
cPosition = tip.position;
|
||||
ac = cPosition - aPosition;
|
||||
root.rotation = FromToRotation(ac, at) * root.rotation;
|
||||
|
||||
if (hasHint)
|
||||
{
|
||||
float acSqrMag = ac.sqrMagnitude;
|
||||
if (acSqrMag > 0f)
|
||||
{
|
||||
bPosition = mid.position;
|
||||
cPosition = tip.position;
|
||||
ab = bPosition - aPosition;
|
||||
ac = cPosition - aPosition;
|
||||
|
||||
Vector3 acNorm = ac / Mathf.Sqrt(acSqrMag);
|
||||
Vector3 ah = hint.position - aPosition;
|
||||
Vector3 abProj = ab - acNorm * Vector3.Dot(ab, acNorm);
|
||||
Vector3 ahProj = ah - acNorm * Vector3.Dot(ah, acNorm);
|
||||
|
||||
float maxReach = abLen + bcLen;
|
||||
if (abProj.sqrMagnitude > (maxReach * maxReach * 0.001f) && ahProj.sqrMagnitude > 0f)
|
||||
{
|
||||
Quaternion hintR = FromToRotation(abProj, ahProj);
|
||||
hintR.x *= hintWeight;
|
||||
hintR.y *= hintWeight;
|
||||
hintR.z *= hintWeight;
|
||||
hintR = NormalizeSafe(hintR);
|
||||
root.rotation = hintR * root.rotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tip.rotation = tRotation;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9637a7f6138e4e12a4db8b42a5c0738a
|
||||
timeCreated: 1708838183
|
@@ -0,0 +1,62 @@
|
||||
// Designed by KINEMATION, 2024.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Kinemation.MotionWarping.Runtime.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Kinemation.MotionWarping.Runtime.Core
|
||||
{
|
||||
// Obstacle with pre-defined Warp Points
|
||||
public class WarpObstacle : MonoBehaviour, IWarpPointProvider
|
||||
{
|
||||
[SerializeField] private MotionWarpingAsset motionWarpingAsset;
|
||||
[SerializeField] private List<Transform> points = new List<Transform>();
|
||||
[SerializeField] private bool useTransforms = false;
|
||||
[SerializeField] private bool drawDebugPoints = false;
|
||||
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
if (!drawDebugPoints) return;
|
||||
|
||||
var color = Gizmos.color;
|
||||
Gizmos.color = Color.green;
|
||||
|
||||
foreach (var point in points)
|
||||
{
|
||||
if(point == null) continue;
|
||||
Gizmos.DrawWireSphere(point.position, 0.07f);
|
||||
}
|
||||
|
||||
Gizmos.color = color;
|
||||
}
|
||||
|
||||
public WarpInteractionResult Interact(GameObject instigator)
|
||||
{
|
||||
WarpInteractionResult result;
|
||||
result.points = new WarpPoint[points.Count];
|
||||
result.asset = motionWarpingAsset;
|
||||
result.success = points.Count > 0;
|
||||
|
||||
for (int i = 0; i < result.points.Length; i++)
|
||||
{
|
||||
if (useTransforms)
|
||||
{
|
||||
result.points[i] = new WarpPoint()
|
||||
{
|
||||
transform = points[i]
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
result.points[i] = new WarpPoint()
|
||||
{
|
||||
position = points[i].position,
|
||||
rotation = points[i].rotation
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ceff3fd151a844759bf3cc1490295ef7
|
||||
timeCreated: 1694707273
|
@@ -0,0 +1,24 @@
|
||||
// Designed by KINEMATION, 2024.
|
||||
|
||||
using Kinemation.MotionWarping.Runtime.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Kinemation.MotionWarping.Runtime.Core
|
||||
{
|
||||
public struct WarpInteractionResult
|
||||
{
|
||||
public WarpPoint[] points;
|
||||
public MotionWarpingAsset asset;
|
||||
public bool success;
|
||||
|
||||
public bool IsValid()
|
||||
{
|
||||
return success && points != null && asset != null;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IWarpPointProvider
|
||||
{
|
||||
public WarpInteractionResult Interact(GameObject instigator);
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af4b4f93ff874248959946ba0a83a8ea
|
||||
timeCreated: 1699699303
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5f330a55deff4a9e9e9ff0379a74eb82
|
||||
timeCreated: 1695198325
|
@@ -0,0 +1,94 @@
|
||||
// Designed by KINEMATION, 2024.
|
||||
|
||||
using Kinemation.MotionWarping.Runtime.Core;
|
||||
using Kinemation.MotionWarping.Runtime.Utility;
|
||||
|
||||
using UnityEngine;
|
||||
using Quaternion = UnityEngine.Quaternion;
|
||||
using Vector3 = UnityEngine.Vector3;
|
||||
|
||||
namespace Kinemation.MotionWarping.Runtime.Examples
|
||||
{
|
||||
public class AlignComponent : MonoBehaviour, IWarpPointProvider
|
||||
{
|
||||
[SerializeField] [Range(0f, 180f)] private float interactionAngle = 0f;
|
||||
[SerializeField] [Range(-180f, 180f)] private float offsetAngle = 0f;
|
||||
[SerializeField] [Min(0f)] private float distance = 0f;
|
||||
[SerializeField] private MotionWarpingAsset motionWarpingAsset;
|
||||
[SerializeField] private string targetAnimName;
|
||||
|
||||
private Animator _animator;
|
||||
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
Gizmos.color = Color.red;
|
||||
|
||||
Vector3 position = transform.position;
|
||||
Vector3 forward = Quaternion.Euler(0f, offsetAngle, 0f) * transform.forward;
|
||||
float debugRadius = 0.05f;
|
||||
|
||||
Vector3 left = Quaternion.Euler(0f, interactionAngle, 0f) * forward;
|
||||
Vector3 right = Quaternion.Euler(0f, -interactionAngle, 0f) * forward;
|
||||
|
||||
Gizmos.DrawLine(position, position + left * distance);
|
||||
Gizmos.DrawLine(position, position + right * distance);
|
||||
|
||||
Gizmos.DrawWireSphere(position, debugRadius);
|
||||
Gizmos.DrawWireSphere(position + left * distance, debugRadius);
|
||||
Gizmos.DrawWireSphere(position + right * distance, debugRadius);
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_animator = GetComponent<Animator>();
|
||||
}
|
||||
|
||||
public WarpInteractionResult Interact(GameObject instigator)
|
||||
{
|
||||
WarpInteractionResult result = new WarpInteractionResult()
|
||||
{
|
||||
success = false,
|
||||
points = null,
|
||||
asset = null,
|
||||
};
|
||||
|
||||
if (instigator == null || motionWarpingAsset == null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if ((instigator.transform.position - transform.position).magnitude > distance)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector3 forward = Quaternion.Euler(0f, offsetAngle, 0f) * transform.forward;
|
||||
float angle = Mathf.Acos(Vector3.Dot(-instigator.transform.forward, forward)) * Mathf.Rad2Deg;
|
||||
|
||||
if (angle > interactionAngle)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result.asset = motionWarpingAsset;
|
||||
result.points = new[]
|
||||
{
|
||||
new WarpPoint()
|
||||
{
|
||||
transform = this.transform,
|
||||
position = Vector3.zero,
|
||||
rotation = Quaternion.identity
|
||||
}
|
||||
};
|
||||
|
||||
result.success = true;
|
||||
|
||||
if (_animator != null)
|
||||
{
|
||||
_animator.CrossFade(targetAnimName, 0.15f);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cce7418bf5c1f1f4b85e97a5470f0bd5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,137 @@
|
||||
// Designed by KINEMATION, 2024.
|
||||
|
||||
using Kinemation.MotionWarping.Runtime.Core;
|
||||
using Kinemation.MotionWarping.Runtime.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Kinemation.MotionWarping.Runtime.Examples
|
||||
{
|
||||
public class HangComponent : MonoBehaviour, IWarpPointProvider
|
||||
{
|
||||
[SerializeField] private MotionWarpingAsset hangAsset;
|
||||
[SerializeField] private MotionWarpingAsset jumpDownAsset;
|
||||
|
||||
[Header("Trace Settings")]
|
||||
[SerializeField] [Min(0f)] private float capsuleRadius;
|
||||
[SerializeField] [Min(0f)] private float sphereCheckRadius;
|
||||
[SerializeField] [Min(0f)] private float minLedgeLength;
|
||||
[SerializeField] [Min(0f)] private LayerMask layerMask;
|
||||
|
||||
[Header("Hanging")]
|
||||
[SerializeField] [Min(0f)] private float maxAllowedDistance;
|
||||
[SerializeField] [Min(0f)] private float maxAllowedHeight;
|
||||
[SerializeField] [Min(0f)] private float minAllowedHeight;
|
||||
|
||||
private bool _isHanging;
|
||||
|
||||
private WarpInteractionResult TryToJumpDown()
|
||||
{
|
||||
WarpInteractionResult result = new WarpInteractionResult()
|
||||
{
|
||||
success = false,
|
||||
points = null,
|
||||
asset = null,
|
||||
};
|
||||
|
||||
Vector3 start = transform.position;
|
||||
bool bHit = Physics.SphereCast(start, sphereCheckRadius, -transform.up, out var hit,
|
||||
maxAllowedHeight, layerMask);
|
||||
|
||||
if (!bHit)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
Quaternion resultRotation = transform.rotation;
|
||||
Vector3 resultPosition = hit.point;
|
||||
|
||||
result.points = new[]
|
||||
{
|
||||
new WarpPoint()
|
||||
{
|
||||
position = resultPosition,
|
||||
rotation = resultRotation
|
||||
}
|
||||
};
|
||||
result.asset = jumpDownAsset;
|
||||
result.success = true;
|
||||
|
||||
_isHanging = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
private WarpInteractionResult TryToHang()
|
||||
{
|
||||
WarpInteractionResult result = new WarpInteractionResult()
|
||||
{
|
||||
success = false,
|
||||
points = null,
|
||||
asset = null,
|
||||
};
|
||||
|
||||
Vector3 start = transform.position;
|
||||
Vector3 end = start;
|
||||
|
||||
start.y += minAllowedHeight;
|
||||
end.y += minAllowedHeight + maxAllowedHeight;
|
||||
|
||||
Vector3 direction = transform.forward;
|
||||
float distance = maxAllowedDistance;
|
||||
|
||||
bool bHit = Physics.CapsuleCast(start, end, capsuleRadius, direction,
|
||||
out var hit, distance, layerMask);
|
||||
|
||||
if (!bHit)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
Quaternion targetRotation = Quaternion.LookRotation(-hit.normal, transform.up);
|
||||
|
||||
distance = (end - start).magnitude;
|
||||
|
||||
start = hit.point;
|
||||
start.y = end.y;
|
||||
|
||||
bHit = Physics.SphereCast(start, sphereCheckRadius, -transform.up, out hit, distance,
|
||||
layerMask);
|
||||
|
||||
if (!bHit)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
start = hit.point;
|
||||
start.y += sphereCheckRadius + 0.01f;
|
||||
|
||||
Vector3 targetPosition = hit.point;
|
||||
|
||||
bHit = Physics.SphereCast(start, sphereCheckRadius, targetRotation * Vector3.forward, out hit,
|
||||
minLedgeLength, layerMask);
|
||||
|
||||
if (bHit)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result.success = true;
|
||||
result.asset = hangAsset;
|
||||
result.points = new[]
|
||||
{
|
||||
new WarpPoint()
|
||||
{
|
||||
position = targetPosition,
|
||||
rotation = targetRotation
|
||||
}
|
||||
};
|
||||
|
||||
_isHanging = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
public WarpInteractionResult Interact(GameObject instigator)
|
||||
{
|
||||
return _isHanging ? TryToJumpDown() : TryToHang();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 18c8d51c4bfa48c69c050aa993b9b441
|
||||
timeCreated: 1705657565
|
@@ -0,0 +1,131 @@
|
||||
// Designed by KINEMATION, 2024.
|
||||
|
||||
using Kinemation.MotionWarping.Runtime.Core;
|
||||
using Kinemation.MotionWarping.Runtime.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace Kinemation.MotionWarping.Runtime.Examples
|
||||
{
|
||||
public class MantleComponent : MonoBehaviour, IWarpPointProvider
|
||||
{
|
||||
[SerializeField] private MotionWarpingAsset mantleHigh;
|
||||
[SerializeField] private MotionWarpingAsset mantleLow;
|
||||
[SerializeField] private MantleSettings settings;
|
||||
|
||||
private Vector3 _targetPosition;
|
||||
private Quaternion _targetRotation;
|
||||
|
||||
public WarpInteractionResult Interact(GameObject instigator)
|
||||
{
|
||||
WarpInteractionResult result = new WarpInteractionResult()
|
||||
{
|
||||
points = null,
|
||||
asset = null,
|
||||
success = false
|
||||
};
|
||||
|
||||
if (settings == null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var motionWarping = instigator.GetComponent<Core.MotionWarping>();
|
||||
|
||||
Vector3 start = transform.position;
|
||||
Vector3 end = start;
|
||||
|
||||
start.y += settings.minHeight + settings.characterCapsuleRadius;
|
||||
end.y += settings.maxHeight;
|
||||
|
||||
Vector3 direction = transform.forward;
|
||||
float distance = settings.maxDistance;
|
||||
|
||||
bool bHit = Physics.CapsuleCast(start, end, settings.characterCapsuleRadius, direction,
|
||||
out var hit, distance, settings.layerMask);
|
||||
|
||||
if (!bHit)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
_targetRotation = Quaternion.LookRotation(-hit.normal, transform.up);
|
||||
|
||||
distance = (end - start).magnitude;
|
||||
|
||||
start = hit.point;
|
||||
start += (_targetRotation * Vector3.forward) * settings.forwardOffset;
|
||||
|
||||
start.y = end.y;
|
||||
|
||||
bHit = Physics.SphereCast(start, settings.sphereEdgeCheckRadius, -transform.up, out hit,
|
||||
distance, settings.layerMask);
|
||||
|
||||
start = hit.point;
|
||||
|
||||
if (!bHit)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector3 surfaceNormal = hit.normal;
|
||||
|
||||
start += surfaceNormal * (0.02f + settings.characterCapsuleRadius);
|
||||
end = start + surfaceNormal * settings.characterCapsuleHeight;
|
||||
|
||||
bHit = Physics.CheckCapsule(start, end, settings.characterCapsuleRadius, settings.layerMask);
|
||||
|
||||
if (bHit)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
float surfaceIncline = Mathf.Clamp(Vector3.Dot(transform.up, surfaceNormal), -1f, 1f);
|
||||
surfaceIncline = Mathf.Acos(surfaceIncline) * Mathf.Rad2Deg;
|
||||
|
||||
if (surfaceIncline > settings.maxSurfaceInclineAngle)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector3 forwardVector = _targetRotation * Vector3.forward;
|
||||
_targetRotation = Quaternion.LookRotation(forwardVector);
|
||||
_targetPosition = hit.point;
|
||||
|
||||
result.points = new[]
|
||||
{
|
||||
new WarpPoint()
|
||||
{
|
||||
transform = hit.transform,
|
||||
position = hit.transform.InverseTransformPoint(_targetPosition),
|
||||
rotation = Quaternion.Inverse(hit.transform.rotation) * _targetRotation
|
||||
}
|
||||
};
|
||||
|
||||
float height = _targetPosition.y - transform.position.y;
|
||||
|
||||
result.asset = height > settings.lowHeight ? mantleHigh : mantleLow;
|
||||
result.success = true;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
Core.MotionWarping.AddWarpDebugData(motionWarping, new WarpDebugData()
|
||||
{
|
||||
duration = 5f,
|
||||
onDrawGizmos = () =>
|
||||
{
|
||||
var color = Gizmos.color;
|
||||
Gizmos.color = Color.green;
|
||||
Gizmos.DrawWireSphere(_targetPosition, 0.1f);
|
||||
Handles.Label(_targetPosition, "Mantle Target Point");
|
||||
Gizmos.color = color;
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b86a5418587f4d5c9107dabe449506cb
|
||||
timeCreated: 1695198402
|
@@ -0,0 +1,25 @@
|
||||
// Designed by KINEMATION, 2024
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Kinemation.MotionWarping.Runtime.Examples
|
||||
{
|
||||
[CreateAssetMenu(menuName = "KINEMATION/MotionWarping/MantleSettings", fileName = "NewMantleSettings", order = 2)]
|
||||
public class MantleSettings : ScriptableObject
|
||||
{
|
||||
public LayerMask layerMask;
|
||||
|
||||
[Min(0f)] public float maxHeight;
|
||||
[Min(0f)] public float lowHeight;
|
||||
[Min(0f)] public float minHeight;
|
||||
[Min(0f)] public float maxDistance;
|
||||
|
||||
[Min(0f)] public float characterCapsuleRadius;
|
||||
[Min(0f)] public float characterCapsuleHeight;
|
||||
[Min(0f)] public float sphereEdgeCheckRadius;
|
||||
|
||||
[Range(0f, 90f)] public float maxSurfaceInclineAngle;
|
||||
|
||||
[Min(0f)] public float forwardOffset;
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c6eaf18cf8aa0654d98c494153a7b6e8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,183 @@
|
||||
// Designed by KINEMATION, 2024.
|
||||
|
||||
using Kinemation.MotionWarping.Runtime.Core;
|
||||
using Kinemation.MotionWarping.Runtime.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace Kinemation.MotionWarping.Runtime.Examples
|
||||
{
|
||||
public class VaultComponent : MonoBehaviour, IWarpPointProvider
|
||||
{
|
||||
[SerializeField] private MotionWarpingAsset motionWarpingAsset;
|
||||
[SerializeField] private VaultSettings settings;
|
||||
|
||||
private Vector3 _closeEdge;
|
||||
private Vector3 _farEdge;
|
||||
private Vector3 _endPoint;
|
||||
private Quaternion _targetRotation;
|
||||
|
||||
private bool FindCloseEdge()
|
||||
{
|
||||
if (settings == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector3 start = transform.position;
|
||||
Vector3 end = start + transform.up * settings.maxAllowedStartHeight;
|
||||
start.y += settings.characterCapsuleRadius + settings.minAllowedStartHeight;
|
||||
|
||||
Vector3 direction = transform.forward;
|
||||
|
||||
bool bHit = Physics.CapsuleCast(start, end, settings.characterCapsuleRadius, direction,
|
||||
out var hit, settings.maxAllowedStartLength, settings.layerMask);
|
||||
|
||||
if (!bHit)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_targetRotation = Quaternion.LookRotation(-hit.normal, transform.up);
|
||||
|
||||
start = hit.point;
|
||||
start.y = end.y;
|
||||
direction = -transform.up;
|
||||
|
||||
bHit = Physics.SphereCast(start, settings.sphereEdgeCheckRadius, direction, out hit,
|
||||
settings.maxAllowedStartHeight, settings.layerMask);
|
||||
|
||||
if (!bHit)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_closeEdge = hit.point;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool FindEndPoint()
|
||||
{
|
||||
Vector3 start = _farEdge + (_targetRotation * Vector3.forward) * settings.farEdgeOffset;
|
||||
Vector3 direction = -transform.up;
|
||||
float distance = settings.maxAllowedEndHeight;
|
||||
|
||||
bool bHit = Physics.SphereCast(start, settings.sphereEdgeCheckRadius, direction, out var hit,
|
||||
distance, settings.layerMask);
|
||||
|
||||
if (!bHit || (hit.point - start).magnitude < settings.minAllowedEndHeight)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_endPoint = hit.point;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool FindEndEdge()
|
||||
{
|
||||
Vector3 forward = (_targetRotation * Vector3.forward).normalized;
|
||||
|
||||
float length = settings.maxObstacleLength + settings.characterCapsuleRadius;
|
||||
Vector3 start = _closeEdge + forward * length;
|
||||
Vector3 end = start;
|
||||
|
||||
start.y = _closeEdge.y - settings.closeEdgeDeviation;
|
||||
end.y = _closeEdge.y + settings.closeEdgeDeviation;
|
||||
|
||||
length -= settings.minObstacleLength;
|
||||
|
||||
bool bHit = Physics.CapsuleCast(start, end, settings.characterCapsuleRadius,
|
||||
-forward, out var hit, length, settings.layerMask);
|
||||
|
||||
if (!bHit) return false;
|
||||
|
||||
start = hit.point;
|
||||
start.y = _closeEdge.y + settings.closeEdgeDeviation + settings.sphereEdgeCheckRadius;
|
||||
|
||||
bHit = Physics.SphereCast(start, settings.sphereEdgeCheckRadius, -transform.up, out hit,
|
||||
settings.closeEdgeDeviation * 2f, settings.layerMask);
|
||||
|
||||
if (!bHit) return false;
|
||||
|
||||
_farEdge = hit.point;
|
||||
return true;
|
||||
}
|
||||
|
||||
public WarpInteractionResult Interact(GameObject instigator)
|
||||
{
|
||||
WarpInteractionResult result = new WarpInteractionResult()
|
||||
{
|
||||
points = null,
|
||||
asset = null,
|
||||
success = false
|
||||
};
|
||||
|
||||
if (motionWarpingAsset == null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var motionWarping = instigator.GetComponent<Core.MotionWarping>();
|
||||
|
||||
bool success = FindCloseEdge() && FindEndEdge() && FindEndPoint();
|
||||
|
||||
if (!success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result.asset = motionWarpingAsset;
|
||||
|
||||
result.points = new WarpPoint[]
|
||||
{
|
||||
new WarpPoint()
|
||||
{
|
||||
position = _closeEdge,
|
||||
rotation = _targetRotation
|
||||
},
|
||||
new WarpPoint()
|
||||
{
|
||||
position = _farEdge,
|
||||
rotation = _targetRotation
|
||||
},
|
||||
new WarpPoint()
|
||||
{
|
||||
position = _endPoint,
|
||||
rotation = _targetRotation
|
||||
}
|
||||
};
|
||||
|
||||
result.success = true;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
Core.MotionWarping.AddWarpDebugData(motionWarping, new WarpDebugData()
|
||||
{
|
||||
duration = 5f,
|
||||
onDrawGizmos = () =>
|
||||
{
|
||||
var color = Gizmos.color;
|
||||
|
||||
Gizmos.color = Color.green;
|
||||
Gizmos.DrawWireSphere(_closeEdge, 0.1f);
|
||||
Handles.Label(_closeEdge, "Close Edge");
|
||||
|
||||
Gizmos.DrawWireSphere(_farEdge, 0.1f);
|
||||
Handles.Label(_farEdge, "Far Edge");
|
||||
|
||||
Gizmos.DrawWireSphere(_endPoint, 0.1f);
|
||||
Handles.Label(_endPoint, "End Point");
|
||||
|
||||
Gizmos.color = color;
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 854419404bdc4b2a9990c6557b00e445
|
||||
timeCreated: 1695198460
|
@@ -0,0 +1,30 @@
|
||||
// Designed by KINEMATION, 2024
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Kinemation.MotionWarping.Runtime.Examples
|
||||
{
|
||||
[CreateAssetMenu(menuName = "KINEMATION/MotionWarping/VaultSettings", fileName = "New VaultSettings", order = 1)]
|
||||
public class VaultSettings : ScriptableObject
|
||||
{
|
||||
[Header("General Settings")]
|
||||
public LayerMask layerMask;
|
||||
[Min(0f)] public float characterCapsuleRadius;
|
||||
[Min(0f)] public float maxObstacleLength;
|
||||
[Min(0f)] public float minObstacleLength;
|
||||
[Min(0f)] public float sphereEdgeCheckRadius;
|
||||
|
||||
[Header("Close Edge Check")]
|
||||
[Min(0f)] public float maxAllowedStartLength;
|
||||
[Min(0f)] public float maxAllowedStartHeight;
|
||||
[Min(0f)] public float minAllowedStartHeight;
|
||||
|
||||
[Header("Far Edge Check")]
|
||||
[Min(0f)] public float closeEdgeDeviation;
|
||||
|
||||
[Header("End Check")]
|
||||
[Min(0f)] public float farEdgeOffset;
|
||||
[Min(0f)] public float maxAllowedEndHeight;
|
||||
[Min(0f)] public float minAllowedEndHeight;
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16c489fe43e24410b556466ad9c96890
|
||||
timeCreated: 1695738291
|
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"name": "MotionWarping.Runtime"
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4c36dcf6d8eeb6d44ab73ef966534b46
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 618709f05a7d454696422458ea36d4b5
|
||||
timeCreated: 1695456982
|
@@ -0,0 +1,10 @@
|
||||
// Designed by KINEMATION, 2024
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Kinemation.MotionWarping.Runtime.Utility
|
||||
{
|
||||
public class ReadOnlyAttribute : PropertyAttribute
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6cf5c593ae6f4fc98d24284c067a5514
|
||||
timeCreated: 1700464610
|
@@ -0,0 +1,134 @@
|
||||
// Designed by KINEMATION, 2024
|
||||
|
||||
using System;
|
||||
using KINEMATION.MotionWarping.Runtime.Core;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Kinemation.MotionWarping.Runtime.Utility
|
||||
{
|
||||
public struct WarpDebugData
|
||||
{
|
||||
public float duration;
|
||||
public float timer;
|
||||
public Action onDrawGizmos;
|
||||
}
|
||||
|
||||
public struct WarpPoint
|
||||
{
|
||||
// Target transform in world space
|
||||
public Transform transform;
|
||||
public Vector3 localPosition;
|
||||
public Vector3 localRotation;
|
||||
|
||||
// Target position in world space
|
||||
public Vector3 position;
|
||||
|
||||
// Target rotation in world space
|
||||
public Quaternion rotation;
|
||||
|
||||
public WarpPoint(Transform transform)
|
||||
{
|
||||
position = transform.position;
|
||||
rotation = transform.rotation;
|
||||
localPosition = localRotation = Vector3.zero;
|
||||
this.transform = null;
|
||||
}
|
||||
|
||||
public Vector3 GetPosition()
|
||||
{
|
||||
if (transform == null)
|
||||
{
|
||||
return position;
|
||||
}
|
||||
|
||||
// Get the raw warp point in world space.
|
||||
Vector3 rawPosition = transform.TransformPoint(position);
|
||||
Quaternion rawRotation = transform.rotation * rotation;
|
||||
|
||||
return WarpingUtility.ToWorld(rawPosition, rawRotation, localPosition);
|
||||
}
|
||||
|
||||
public Quaternion GetRotation()
|
||||
{
|
||||
if (transform == null)
|
||||
{
|
||||
return rotation;
|
||||
}
|
||||
|
||||
return transform.rotation * rotation * Quaternion.Euler(localRotation);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct WarpPhase
|
||||
{
|
||||
// Target point
|
||||
public WarpPoint Target;
|
||||
|
||||
// Translation offset for the B point
|
||||
public Vector3 tOffset;
|
||||
|
||||
// Angular offset for the B point
|
||||
public Vector3 rOffset;
|
||||
|
||||
[Min(0f)] public float startTime;
|
||||
[Min(0f)] public float endTime;
|
||||
|
||||
// Min allowed play rate
|
||||
[Range(0f, 1f)] public float minRate;
|
||||
|
||||
// Max allowed play rate
|
||||
[Range(1f, 2f)] public float maxRate;
|
||||
|
||||
[ReadOnly] public Vector3 totalRootMotion;
|
||||
}
|
||||
|
||||
public struct WarpingCurve
|
||||
{
|
||||
public AnimationCurve X;
|
||||
public AnimationCurve Y;
|
||||
public AnimationCurve Z;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct VectorBool
|
||||
{
|
||||
public bool x;
|
||||
public bool y;
|
||||
public bool z;
|
||||
|
||||
public VectorBool(bool x, bool y, bool z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public static VectorBool Enabled = new VectorBool(true, true, true);
|
||||
}
|
||||
|
||||
public static class WarpingUtility
|
||||
{
|
||||
public static float ExpDecayAlpha(float speed, float deltaTime)
|
||||
{
|
||||
return 1 - Mathf.Exp(-speed * deltaTime);
|
||||
}
|
||||
|
||||
public static Vector3 ToWorld(Vector3 tWorld, Quaternion rWorld, Vector3 localOffset)
|
||||
{
|
||||
return tWorld + rWorld * localOffset;
|
||||
}
|
||||
|
||||
public static Vector3 ToLocal(Vector3 tWorld, Quaternion rWorld, Vector3 worldOffset)
|
||||
{
|
||||
return Quaternion.Inverse(rWorld) * (worldOffset - tWorld);
|
||||
}
|
||||
|
||||
public static void SolveTwoBoneIK(Transform bone, Transform target, float weight)
|
||||
{
|
||||
Transform mid = bone.parent;
|
||||
TwoBoneIk.SolveTwoBoneIK(mid.parent, mid, bone, (target.position, target.rotation), mid,
|
||||
weight, 1f);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c787d691183bbd0448a95349d4776b18
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user