导入leg插件,完成腿部动画
This commit is contained in:
@@ -0,0 +1,258 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace.AnimationTools
|
||||
{
|
||||
public static class AnimationGenerateUtils
|
||||
{
|
||||
|
||||
#region Curve Utilties
|
||||
|
||||
|
||||
public static AnimationCurve ReduceKeyframes(AnimationCurve curve, float maxError)
|
||||
{
|
||||
Keyframe[] keys = curve.keys;
|
||||
int i = 1;
|
||||
|
||||
while (keys.Length > 2 && i < keys.Length - 1)
|
||||
{
|
||||
Keyframe[] tempK = new Keyframe[keys.Length - 1];
|
||||
|
||||
int t = 0;
|
||||
|
||||
for (int k = 0; k < keys.Length; k++)
|
||||
if (i != k) { tempK[t] = new Keyframe(keys[k].time, keys[k].value, keys[k].inTangent, keys[k].outTangent); t++; }
|
||||
|
||||
AnimationCurve tempCurve = new AnimationCurve();
|
||||
tempCurve.keys = tempK;
|
||||
|
||||
float eval = Mathf.Abs(tempCurve.Evaluate(keys[i].time) - keys[i].value);
|
||||
float checkBack = keys[i].time + (keys[i - 1].time - keys[i].time) * 0.5f;
|
||||
float checkForward = keys[i].time + (keys[i + 1].time - keys[i].time) * 0.5f;
|
||||
|
||||
float previous = Mathf.Abs(tempCurve.Evaluate(checkBack) - curve.Evaluate(checkBack));
|
||||
float next = Mathf.Abs(tempCurve.Evaluate(checkForward) - curve.Evaluate(checkForward));
|
||||
|
||||
if (eval < maxError && previous < maxError && next < maxError) keys = tempK; else i++;
|
||||
}
|
||||
|
||||
return new AnimationCurve(keys);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void LoopCurve(ref AnimationCurve curve, bool averageBoth = false, float? endTime = null)
|
||||
{
|
||||
float key0Val = 0f;
|
||||
if (curve.keys.Length > 0) key0Val = curve.keys[0].value;
|
||||
|
||||
if (endTime == null) // Loop start and last keyframe
|
||||
{
|
||||
if (curve.keys.Length == 0)
|
||||
{
|
||||
curve.AddKey(new Keyframe(0f, key0Val));
|
||||
curve.AddKey(new Keyframe(1f, key0Val));
|
||||
return;
|
||||
}
|
||||
else if (curve.keys.Length == 1)
|
||||
{
|
||||
curve.AddKey(new Keyframe(Mathf.Max(1f, curve.keys[0].time + 0.5f), key0Val));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
float targetVal = key0Val;
|
||||
if (averageBoth) targetVal = Mathf.Lerp(key0Val, curve.keys[curve.keys.Length - 1].value, 0.5f);
|
||||
|
||||
curve.MoveKey(0, new Keyframe(curve.keys[0].time, targetVal));
|
||||
curve.MoveKey(curve.keys.Length - 1, new Keyframe(curve.keys[curve.keys.Length - 1].time, targetVal));
|
||||
}
|
||||
else // Shifting last key near to end or adding new key
|
||||
{
|
||||
float maxTime = endTime.Value;
|
||||
|
||||
if (curve.keys.Length == 0)
|
||||
{
|
||||
curve.AddKey(new Keyframe(0f, key0Val));
|
||||
curve.AddKey(new Keyframe(maxTime, key0Val));
|
||||
return;
|
||||
}
|
||||
else if (curve.keys.Length == 1)
|
||||
{
|
||||
curve.AddKey(new Keyframe(maxTime, key0Val));
|
||||
return;
|
||||
}
|
||||
|
||||
float targetVal = key0Val;
|
||||
if (averageBoth) targetVal = Mathf.Lerp(key0Val, curve.keys[curve.keys.Length - 1].value, 0.5f);
|
||||
|
||||
var key = curve.keys[curve.keys.Length - 1];
|
||||
float lastKeyTime = key.time;
|
||||
|
||||
if (lastKeyTime != maxTime)
|
||||
{
|
||||
if (lastKeyTime < maxTime)
|
||||
{
|
||||
if (maxTime - lastKeyTime < maxTime * 0.1f)
|
||||
{
|
||||
lastKeyTime = maxTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
curve.MoveKey(0, new Keyframe(curve.keys[0].time, targetVal));
|
||||
curve.MoveKey(curve.keys.Length - 1, new Keyframe(lastKeyTime, targetVal));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static void DistrubuteCurveOnTime(ref AnimationCurve curve, float startTime, float endTime)
|
||||
{
|
||||
float curveStart = curve.keys[0].time;
|
||||
float curveEnd = curve.keys[curve.keys.Length - 1].time;
|
||||
|
||||
Keyframe[] evalKeys = new Keyframe[curve.keys.Length];
|
||||
curve.keys.CopyTo(evalKeys, 0);
|
||||
AnimationCurve refCurve = new AnimationCurve(evalKeys);
|
||||
|
||||
while (curve.keys.Length > 0)
|
||||
{
|
||||
curve.RemoveKey(curve.keys.Length - 1);
|
||||
}
|
||||
|
||||
for (int k = 0; k < refCurve.keys.Length; k++)
|
||||
//for (int k = refCurve.keys.Length - 1; k >= 0; k--)
|
||||
//for (int k = refCurve.keys.Length - 1; k >= 0; k--)
|
||||
{
|
||||
Keyframe src = refCurve.keys[k];
|
||||
Keyframe newK = src;
|
||||
|
||||
newK.time = Mathf.Lerp(startTime, endTime, Mathf.InverseLerp(curveStart, curveEnd, src.time));
|
||||
curve.AddKey(newK);
|
||||
}
|
||||
|
||||
//for (int k = 0; k < curve.keys.Length; k++)
|
||||
//{
|
||||
// Keyframe src = curve.keys[k];
|
||||
// Keyframe newK = src;
|
||||
// newK.time = Mathf.Lerp(startTime, endTime, Mathf.InverseLerp(curve.keys[0].time, curve.keys[curve.keys.Length - 1].time, src.time));
|
||||
|
||||
// curve.MoveKey(k, newK);
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Coords Utilities
|
||||
|
||||
|
||||
public static Quaternion EnsureQuaternionContinuity(Quaternion latestRot, Quaternion targetRot, bool normalize = false)
|
||||
{
|
||||
Quaternion flipped = new Quaternion(-targetRot.x, -targetRot.y, -targetRot.z, -targetRot.w);
|
||||
|
||||
Quaternion midQ = new Quaternion
|
||||
(
|
||||
Mathf.LerpUnclamped(latestRot.x, targetRot.x, 0.5f),
|
||||
Mathf.LerpUnclamped(latestRot.y, targetRot.y, 0.5f),
|
||||
Mathf.LerpUnclamped(latestRot.z, targetRot.z, 0.5f),
|
||||
Mathf.LerpUnclamped(latestRot.w, targetRot.w, 0.5f)
|
||||
);
|
||||
|
||||
Quaternion midQFlipped = new Quaternion
|
||||
(
|
||||
Mathf.LerpUnclamped(latestRot.x, flipped.x, 0.5f),
|
||||
Mathf.LerpUnclamped(latestRot.y, flipped.y, 0.5f),
|
||||
Mathf.LerpUnclamped(latestRot.z, flipped.z, 0.5f),
|
||||
Mathf.LerpUnclamped(latestRot.w, flipped.w, 0.5f)
|
||||
);
|
||||
|
||||
float angle = Quaternion.Angle(latestRot, midQ);
|
||||
float angleTreshold = Quaternion.Angle(latestRot, midQFlipped);
|
||||
|
||||
if (normalize)
|
||||
return angleTreshold < angle ? flipped.normalized : targetRot.normalized;
|
||||
else
|
||||
return angleTreshold < angle ? flipped : targetRot;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Animator Extra Utilities
|
||||
|
||||
#if UNITY_EDITOR
|
||||
static UnityEditor.Animations.AnimatorController _ikHelperAnimController = null;
|
||||
public static UnityEditor.Animations.AnimatorController GetStoredHumanoidIKPreviousController { get { return _ikHelperAnimController; } }
|
||||
//static RuntimeAnimatorController _ikHelperAnimRController = null;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Applying unity humanoid IK on the scene preview after sampling animation
|
||||
/// for better precision for animations editing
|
||||
/// </summary>
|
||||
public static void UpdateHumanoidIKPreview(Animator mecanim, AnimationClip clip, float time, bool restoreAnimator = true)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
|
||||
if (clip != null)
|
||||
{
|
||||
#region Initialize temporary animator controller
|
||||
|
||||
if (_ikHelperAnimController == null)
|
||||
{
|
||||
_ikHelperAnimController = new UnityEditor.Animations.AnimatorController();
|
||||
_ikHelperAnimController.name = "ADesigner-Helper-Controller";
|
||||
}
|
||||
|
||||
if (_ikHelperAnimController.layers.Length == 0)
|
||||
{
|
||||
var state = new UnityEditor.Animations.AnimatorState();
|
||||
state.motion = null;
|
||||
state.iKOnFeet = true;
|
||||
state.name = "0";
|
||||
|
||||
UnityEditor.Animations.AnimatorControllerLayer layer = new UnityEditor.Animations.AnimatorControllerLayer();
|
||||
layer.name = "0";
|
||||
layer.iKPass = true;
|
||||
layer.stateMachine = new UnityEditor.Animations.AnimatorStateMachine();
|
||||
layer.stateMachine.AddState(state, Vector3.zero);
|
||||
layer.stateMachine.defaultState = state;
|
||||
|
||||
_ikHelperAnimController.AddLayer(layer);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
_ikHelperAnimController.layers[0].stateMachine.defaultState.motion = clip;
|
||||
|
||||
RuntimeAnimatorController preController = mecanim.runtimeAnimatorController;
|
||||
|
||||
var preUpd = mecanim.updateMode;
|
||||
|
||||
mecanim.updateMode = AnimatorUpdateMode.UnscaledTime;
|
||||
mecanim.Rebind();
|
||||
mecanim.runtimeAnimatorController = (RuntimeAnimatorController)_ikHelperAnimController;
|
||||
mecanim.Play("0", 0, time / clip.length);
|
||||
mecanim.Update(0f);
|
||||
|
||||
mecanim.updateMode = preUpd;
|
||||
|
||||
if (restoreAnimator) mecanim.runtimeAnimatorController = preController;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd99277efce4cd444ab9c9e25739dada
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e605d77184e9c5145bf97d6474c229dc
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 7400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8c4596dc026aa8d4b9e265a1280c8944
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 7400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bd5f8108facc2474fb6d3fc175e75445
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 7400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,165 @@
|
||||
using System;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace.FProceduralAnimation
|
||||
{
|
||||
[System.Serializable]
|
||||
public class MotionInfluenceProcessor
|
||||
{
|
||||
/// <summary> Using separated influence on each axis when enabled </summary>
|
||||
public bool AdvancedInfluence = false;
|
||||
public Vector3 AxisMotionInfluence = Vector3.one;
|
||||
public Vector3 AxisMotionInfluenceBackwards = Vector3.one;
|
||||
|
||||
/// <summary> Can be used to lower blending when character is moving / idling </summary>
|
||||
private float MultiplyBlend = 1f;
|
||||
[NonSerialized] public float ExtraBoost = 1f;
|
||||
private float _sd_mb = 0f;
|
||||
public void TransitionBlend(float target, float duration, float delta)
|
||||
{
|
||||
MultiplyBlend = Mathf.SmoothDamp(MultiplyBlend, target, ref _sd_mb, duration, 10000000f, delta);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
previousPosition = root.position;
|
||||
localOffset = Vector3.zero;
|
||||
rootOffset = Vector3.zero;
|
||||
}
|
||||
|
||||
|
||||
private Transform root;
|
||||
|
||||
public Vector3 OutputInfluenceOffset { get; private set; }
|
||||
public Vector3 previousPosition { get; private set; }
|
||||
public Vector3 rootOffset { get; private set; }
|
||||
public Vector3 localOffset { get; private set; }
|
||||
|
||||
public void Init(Transform root)
|
||||
{
|
||||
this.root = root;
|
||||
previousPosition = root.position;
|
||||
localOffset = Vector3.zero;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
rootOffset = root.position - previousPosition;
|
||||
previousPosition = root.position;
|
||||
localOffset = root.InverseTransformVector(rootOffset);
|
||||
|
||||
float blend = MultiplyBlend * ExtraBoost;
|
||||
if (blend != 1f) localOffset *= blend;
|
||||
|
||||
Motion_MotionInfluence();
|
||||
}
|
||||
|
||||
|
||||
public void OverrideOffset(Vector3 offset)
|
||||
{
|
||||
rootOffset = offset;
|
||||
localOffset = root.InverseTransformVector(rootOffset);
|
||||
|
||||
Motion_MotionInfluence();
|
||||
}
|
||||
|
||||
private void Motion_MotionInfluence()
|
||||
{
|
||||
if (!AdvancedInfluence)
|
||||
{
|
||||
if (AxisMotionInfluence != Vector3.one) OutputInfluenceOffset = root.TransformVector(ScaleMotionInfluence(localOffset, AxisMotionInfluence));
|
||||
}
|
||||
else OutputInfluenceOffset = root.TransformVector(ScaleMotionInfluence(localOffset, AxisMotionInfluence, AxisMotionInfluenceBackwards));
|
||||
}
|
||||
|
||||
private Vector3 ScaleMotionInfluence(Vector3 toScale, Vector3 influenceMul)
|
||||
{
|
||||
return new Vector3(
|
||||
toScale.x * (1f - influenceMul.x),
|
||||
toScale.y * (1f - influenceMul.y),
|
||||
toScale.z * (1f - influenceMul.z));
|
||||
}
|
||||
|
||||
private Vector3 ScaleMotionInfluence(Vector3 toScale, Vector3 influenceMulForw, Vector3 influenceMulBack)
|
||||
{
|
||||
if (toScale.x > 0f) toScale.x *= (1f - influenceMulForw.x); else toScale.x *= (1f - influenceMulBack.x);
|
||||
if (toScale.y > 0f) toScale.y *= (1f - influenceMulForw.y); else toScale.y *= (1f - influenceMulBack.y);
|
||||
if (toScale.z > 0f) toScale.z *= (1f - influenceMulForw.z); else toScale.z *= (1f - influenceMulBack.z);
|
||||
return toScale;
|
||||
}
|
||||
|
||||
|
||||
public Vector3 CalculateInversedInfluence()
|
||||
{
|
||||
if (!AdvancedInfluence)
|
||||
{
|
||||
if (AxisMotionInfluence != Vector3.one) return root.TransformVector(ScaleMotionInfluenceInverse(localOffset, AxisMotionInfluence));
|
||||
}
|
||||
else return root.TransformVector(ScaleMotionInfluenceInverse(localOffset, AxisMotionInfluence, AxisMotionInfluenceBackwards));
|
||||
|
||||
return rootOffset;
|
||||
}
|
||||
|
||||
private Vector3 ScaleMotionInfluenceInverse(Vector3 toScale, Vector3 influenceMul)
|
||||
{
|
||||
return new Vector3(
|
||||
toScale.x * (influenceMul.x),
|
||||
toScale.y * (influenceMul.y),
|
||||
toScale.z * (influenceMul.z));
|
||||
}
|
||||
|
||||
private Vector3 ScaleMotionInfluenceInverse(Vector3 toScale, Vector3 influenceMulForw, Vector3 influenceMulBack)
|
||||
{
|
||||
if (toScale.x > 0f) toScale.x *= (influenceMulForw.x); else toScale.x *= (influenceMulBack.x);
|
||||
if (toScale.y > 0f) toScale.y *= (influenceMulForw.y); else toScale.y *= (influenceMulBack.y);
|
||||
if (toScale.z > 0f) toScale.z *= (influenceMulForw.z); else toScale.z *= (influenceMulBack.z);
|
||||
return toScale;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
public static void _EditorDrawGUI(SerializedProperty processor)
|
||||
{
|
||||
|
||||
SerializedProperty motInfl = processor.FindPropertyRelative("AxisMotionInfluence");
|
||||
SerializedProperty motInflAdv = processor.FindPropertyRelative("AdvancedInfluence");
|
||||
SerializedProperty backwards = processor.FindPropertyRelative("AxisMotionInfluenceBackwards");
|
||||
|
||||
|
||||
if (motInflAdv.boolValue == false) // Simple motion influence slider
|
||||
{
|
||||
float pre = motInfl.vector3Value.x;
|
||||
Vector3 newVal = motInfl.vector3Value;
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
newVal.x = EditorGUILayout.Slider(new GUIContent(motInfl.displayName, motInfl.tooltip), motInfl.vector3Value.x, 0f, 1f); EditorGUIUtility.labelWidth = 4;
|
||||
EditorGUILayout.PropertyField(motInflAdv, new GUIContent(" ", "Switch to advanced motion influence settings"), GUILayout.Width(24)); EditorGUIUtility.labelWidth = 0;
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
if (newVal.x != pre)
|
||||
{
|
||||
motInfl.vector3Value = Vector3.one * newVal.x;
|
||||
backwards.vector3Value = motInfl.vector3Value;
|
||||
}
|
||||
}
|
||||
else // Motion influence per axis
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PropertyField(motInfl); EditorGUIUtility.labelWidth = 4;
|
||||
EditorGUILayout.PropertyField(motInflAdv, new GUIContent(" ", "Switch to advanced motion influence settings"), GUILayout.Width(24)); EditorGUIUtility.labelWidth = 0;
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.PropertyField(backwards);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1bdd585e067e72d44a94b0c1439d035c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ec5aa9bdd4ad854bbebebe6246c2327
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user