导入leg插件,完成腿部动画

This commit is contained in:
2025-09-24 15:12:17 +08:00
parent 5087ff1cfe
commit 090e86c0ee
1087 changed files with 417484 additions and 30288 deletions

View File

@@ -0,0 +1,17 @@
{
"name": "AD_FimpAnimating",
"references": [
"GUID:0e6b1f35d8416da46a5e09a445db4184",
"GUID:4d3c0eb3c5c6f2243952516f8611fff4",
"GUID:7fe40b228d32d4d4e9b1cf8eb73412fd"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: f6cd8915a8323e640bef362b6f5dd974
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4e848ece83ea80a49912596471422c78
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d269edcf8cd841f48981f2f7bbb4a69e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 14cff47c10738c4479ff5dd80c35a2cf
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,17 @@
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
[CreateAssetMenu(fileName = "LA Leg Motion Settings", menuName = "FImpossible Creations/Legs Animator/Leg Motion Preset", order = 0)]
public class LegMotionSettingsPreset : ScriptableObject
{
[Header("Settings for single leg - leg animation style")]
public LegsAnimator.LegStepAnimatingParameters Settings;
private void Reset()
{
Settings = new LegsAnimator.LegStepAnimatingParameters();
Settings.RefreshDefaultCurves();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f6d73a50b3ca63c499581f859aad8092
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7803bdfaac5113546958599d80be7d57, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,350 @@
using System;
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
[Tooltip("Optional reference to unity's Animator. Legs Animator can use animator's variables to read state of your character movement, like IsGrounded or IsMoving + other extra helpers for custom modules and special calculations.")]
public Animator Mecanim;
[Tooltip("Animator parameter to read value for auto-define grounded state of the character (you can use LegAnimator.User_SetIsGrounded() through code instead)")]
public string GroundedParameter = "";
[Tooltip("Animator parameter (bool or float - Bool recommended for quicker Not-Moving reaction) to read value for auto-define movement state of the character (you can use LegAnimator.User_SetIsMoving() through code instead)")]
public string MovingParameter = "";
private int _hash_Grounded = -1;
private int _hash_Moving = -1;
private bool _hash_MovingIsFloat = false;
[Range(0f,0.5f)]
[HideInInspector] public float User_IsMovingMecanim_NotMovingFloat_Threshold = 0.1f;
private int _hash_Sliding = -1;
[Tooltip("Optional Rigidbody which is used for few helper calculations. If rigidbody is assigned, then rigidbody velocity will drive 'Desired Move Direction' value (! only if .IsMoving is true !), unless you use 'User_SetDesiredMovementDirection'")]
public Rigidbody Rigidbody;
[Tooltip("Use rigidboy velocity state to automatically drive Leg Animator's helper 'IsMoving' flag")]
public bool UseRigidbodyVelocityForIsMoving = false;
//[Tooltip("Use rigidboy velocity to automatically drive Leg Animator's helper 'Desired Direction' value")]
//public bool UseRigidbodyVelocityForDesiredVelocity = false;
[Tooltip("Use leg raycasts to automatically define Leg Animator's helper 'IsGrounded' flag")]
public bool UseRaycastsForIsGrounded = false;
[Tooltip("Animator parameter to read value for auto-define sliding state of the character - auto fading off gluing (you can use LegAnimator.User_SetIsSliding() through code instead)")]
public string SlidingParameter = "";
[Tooltip("Optional bone for modules if needed")]
public Transform SpineBone;
[Tooltip("Optional bone for modules if needed")]
public Transform ChestBone;
/// <summary> (world space move direction)\n(When rigidbody is assigned, rigidbody velocity is used here)\n Control value which helps animation based gluing detection </summary>
public Vector3 DesiredMovementDirection { get; private set; }
public bool usingCustomDesiredMovementDirection { get; private set; }
CalibrateTransform _spineBoneCalibrate;
CalibrateTransform _ChestBoneCalibrate;
#region Ragdolled State Switch Implementation
[Tooltip("Animator parameter to read value for auto-define calculations state of the character. The ragdolled parameter is disabling legs, and other algorithms which can conflict with physical animations. (you can use LegAnimator.User_SetIsRagdolled() through code instead)")]
public string RagdolledParameter = "";
private int _hash_Ragdolled = -1;
public bool IsRagdolled { get { return _ragdolled; } }
bool _ragdolled = false;
/// <summary> Negative for Non-Ragdolled elapsed time </summary>
public float RagdolledTime { get; private set; }
[NonSerialized] public float MinNonRagdolledForBlendOut = 0f;
public void User_SetIsRagdolled(bool isRagdolled)
{
if (isRagdolled != _ragdolled)
{
if (_ragdolled)
{
OnLegsReactivate();
}
_ragdolled = isRagdolled;
}
}
#endregion
public void User_SetDesiredMovementDirection(Vector3 worldDirection)
{
usingCustomDesiredMovementDirection = true;
DesiredMovementDirection = worldDirection;
}
public void User_SetDesiredMovementDirection(Vector3 worldDirection, bool removeYspeed)
{
if (removeYspeed)
{
worldDirection = ToRootLocalSpaceVec(worldDirection);
worldDirection.y = 0f;
worldDirection = RootToWorldSpaceVec(worldDirection);
}
User_SetDesiredMovementDirection(worldDirection);
}
public void User_SetDesiredMovementDirectionOff()
{
usingCustomDesiredMovementDirection = false;
}
/// <summary>
/// Requires assigned 'Mecanim' parameter to calculate mecanim properties hashes
/// </summary>
public virtual void Controll_DefineHashes()
{
if (Mecanim == null) return;
if( string.IsNullOrWhiteSpace( GroundedParameter ) == false ) _hash_Grounded = Animator.StringToHash( GroundedParameter ); else _hash_Grounded = -1;
if( string.IsNullOrWhiteSpace( MovingParameter ) == false )
{
_hash_Moving = Animator.StringToHash( MovingParameter );
if( Mecanim.runtimeAnimatorController != null )
{
for( int i = 0; i < Mecanim.parameterCount; i++ )
if( Mecanim.GetParameter( i ).nameHash == _hash_Moving )
if( Mecanim.GetParameter( i ).type == AnimatorControllerParameterType.Float )
{
_hash_MovingIsFloat = true;
break;
}
}
}
else _hash_Moving = -1;
if( string.IsNullOrWhiteSpace( SlidingParameter ) == false ) _hash_Sliding = Animator.StringToHash( SlidingParameter ); else _hash_Sliding = -1;
if( string.IsNullOrWhiteSpace( RagdolledParameter ) == false ) _hash_Ragdolled = Animator.StringToHash( RagdolledParameter ); else _hash_Ragdolled = -1;
}
public bool Helper_WasMoving { get; private set; }
protected virtual void Controll_Update()
{
#region Ragdolled Blending Switch Support
if (_hash_Ragdolled != -1)
{
User_SetIsRagdolled(Mecanim.GetBool(_hash_Ragdolled));
}
if (IsRagdolled)
{
if (RagdolledTime < 0f) RagdolledTime = 0f; RagdolledTime += DeltaTime;
RagdolledDisablerBlend = Mathf.MoveTowards(RagdolledDisablerBlend, 0f, DeltaTime * 6f);
UpdateBeingRagdolled();
}
else
{
if (RagdolledTime > 0f) RagdolledTime = 0f; RagdolledTime -= DeltaTime;
bool blendTo1 = false;
if (MinNonRagdolledForBlendOut > 0f) { if (-RagdolledTime > MinNonRagdolledForBlendOut) blendTo1 = true; } else blendTo1 = true;
if (blendTo1)
{
float was = RagdolledDisablerBlend;
RagdolledDisablerBlend = Mathf.MoveTowards(RagdolledDisablerBlend, 1f, DeltaTime * 4f);
if (was != RagdolledDisablerBlend) UpdateBeingRagdolled();
}
else
{
RagdolledDisablerBlend = Mathf.MoveTowards(RagdolledDisablerBlend, 0f, DeltaTime * 6f);
}
}
#endregion
if (IsMoving || IsMovingBlend > 0.5f) Helper_WasMoving = true; else Helper_WasMoving = false;
if (_hash_Grounded != -1) User_SetIsGrounded(Mecanim.GetBool(_hash_Grounded));
if (_hash_Moving != -1)
{
if (_hash_MovingIsFloat)
User_SetIsMoving(Mecanim.GetFloat(_hash_Moving) > ScaleReference * User_IsMovingMecanim_NotMovingFloat_Threshold);
else
User_SetIsMoving(Mecanim.GetBool(_hash_Moving));
}
if (_hash_Sliding != -1) User_SetIsSliding(Mecanim.GetBool(_hash_Sliding));
if (GlueMode == EGlueMode.Automatic)
{
if (GroundedTime < 0.1f /*|| IsMovingBlend > 0.5f*/) _glueModeExecuted = EGlueMode.Moving;
else
{
if (IsMoving) _glueModeExecuted = EGlueMode.Moving;
else _glueModeExecuted = EGlueMode.Idle;
}
}
else
{
_glueModeExecuted = GlueMode;
}
if (IsGrounded && GroundedTime < 0.2f) _glueModeExecuted = EGlueMode.Moving;
//if (IsGrounded) GroundedTime += DeltaTime; else GroundedTime = -0.000001f;
if (IsGrounded) { if (GroundedTime < 0f) GroundedTime = 0f; GroundedTime += DeltaTime; } else { if (GroundedTime > 0f) GroundedTime = 0f; GroundedTime -= DeltaTime; }
if (IsMoving) { if (MovingTime < 0f) MovingTime = 0f; MovingTime += DeltaTime; } else { if (MovingTime > 0f) MovingTime = 0f; MovingTime -= DeltaTime; }
if (GluingFloorLevelUseOnMoving)
_glueingFloorLevel = Mathf.LerpUnclamped(GluingFloorLevel, GluingFloorLevelOnMoving, IsMovingBlend);
else
_glueingFloorLevel = GluingFloorLevel;
if (UseStepPointsOverlapRadiusOnMoving)
_stepPointsOverlapRadius = Mathf.LerpUnclamped(StepPointsOverlapRadius, StepPointsOverlapRadiusOnMoving, IsMovingBlend);
else
_stepPointsOverlapRadius = StepPointsOverlapRadius;
}
[Space(5)]
[Tooltip("Calculating leg swing velocity in order to prevent gluing foot when swinging forward during movement forward (during forward swing, foot sometimes is touching ground which can result in gluing foot too soon, especially with ground level increased)\nWhen this value is high, foot will detect gluing less oftem.")]
[Range(0f, 1f)]
public float SwingHelper = 0.0f;
[Tooltip("Local height value for the glue algorithm. You can try adjusting it's value during character movement and idling, to detect glue more effectively.")]
public float GluingFloorLevel = 0.05f;
public bool GluingFloorLevelUseOnMoving = false;
public float GluingFloorLevelOnMoving = 0.0f;
float _glueingFloorLevel = 0f;
[Space(5)]
[Tooltip("If you want to push out legs out of each other if their IK points are overlapping in one placement")]
public float StepPointsOverlapRadius = 0.0f;
public float _stepPointsOverlapRadius { get; private set; }
public bool UseStepPointsOverlapRadiusOnMoving = false;
public float StepPointsOverlapRadiusOnMoving = 0.0f;
//[Tooltip("Local height value for the algorithms when character is in movement mode")]
//public float FloorLevelOnMoving = 0.0125f;
//[Space(5)]
//public float AnimationFloorLevel = 0.05f;
public MotionInfluenceProcessor MotionInfluence = new MotionInfluenceProcessor();
/// <summary> Velocity and motion influence update </summary>
void BaseObject_MotionUpdate()
{
MotionInfluence.Update();
}
void MotionInfluence_Init()
{
MotionInfluence.Init(BaseTransform);
}
protected virtual void Control_OnLand()
{
//if (ImpulseOnLanding != 0f)
//{
// User_AddImpulse(new ImpulseExecutor(new Vector3(0f, -ImpulseOnLanding, 0f), 0.225f, .65f));
//}
if (StepEventOnLanding)
{
float lowestLegH = float.MaxValue;
Leg lowestL = null;
for (int i = 0; i < Legs.Count; i++)
{
float localH = ToRootLocalSpace(Legs[i]._PreviousFinalIKPos).y;
if (localH < lowestLegH)
{
lowestLegH = localH;
lowestL = Legs[i];
Legs[i].StepEventSentInCustomWay();
}
}
if (lowestL != null)
{
Events_OnStep(lowestL, 1f, EStepType.OnLanding);
}
}
}
protected virtual void Control_OnLooseGround()
{
//if (ImpulseOnInAirChange != 0f) User_AddImpulse(new ImpulseExecutor(new Vector3(0f, ImpulseOnInAirChange, 0f), 0.25f, 0.0f));
// Modules
}
protected virtual void Control_OnStopMoving()
{
//if (ImpulseOnStop != 0f)
//{
// User_AddImpulse(new ImpulseExecutor(new Vector3(0f, -ImpulseOnStop * 0.75f, ImpulseOnStop), 0.225f, 0.65f));
//}
// Modules
}
protected virtual void Control_OnStartMoving()
{
//if (ImpulseOnInAirChange != 0f) User_AddImpulse(new ImpulseExecutor(new Vector3(0f, ImpulseOnInAirChange, 0f), 0.25f, 0.0f));
// Modules
}
private void RefreshTargetMovementDirectionHelper()
{
if (!usingCustomDesiredMovementDirection)
{
if (IsMoving == false)
{
DesiredMovementDirection = Vector3.zero;
}
else
{
if (Rigidbody)
{
if (Rigidbody.linearVelocity.magnitude < ScaleReference * 0.1f)
DesiredMovementDirection = Vector3.zero;
else
DesiredMovementDirection = Rigidbody.linearVelocity.normalized;
}
}
}
}
/// <summary>
/// IK parameters needs to be upadted during ragdolled state for proper blend off
/// </summary>
private void UpdateBeingRagdolled()
{
for (int l = 0; l < Legs.Count; l++)
{
var leg = Legs[l];
leg.Leg_UpdateParams();
}
}
struct CalibrateTransform
{
public Transform Transform;
private Quaternion initLocalRot;
public CalibrateTransform(Transform t) { Transform = t; initLocalRot = t.localRotation; }
public void Calibrate() { Transform.localRotation = initLocalRot; }
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 68f1dc76c84874f48be32522fba75742
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,82 @@
using UnityEngine;
using UnityEngine.Events;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
public UnityEvent Event_OnStep = new UnityEvent();
[Tooltip("Increase to execute step event sooner (speed up step confirmation). Useful if step events are executed too late.")]
[Range(0f, 0.3f)] public float EventExecuteSooner = 0.05f;
[Tooltip("If you want to send step events also during movement idle (in case you already use animation clip events for it)")]
public bool SendOnMovingGlue = false;
[Tooltip("When false, it will not allow to send step event when character is stopped")]
public bool SendOnStopping = false;
[Tooltip("Enabling triggering step events when character just switched grounded state")]
public bool StepEventOnLanding = false;
[Space(5)]
[Tooltip("Game Object with attached component implementing LegsAnimator.ILegStepInfoReceiver interface to receiver detailed info about leg step")]
public Transform StepInfoReceiver;
private ILegStepReceiver _StepReceiver = null;
private ILegRaiseReceiver _RaiseReceiver = null;
public enum EStepType
{
IdleGluing, MovementGluing, OnLanding, OnStopping
}
protected bool UseEvents { get; private set; }
void Events_TriggerStepUnityEvent()
{
Event_OnStep.Invoke();
}
void Events_OnStep(Leg leg, float stepFactor = 1f, EStepType type = EStepType.IdleGluing)
{
if (!StepEventOnLanding)
if (IsGroundedBlend * RagdolledDisablerBlend < 0.99f) return;
Events_TriggerStepUnityEvent();
if ( _StepReceiver != null)
{
Vector3 footMidPos = leg._PreviousFinalIKPos + leg.BoneEnd.TransformVector( (leg.AnkleToFeetEnd + leg.AnkleToHeel) * 0.5f);
Quaternion stepRotation = Quaternion.LookRotation(leg._PreviousFinalIKRot * leg.IKProcessor.EndIKBone.forward, leg._PreviousFinalIKRot * leg.IKProcessor.EndIKBone.up);
_StepReceiver.LegAnimatorStepEvent(leg, stepFactor, leg.Side == ELegSide.Right, footMidPos, stepRotation, type);
}
}
void Events_OnRaise( Leg leg, float distanceToNewLegPosition = 1f, EStepType type = EStepType.IdleGluing )
{
if( !StepEventOnLanding )
if( IsGroundedBlend * RagdolledDisablerBlend < 0.99f ) return;
if( _RaiseReceiver != null )
{
Vector3 footMidPos = leg._PreviousFinalIKPos + leg.BoneEnd.TransformVector( ( leg.AnkleToFeetEnd + leg.AnkleToHeel ) * 0.5f );
Quaternion stepRotation = Quaternion.LookRotation( leg._PreviousFinalIKRot * leg.IKProcessor.EndIKBone.forward, leg._PreviousFinalIKRot * leg.IKProcessor.EndIKBone.up );
_RaiseReceiver.LegAnimatorRaiseEvent( leg, distanceToNewLegPosition, leg.Side == ELegSide.Right, footMidPos, stepRotation, type );
}
}
public interface ILegStepReceiver
{
void LegAnimatorStepEvent(Leg leg, float power, bool isRight, Vector3 position, Quaternion rotation, EStepType type);
}
public interface ILegRaiseReceiver
{
void LegAnimatorRaiseEvent( Leg leg, float predictedDistance, bool isRight, Vector3 position, Quaternion rotation, EStepType type );
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 503ff7615ffc45943819dd65561453f4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,85 @@
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
[Tooltip("Additional pelvis position push in local space. Can be accesed for custom pelvis offset animation or for constant model pose correction.")]
public Vector3 ExtraPelvisOffset = Vector3.zero;
[Tooltip("Time which needs to elapse after character stop, to trigger legs repose to most relevant pose in comparison to played idle animation")]
[FPD_Suffix(0f, 2f, FPD_SuffixAttribute.SuffixMode.FromMinToMax, "sec")]
public float ReposeGluingAfter = 0f;
[Tooltip("Enable if you want to use gluing only when character is idling. Useful when it's too much work needed to setup dynamic gluing during movement for your character. (it will still use feet ground align)")]
public bool GlueOnlyOnIdle = false;
[Tooltip("Raycasting down direction will be synced with base transform up axis when this feature is enabled.")]
public bool LocalWorldUp = true;
float reposeGluingTimer = 0f;
bool reposedGluing = false;
public bool JustGrounded { get; private set; }
/// <summary> Reglue Controls </summary>
void ExtraControls_Update()
{
if (IsGrounded && GroundedTime < 0.2f) JustGrounded = true; else JustGrounded = false;
if (ReposeGluingAfter > 0f)
{
if (MotionInfluence.rootOffset.magnitude > ScaleReference * 0.05f || IsMoving)
{
reposeGluingTimer = 0f;
reposedGluing = false;
}
else
{
reposeGluingTimer += DeltaTime;
}
if (!reposedGluing)
{
if (reposeGluingTimer > ReposeGluingAfter)
{
IK_TriggerReglue();
reposedGluing = true;
}
}
}
}
#region Rotate IK
//bool usingIKRotate = false;
//public void IK_ToggleForceUseIKRotate() { usingIKRotate = true; }
Quaternion IK_UseIKRotatorQuat = Quaternion.identity;
//bool useCustomIKRotatorVector = false;
public Vector3 IK_CustomIKRotatorVector { get; private set; } = Vector3.zero;
public void DisableCustomIKRotatorVector()
{
//useCustomIKRotatorVector = false;
}
public void SetCustomIKRotatorVector(Vector3 localVector)
{
IK_CustomIKRotatorVector = localVector;
//useCustomIKRotatorVector = true;
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a68cdc0942294b942a5c034b31ca8e97
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
[System.Serializable]
public class LegsAnimatorCustomModuleHelper
{
public bool Enabled = true;
public LegsAnimator Parent;
public LegsAnimatorControlModuleBase ModuleReference = null;
public LegsAnimatorControlModuleBase PlaymodeModule { get; private set; }
#region Get Module
public LegsAnimatorControlModuleBase CurrentModule
{
get
{
#if UNITY_EDITOR
if (Application.isPlaying) return PlaymodeModule; else return ModuleReference;
#else
return PlaymodeModule;
#endif
}
}
#endregion
/// <summary> Can be used for containing any parasable value or just strings </summary>
[SerializeField, HideInInspector] public List<string> customStringList = null;
/// <summary> Support for list of unity objects </summary>
[SerializeField, HideInInspector] public List<UnityEngine.Object> customObjectList = null;
public LegsAnimatorCustomModuleHelper(LegsAnimator get)
{
Parent = get;
}
public void PreparePlaymodeModule(LegsAnimator parent)
{
if (PlaymodeModule != null) return;
if (ModuleReference == null) return;
PlaymodeModule = Instantiate(ModuleReference) as LegsAnimatorControlModuleBase;
PlaymodeModule.Base_Init(parent, this);
}
public void DisposeModule()
{
if (PlaymodeModule != null) Destroy(PlaymodeModule);
PlaymodeModule = null;
}
[SerializeField] private List<LegsAnimator.Variable> variables = new List<LegsAnimator.Variable>();
public LegsAnimator.Variable RequestVariable(string name, object defaultValue)
{
if (variables == null) variables = new List<LegsAnimator.Variable>();
int hash = name.GetHashCode();
for (int i = 0; i < variables.Count; i++)
{
if (variables[i].GetNameHash == hash) return variables[i];
}
LegsAnimator.Variable nVar = new LegsAnimator.Variable(name, defaultValue);
variables.Add(nVar);
return nVar;
}
#region Editor Code
#if UNITY_EDITOR
[NonSerialized] public string formattedName = "";//
#endif
#endregion
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 196195dfd65be2c4c851ea6fed645240
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,160 @@
using System.Collections.Generic;
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
[Tooltip("Completely turning off all custom modules scripts execution.")]
public bool DisableCustomModules = false;
[Tooltip("Custom coded legs animator modules to change component behaviour without affecting core code")]
public List<LegsAnimatorCustomModuleHelper> CustomModules = new List<LegsAnimatorCustomModuleHelper>();
void InitializeModules()
{
bool anyModule = false;
if (CustomModules == null) return;
for (int i = CustomModules.Count - 1; i >= 0; i--)
{
if (CustomModules[i] == null) { CustomModules.RemoveAt(i); continue; }
if (CustomModules[i].ModuleReference == null) { CustomModules.RemoveAt(i); continue; }
CustomModules[i].PreparePlaymodeModule(this);
anyModule = true;
}
UsingControlModules = anyModule;
}
void DisposeModules()
{
if (CustomModules == null) return;
for (int i = CustomModules.Count - 1; i >= 0; i--)
{
if (CustomModules[i] == null) { CustomModules.RemoveAt(i); continue; }
if (CustomModules[i].ModuleReference == null) { CustomModules.RemoveAt(i); continue; }
CustomModules[i].DisposeModule();
}
}
public T GetModule<T>() where T : LegsAnimatorControlModuleBase
{
if (CustomModules == null) return null;
for (int i = 0; i < CustomModules.Count; i++)
{
if (CustomModules[i].ModuleReference is T)
{
return CustomModules[i].PlaymodeModule as T;
}
}
return null;
}
public LegsAnimatorCustomModuleHelper GetModuleHelper<T>() where T : LegsAnimatorControlModuleBase
{
if (CustomModules == null) return null;
for (int i = 0; i < CustomModules.Count; i++)
{
if (CustomModules[i].ModuleReference is T)
{
return CustomModules[i];
}
}
return null;
}
void Modules_OnReInitialize()
{
if (UsingControlModules == false) return;
if (DisableCustomModules) return;
for (int m = 0; m < CustomModules.Count; m++) if (CustomModules[m].Enabled) CustomModules[m].PlaymodeModule.OnReInitialize(CustomModules[m]);
}
private bool UsingControlModules = false;
void Modules_Update()
{
if (UsingControlModules == false) return;
if (DisableCustomModules) return;
for (int m = 0; m < CustomModules.Count; m++) if (CustomModules[m].Enabled) CustomModules[m].PlaymodeModule.OnUpdate(CustomModules[m]);
}
void Modules_UpdateAfterManualChanges()
{
if (UsingControlModules == false) return;
if (DisableCustomModules) return;
for (int m = 0; m < CustomModules.Count; m++) if (CustomModules[m].Enabled) CustomModules[m].PlaymodeModule.OnValidateAfterManualChanges(CustomModules[m]);
}
void Modules_LegBeforeRaycastingUpdate(Leg leg)
{
if (UsingControlModules == false) return;
if (DisableCustomModules) return;
for (int m = 0; m < CustomModules.Count; m++) if (CustomModules[m].Enabled) CustomModules[m].PlaymodeModule.Leg_LatePreRaycastingUpdate(CustomModules[m], leg);
}
void Modules_AfterAnimatorCaptureUpdate()
{
if (UsingControlModules == false) return;
if (DisableCustomModules) return;
for (int m = 0; m < CustomModules.Count; m++) if (CustomModules[m].Enabled) CustomModules[m].PlaymodeModule.OnAfterAnimatorCaptureUpdate(CustomModules[m]);
}
void Modules_PreLateUpdate()
{
if (UsingControlModules == false) return;
if (DisableCustomModules) return;
for (int m = 0; m < CustomModules.Count; m++) if (CustomModules[m].Enabled) CustomModules[m].PlaymodeModule.OnPreLateUpdate(CustomModules[m]);
}
void Modules_LateUpdatePreApply()
{
if (UsingControlModules == false) return;
if (DisableCustomModules) return;
for (int m = 0; m < CustomModules.Count; m++) if (CustomModules[m].Enabled) CustomModules[m].PlaymodeModule.OnLateUpdatePreApply(CustomModules[m]);
}
void Modules_PostLateUpdate()
{
if (UsingControlModules == false) return;
if (DisableCustomModules) return;
for (int m = 0; m < CustomModules.Count; m++) if (CustomModules[m].Enabled) CustomModules[m].PlaymodeModule.OnPostLateUpdate(CustomModules[m]);
}
void Modules_Leg_LateUpdate(Leg leg)
{
if (UsingControlModules == false) return;
if (DisableCustomModules) return;
for (int m = 0; m < CustomModules.Count; m++) if (CustomModules[m].Enabled) CustomModules[m].PlaymodeModule.Leg_LateUpdate(CustomModules[m], leg);
}
#if UNITY_EDITOR
public void _Editor_ModulesOnSceneGUI()
{
if (UsingControlModules == false) return;
if (CustomModules == null) return;
if (DisableCustomModules) return;
if ( Application.isPlaying == false)
{
for (int m = 0; m < CustomModules.Count; m++) if (CustomModules[m].ModuleReference) if (CustomModules[m].Enabled) CustomModules[m].ModuleReference.Editor_OnSceneGUI(this, CustomModules[m]);
return;
}
for (int m = 0; m < CustomModules.Count; m++) if (CustomModules[m].Enabled) CustomModules[m].PlaymodeModule.Editor_OnSceneGUI(this, CustomModules[m]);
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a186752dc3d92c2479eb3c652f2db569
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,77 @@
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
[Tooltip("Power multiplier for pelvis push events")]
public float ImpulsesPowerMultiplier = 1f;
public float ImpulsesDurationMultiplier = 1f;
[Range(0f,1f)]
[Tooltip("Damping impulses which are pushing body above ground level")]
public float ImpulsesDampUpPushes = 0f;
public PelvisImpulseSettings DebugPushHipsImpulse = new PelvisImpulseSettings(Vector3.down, 0.6f, 1f);
[System.Serializable]
public class PelvisImpulseSettings
{
public string OptionalName = "Impulse";
[Space(3)]
public float PowerMultiplier = 1f;
[Tooltip("Duration of translation impulse in seconds")]
public float ImpulseDuration = 0.5f;
[Space(5)]
public Vector3 WorldTranslation = Vector3.zero;
public Vector3 LocalTranslation = new Vector3(0f, -0.2f, 0.1f);
[Space(5)]
public Vector3 HipsRotate = Vector3.zero;
[Space(5)]
[Range(0f,1f)]
public float InheritElasticness = 0.75f;
[FPD_FixedCurveWindow(0f, 0f, 1f, 1f)]
public AnimationCurve ImpulseCurve = AnimationCurve.EaseInOut(0f, 1f, 1f, 0f);
[FPD_FixedCurveWindow(0f, 0f, 1f, 1f)]
public AnimationCurve YAxisMultiplyCurve = AnimationCurve.EaseInOut(0f, 1f, 1f, 1f);
[Space(5)]
[Tooltip("Local Offset Z-forward will bo rotated to face the legs animator's current desired move direction value")]
public bool AlignWithDesiredMoveDirection = false;
public PelvisImpulseSettings Copy()
{
return (PelvisImpulseSettings)MemberwiseClone();
}
public PelvisImpulseSettings()
{
ImpulseCurve = GetDefaultCurveInstance();
}
public static AnimationCurve GetDefaultCurveInstance()
{
AnimationCurve impulseCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 0f);
impulseCurve.AddKey(new Keyframe(0.2f, 1f));
impulseCurve.SmoothTangents(1, 0.5f);
return impulseCurve;
}
public PelvisImpulseSettings(Vector3 vector3, float duration, float power):this()
{
LocalTranslation = vector3;
ImpulseDuration = duration;
PowerMultiplier = power;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: be6e800aea28b9d4e9ca3c35cc864380
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,273 @@
using System.Collections.Generic;
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
protected List<ImpulseExecutor> Impulses = new List<ImpulseExecutor>();
public struct ImpulseExecutor
{
public float Elapsed;
public float PowerMultiplier;
public float ImpulseDuration;
public Vector3 WorldTranslation;
public Vector3 LocalTranslation;
public float InheritElasticness;
public Vector3 HipsRotation;
public bool AlignDesired;
public AnimationCurve ImpulseCurve;
public AnimationCurve YAxisMultiplyCurve;
public static AnimationCurve DefaultCurve { get { if (_defaultCurve == null) _defaultCurve = PelvisImpulseSettings.GetDefaultCurveInstance(); return _defaultCurve; } }
private static AnimationCurve _defaultCurve = null;
public static AnimationCurve DefaultCurve11 { get { if (_defaultCurve11 == null) _defaultCurve11 = AnimationCurve.Linear(0f, 1f, 1f, 1f); return _defaultCurve11; } }
private static AnimationCurve _defaultCurve11 = null;
public ImpulseExecutor(PelvisImpulseSettings settings, float powerMultiplier = 1f, float durationMultiplier = 1f)
{
Elapsed = 0f;
PowerMultiplier = settings.PowerMultiplier * powerMultiplier;
ImpulseDuration = settings.ImpulseDuration * durationMultiplier;
WorldTranslation = settings.WorldTranslation;
LocalTranslation = settings.LocalTranslation;
InheritElasticness = settings.InheritElasticness;
HipsRotation = settings.HipsRotate;
ImpulseCurve = settings.ImpulseCurve;
YAxisMultiplyCurve = settings.YAxisMultiplyCurve;
AlignDesired = settings.AlignWithDesiredMoveDirection;
}
public ImpulseExecutor(Vector3 localOffset, float duration, float elastic = 1f, AnimationCurve curve = null, bool alignWithDesiredDir = false)
{
Elapsed = 0f;
PowerMultiplier = 1f;
ImpulseDuration = duration;
WorldTranslation = Vector3.zero;
LocalTranslation = localOffset;
InheritElasticness = elastic;
ImpulseCurve = curve;
if (curve == null) ImpulseCurve = DefaultCurve;
YAxisMultiplyCurve = DefaultCurve11;
HipsRotation = Vector3.zero;
AlignDesired = alignWithDesiredDir;
}
public ImpulseExecutor(Vector3 localOffset, Vector3 hipsRotation, float duration, float elastic = 1f, AnimationCurve curve = null, bool alignWithDesiredDir = false)
{
Elapsed = 0f;
PowerMultiplier = 1f;
ImpulseDuration = duration;
WorldTranslation = Vector3.zero;
HipsRotation = hipsRotation;
LocalTranslation = localOffset;
InheritElasticness = elastic;
ImpulseCurve = curve;
if (curve == null) ImpulseCurve = DefaultCurve;
YAxisMultiplyCurve = DefaultCurve11;
AlignDesired = alignWithDesiredDir;
}
public ImpulseExecutor(float duration, Vector3 worldOffset, float elastic = 1f, AnimationCurve curve = null, bool alignWithDesiredDir = false)
{
Elapsed = 0f;
PowerMultiplier = 1f;
ImpulseDuration = duration;
WorldTranslation = worldOffset;
HipsRotation = Vector3.zero;
LocalTranslation = Vector3.zero;
InheritElasticness = elastic;
ImpulseCurve = curve;
if (curve == null) ImpulseCurve = DefaultCurve;
YAxisMultiplyCurve = DefaultCurve11;
AlignDesired = alignWithDesiredDir;
}
public void Update(float delta)
{
Elapsed += delta;
}
public bool Finished { get { return Elapsed >= ImpulseDuration; } }
public float Progress { get { return ImpulseDuration == 0f ? 1f : Elapsed / ImpulseDuration; } }
public float Evaluation { get { return ImpulseCurve.Evaluate(Progress); } }
public float Elastic { get { return InheritElasticness; } }
public float Power { get { return PowerMultiplier; } }
public Vector3 CurrentLocalOffset { get { return LocalTranslation * Evaluation * Power; } }
public float CurrentLocalYAxisMultiplier { get { return YAxisMultiplyCurve.Evaluate(Progress); } }
public Vector3 CurrentWorldOffset { get { return WorldTranslation * Evaluation * Power; } }
}
bool _ImpulsesDoWorld = false;
bool _ImpulsesDoLocal = false;
bool _ImpulsesDoHips = false;
Vector3 _ImpulsesWorldPush = Vector3.zero;
Vector3 _ImpulsesWorldPushInherit = Vector3.zero;
Vector3 _ImpulsesLocalPush = Vector3.zero;
Vector3 _ImpulsesLocalPushInherit = Vector3.zero;
Vector3 _ImpulsesHipsRotation = Vector3.zero;
Vector3 _ImpulsesRotationElastic = Vector3.zero;
Vector3 _Hips_RotationElasticLocalOffset = Vector3.zero;
void Hips_Calc_UpdateImpulses()
{
_ImpulsesDoLocal = false;
_ImpulsesDoWorld = false;
_ImpulsesDoHips = false;
if (Impulses.Count == 0) return;
if (ImpulsesDurationMultiplier < 0.001) ImpulsesDurationMultiplier = 1f;
_ImpulsesWorldPush = Vector3.zero;
_ImpulsesWorldPushInherit = Vector3.zero;
_ImpulsesLocalPush = Vector3.zero;
_ImpulsesLocalPushInherit = Vector3.zero;
_ImpulsesHipsRotation = Vector3.zero;
Vector3 desirDirNorm = DesiredMovementDirection.normalized;
// Execute impulses in right order
for (int i = 0; i < Impulses.Count; i++)
{
var impulse = Impulses[i];
impulse.Update(DeltaTime / ImpulsesDurationMultiplier);
if (impulse.WorldTranslation != Vector3.zero)
{
Vector3 push = impulse.CurrentWorldOffset * ImpulsesPowerMultiplier;
if (impulse.Elastic <= 0f)
_ImpulsesWorldPush += push;
else if (impulse.Elastic >= 1f)
_ImpulsesWorldPushInherit += push;
else
{
_ImpulsesWorldPush += (1f - impulse.Elastic) * push;
_ImpulsesWorldPushInherit += impulse.Elastic * push;
}
}
if (impulse.LocalTranslation != Vector3.zero)
{
Vector3 push = impulse.CurrentLocalOffset * (ImpulsesPowerMultiplier * ScaleReferenceNoScale);
push.y *= impulse.CurrentLocalYAxisMultiplier;
bool defaultLocal = true;
if (impulse.AlignDesired)
{
if (DesiredMovementDirection != Vector3.zero)
{
defaultLocal = false;
// Remap for desired dir and apply to world space impulse
Quaternion remap = BaseTransform.rotation * Quaternion.FromToRotation(BaseTransform.forward.normalized, desirDirNorm);
push = remap * push;
if (impulse.Elastic <= 0f)
_ImpulsesWorldPush += push;
else if (impulse.Elastic >= 1f)
_ImpulsesWorldPushInherit += push;
else
{
_ImpulsesWorldPush += (1f - impulse.Elastic) * push;
_ImpulsesWorldPushInherit += impulse.Elastic * push;
}
}
}
if (defaultLocal)
{
if (impulse.Elastic <= 0f)
_ImpulsesLocalPush += push;
else if (impulse.Elastic >= 1f)
_ImpulsesLocalPushInherit += push;
else
{
_ImpulsesLocalPush += (1f - impulse.Elastic) * push;
_ImpulsesLocalPushInherit += impulse.Elastic * push;
}
}
}
if (impulse.HipsRotation != Vector3.zero)
{
Vector3 rotImpulse = impulse.HipsRotation;
if ( impulse.AlignDesired)
{
if (Vector3.Dot(BaseTransform.forward.normalized, desirDirNorm) < 0f)
{
rotImpulse.z = -rotImpulse.z;
}
}
_ImpulsesHipsRotation += rotImpulse * (ImpulsesPowerMultiplier * impulse.Evaluation * impulse.Power);
}
Impulses[i] = impulse;
}
// Check for removing
for (int i = Impulses.Count - 1; i >= 0; i--)
{
if (Impulses[i].Finished) Impulses.RemoveAt(i);
}
if (_ImpulsesWorldPush != Vector3.zero || _ImpulsesWorldPushInherit != Vector3.zero)
_ImpulsesDoWorld = true;
if (_ImpulsesLocalPush != Vector3.zero || _ImpulsesLocalPushInherit != Vector3.zero)
_ImpulsesDoLocal = true;
if (_ImpulsesHipsRotation != Vector3.zero)
_ImpulsesDoHips = true;
}
void Hips_Calc_ApplyImpulsesInherit()
{
if (_ImpulsesDoLocal)
{
_Hips_StabilityLocalOffset += (_ImpulsesLocalPushInherit * _MainBlend);
}
if (_ImpulsesDoWorld)
{
_Hips_StabilityLocalOffset += ToRootLocalSpaceVec(_ImpulsesWorldPushInherit * _MainBlend);
}
if (_ImpulsesDoHips || _ImpulsesRotationElastic != Vector3.zero)
{
_Hips_RotationMuscle.Update(DeltaTime, _ImpulsesHipsRotation);
_ImpulsesRotationElastic = _Hips_RotationMuscle.ProceduralEulerAngles;
_Hips_Modules_ExtraRotOffset += _ImpulsesRotationElastic;
}
}
void Hips_Calc_ApplyImpulses()
{
if (_ImpulsesDoLocal)
{
_LastAppliedHipsFinalPosition += RootToWorldSpaceVec(_ImpulsesLocalPush * _MainBlend);
}
if (_ImpulsesDoWorld)
{
_LastAppliedHipsFinalPosition += _ImpulsesWorldPush * _MainBlend;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 220e03362d3e97e4d8ef3d14a5012dfe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,418 @@
//using System;
//using System.Collections.Generic;
//using UnityEditor;
//using UnityEngine;
//using static FIMSpace.FProceduralAnimation.LegsAnimator;
//namespace FIMSpace.FProceduralAnimation
//{
// public partial class LegsAnimator
// {
// private bool _usingStepHeatmap = false;
// private StepHeatmapManager _stepHeatmap;
// [FPD_FixedCurveWindow]
// public AnimationCurve _StepHeatPenaltyCurve = AnimationCurve.EaseInOut(0.25f, 1f, 1f, 0f);
// [FPD_FixedCurveWindow]
// public AnimationCurve _StepHeatPenaltySameSideCurve = AnimationCurve.EaseInOut(0.2f, 1f, 0.7f, 0f);
// public void StepHeatmap_Setup()
// {
// //if (Legs.Count < 4) return;
// _stepHeatmap = new StepHeatmapManager(this);
// _usingStepHeatmap = true;
// _stepHeatmap.Initialize();
// }
// public void StepHeatmap_Update()
// {
// if (!_usingStepHeatmap) return;
// _stepHeatmap.Update();
// }
// public void StepHeatmap_UpdateLeg(int leg)
// {
// //if (Legs.Count < 4) return;
// _stepHeatmap.UpdatePreGlue(leg);
// }
// public class StepHeatmapManager
// {
// public LegsAnimator Owner;
// List<StepLeg> StepLegs;
// public StepHeatmapManager(LegsAnimator owner)
// {
// Owner = owner;
// Initialized = false;
// }
// public bool Initialized { get; private set; }
// public Bounds LocalLegsBounds { get; private set; }
// public void Initialize()
// {
// StepLegs = new List<StepLeg>();
// #region Prepare Step Legs
// for (int l = 0; l < Owner.Legs.Count; l++)
// {
// StepLeg sLeg = new StepLeg(this, Owner.Legs[l]);
// StepLegs.Add(sLeg);
// }
// #endregion
// Bounds b = new Bounds(StepLegs[0].initialLocalPos, Vector3.zero);
// for (int l = 1; l < StepLegs.Count; l++)
// {
// b.Encapsulate(StepLegs[l].initialLocalPos);
// }
// LocalLegsBounds = b;
// for (int l = 0; l < StepLegs.Count; l++)
// {
// StepLegs[l].ConfigureRelations();
// }
// Initialized = true;
// }
// public void Update()
// {
// //for (int i = 0; i < StepLegs.Count; i++)
// //{
// // var leg = StepLegs[i];
// // leg.StepFactorsCompute();
// //}
// }
// public void UpdatePreGlue(int l)
// {
// var leg = StepLegs[l];
// leg.StepFactorsCompute();
// }
// #region Editor Code
//#if UNITY_EDITOR
// public void OnSceneView(int debugLeg = -1)
// {
// if (debugLeg >= StepLegs.Count) debugLeg = -1;
// if (debugLeg < 0)
// {
// // All Legs
// for (int l = 0; l < StepLegs.Count; l++)
// {
// var leg = StepLegs[l];
// Handles.color = Color.HSVToRGB((0.2f + l * 0.15f) % 1, 0.7f, 0.6f);
// Handles.DrawAAPolyLine(2, leg.LALeg._FinalIKPos, leg.relationsOppositeSide[0].Leg.LALeg._FinalIKPos);
// }
// for (int l = 0; l < StepLegs.Count; l++)
// {
// var leg = StepLegs[l];
// if (leg.AllowAccum < 0.5f)
// Handles.color = Color.Lerp(Color.red, Color.yellow, leg.AllowAccum * 2f);
// else
// Handles.color = Color.Lerp(Color.yellow, Color.green, (leg.AllowAccum * 2f) - 1f);
// Handles.SphereHandleCap(0, leg.LALeg._FinalIKPos, Quaternion.identity, Owner.ScaleReference * (0.05f + leg.AllowAccum * 0.03f), EventType.Repaint);
// Handles.Label(leg.LALeg._FinalIKPos, "\nAllow Leg Up: " + Rnd(leg.AllowAccum) + "\nPenalty: " + Rnd(leg.LastPenalty));
// }
// }
// else
// {
// Handles.color = Color.white * 0.8f;
// var leg = StepLegs[debugLeg];
// Handles.DrawAAPolyLine(3, leg.LALeg._FinalIKPos, leg.relationsOppositeSide[0].Leg.LALeg._FinalIKPos);
// for (int r = 0; r < leg.relationsOppositeSide.Count; r++)
// {
// var rel = leg.relationsOppositeSide[r];
// Handles.DrawDottedLine(leg.LALeg._FinalIKPos, rel.Leg.LALeg._FinalIKPos, 3f);
// float penalty = leg.ComputePenaltyForOppositeSide(rel, r);
// Handles.Label(rel.Leg.LALeg._FinalIKPos, "\n" + Rnd(rel.Factor) + "\nPenalty=" + Rnd( penalty) );
// }
// for (int r = 0; r < leg.relationsSameSide.Count; r++)
// {
// var rel = leg.relationsSameSide[r];
// Handles.DrawDottedLine(leg.LALeg._FinalIKPos, rel.Leg.LALeg._FinalIKPos, 5f);
// float penalty = leg.ComputePenaltyForSameSide(rel);
// Handles.Label(rel.Leg.LALeg._FinalIKPos, "\n" + Rnd(rel.Factor)+"\nPenalty="+ Rnd(penalty));
// }
// }
// }
// float Rnd(float v)
// {
// return (float)System.Math.Round(v, 2);
// }
//#endif
// #endregion
// #region Step Leg Class
// class StepLeg
// {
// StepHeatmapManager Heatmapper;
// public LegsAnimator Owner { get { return Heatmapper.Owner; } }
// public Leg LALeg { get; private set; }
// List<StepLeg> StepLegs { get { return Heatmapper.StepLegs; } }
// public List<LegRelation> relationsSameSide { get; private set; }
// public List<LegRelation> relationsOppositeSide { get; private set; }
// public Vector3 initialLocalPos { get; private set; }
// ELegSide side;
// LegRelation nearestSameSide;
// float nrstSame = float.MaxValue;
// LegRelation farestSameSide;
// float frstSame = float.MinValue;
// LegRelation nearestOppositeSide;
// float nrstOppos = float.MaxValue;
// LegRelation farestOppositeSide;
// float frstOppos = float.MinValue;
// public StepLeg(StepHeatmapManager heatmapper, Leg leg)
// {
// Heatmapper = heatmapper;
// LALeg = leg;
// initialLocalPos = leg.Owner.ToRootLocalSpace(leg.BoneEnd.position);
// AllowAccum = 0f;
// if (leg.Side == ELegSide.Undefined)
// {
// if (initialLocalPos.x < 0f) side = ELegSide.Left;
// else if (initialLocalPos.x > 0f) side = ELegSide.Right;
// }
// else
// side = leg.Side;
// }
// public void ConfigureRelations()
// {
// relationsSameSide = new List<LegRelation>();
// relationsOppositeSide = new List<LegRelation>();
// nrstSame = float.MaxValue;
// frstSame = float.MinValue;
// nrstOppos = float.MaxValue;
// frstOppos = float.MinValue;
// Bounds opposideSideBounds = new Bounds(Vector3.zero, Vector3.zero);
// // Calculate initial relations
// for (int l = 0; l < StepLegs.Count; l++)
// {
// if (StepLegs[l] == this) continue;
// var oLeg = StepLegs[l];
// LegRelation rel = new LegRelation(oLeg);
// rel.Distance = Vector3.Distance(initialLocalPos, oLeg.initialLocalPos);
// if (oLeg.side == side) // Same Side
// {
// if (rel.Distance < nrstSame) { nrstSame = rel.Distance; nearestSameSide = rel; }
// if (rel.Distance > frstSame) { frstSame = rel.Distance; farestSameSide = rel; }
// rel.WeightedDistance = rel.Distance;
// relationsSameSide.Add(rel);
// }
// else // Opposite Side
// {
// if (opposideSideBounds.center == Vector3.zero) opposideSideBounds = new Bounds(oLeg.initialLocalPos, Vector3.zero);
// else opposideSideBounds.Encapsulate(oLeg.initialLocalPos);
// relationsOppositeSide.Add(rel);
// }
// }
// // Compute weighted distance basing on position difference in axes
// for (int i = 0; i < relationsOppositeSide.Count; i++)
// {
// var oLeg = relationsOppositeSide[i];
// float a = Mathf.Abs(initialLocalPos.z - oLeg.Leg.initialLocalPos.z);
// oLeg.WeightedDistance = a;
// if (oLeg.WeightedDistance < nrstOppos) { nrstOppos = oLeg.WeightedDistance; nearestOppositeSide = oLeg; }
// if (oLeg.WeightedDistance > frstOppos) { frstOppos = oLeg.WeightedDistance; farestOppositeSide = oLeg; }
// }
// float scaleRefScale = Heatmapper.LocalLegsBounds.size.magnitude;
// // Use distances to define relation factors
// for (int s = 0; s < relationsSameSide.Count; s++)
// relationsSameSide[s].WeightedFactor = relationsSameSide[s].WeightedDistance / scaleRefScale;
// for (int s = 0; s < relationsOppositeSide.Count; s++)
// relationsOppositeSide[s].WeightedFactor = relationsOppositeSide[s].WeightedDistance / scaleRefScale;
// // Sort by weighted factor
// relationsSameSide.Sort((a, b) => a.WeightedFactor.CompareTo(b.WeightedFactor));
// relationsOppositeSide.Sort((a, b) => a.WeightedFactor.CompareTo(b.WeightedFactor));
// // Define main factor
// for (int s = 0; s < relationsSameSide.Count; s++)
// relationsSameSide[s].Factor = relationsSameSide[s].Distance / scaleRefScale;
// for (int s = 1; s < relationsOppositeSide.Count; s++)
// relationsOppositeSide[s].Factor = relationsOppositeSide[s].Distance / scaleRefScale;
// // Remove far relations
// //if (relationsSameSide.Count > 2) relationsSameSide.RemoveRange(2, relationsSameSide.Count - 2);
// //if (relationsOppositeSide.Count > 3) relationsOppositeSide.RemoveRange(3, relationsOppositeSide.Count - 3);
// // Ensure opposite leg assignment
// if (LALeg.OppositeLegIndex < 0)
// {
// LALeg.AssignOppositeLegIndex(relationsOppositeSide[0].Leg.LALeg.PlaymodeIndex);
// }
// }
// public float LastAllowFactor { get; private set; }
// public float LastPenalty { get; private set; }
// public float AllowAccum { get; private set; }
// float moveCulldown = -1f;
// float askingForDetachSince = -1f;
// bool wasAskingForDetach = false;
// internal void StepFactorsCompute()
// {
// float penalty = 0f;
// for (int i = 0; i < relationsOppositeSide.Count; i++)
// {
// var rel = relationsOppositeSide[i];
// penalty += ComputePenaltyForOppositeSide(rel, i);
// }
// for (int i = 0; i < relationsSameSide.Count; i++)
// {
// LegRelation rel = relationsSameSide[i];
// penalty += ComputePenaltyForSameSide(rel);
// }
// LastPenalty = penalty;
// LastAllowFactor = 1f - penalty;
// AllowAccum += LastAllowFactor * Owner.DeltaTime * 10f;
// AllowAccum = Mathf.Clamp01(AllowAccum);
// float stretch = LALeg.IKProcessor.GetStretchValue(LALeg._PreviousFinalIKPos);
// if (stretch > 0.95f )
// {
// if (stretch > 1f) stretch += 1f;
// AllowAccum += LastAllowFactor * Owner.DeltaTime * 10f * stretch;
// return;
// }
// if (AllowAccum < 0.99f) LALeg.G_StepHeatmapForceNOTDetach = true;
// }
// public float ComputePenaltyForSameSide(LegRelation rel)
// {
// float omFactor = 1f - rel.Factor;
// float penalty = 0f;
// if (rel.Leg.LALeg.G_Attached == false || rel.Leg.LALeg.G_StepHeatmapForceDetach)
// {
// float animationProgress = rel.Leg.LALeg.G_GlueInternalTransition * Owner.LegAnimatingSettings.AllowDetachBefore;
// float eval = Owner._StepHeatPenaltySameSideCurve.Evaluate(animationProgress);
// penalty += Mathf.Lerp(1f, omFactor,animationProgress) * eval * 1f;
// }
// else
// {
// float timeDiff = Time.time - rel.Leg.LALeg.G_LastAttachCompleteTime;
// penalty -= Mathf.Min(1f, timeDiff) * 0.1f * omFactor;
// }
// return penalty;
// }
// public float ComputePenaltyForOppositeSide(LegRelation rel, int i)
// {
// float omFactor = 1f - rel.Factor;
// float penalty = 0f;
// if (rel.Leg.LALeg.G_Attached == false || rel.Leg.LALeg.G_StepHeatmapForceDetach)
// {
// float animationProgress = rel.Leg.LALeg.G_GlueInternalTransition * Owner.LegAnimatingSettings.AllowDetachBefore;
// float eval = Owner._StepHeatPenaltyCurve.Evaluate(animationProgress);
// penalty += omFactor * eval;
// if (i == 0) AllowAccum = 0f;
// }
// else
// {
// float timeDiff = Time.time - rel.Leg.LALeg.G_LastAttachCompleteTime;
// penalty -= Mathf.Min(1f, timeDiff) * 0.05f * omFactor;
// }
// return penalty;
// }
// public class LegRelation
// {
// /// <summary> Relation with this leg </summary>
// public StepLeg Leg;
// public float Distance;
// public float Factor;
// public float WeightedDistance;
// public float WeightedFactor;
// public LegRelation(StepLeg with)
// {
// Leg = with;
// }
// }
// }
// #endregion
// }
// #region Editor Code
//#if UNITY_EDITOR
// public void StepHeatmap_DebugOnSceneView(int debugLeg = -1)
// {
// if (_stepHeatmap == null) return;
// _stepHeatmap.OnSceneView(debugLeg);
// }
//#endif
// #endregion
// }
//}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 12c3b190d2304b343b0b0bfc9ac71397
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: cfd3ee890695db04288f2898c5779a75
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,73 @@
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
//public float _Hips_LastHipsOffset { get; private set; } = 0f;
//float _Hips_StepHeightAdjustOffset = 0f;
//float _sd_Hips_StepHeightAdjustOffset = 0f;
//void Hips_Calc_StepAdjustTo(float yOffset)
//{
// //_Hips_StepHeightAdjustOffset = Mathf.MoveTowards(_Hips_StepHeightAdjustOffset,
// // yOffset, Mathf.LerpUnclamped(1f, 22f, HipsHeightStepSpeed) * DeltaTime);
// //return;
// if (HipsHeightStepSpeed >= 1f)
// {
// _Hips_StepHeightAdjustOffset = yOffset;
// return;
// }
// float landingBoost = GetLandingBoost();
// _Hips_StepHeightAdjustOffset = Mathf.SmoothDamp(_Hips_StepHeightAdjustOffset,
// yOffset, ref _sd_Hips_StepHeightAdjustOffset,
// Mathf.LerpUnclamped(0.4f, 0.01f, landingBoost)
// , float.MaxValue, DeltaTime);
//}
public float GetLandingBoost()
{
float landingBoost = HipsHeightStepSpeed;
if (IsGrounded && GroundedTime < 0.2f) landingBoost = Mathf.Max(HipsHeightStepSpeed, Mathf.LerpUnclamped(HipsHeightStepSpeed, 0.95f, 0.9f));
return landingBoost;
}
float HipsBlendWeight { get { return _MainBlend * HipsAdjustingBlend * HipsHeightStepBlend; } }
void Hips_Calc_BodyAdjust()
{
if (HipsHeightStepBlend <= 0f) return;
float bhipsOffset = HipsSetup.CalculateBodyAdjust();
Vector3 baseHipsOffset = Vector3.zero;
if (bhipsOffset != 0f && float.IsNaN(bhipsOffset) == false)
{
baseHipsOffset = Up * (bhipsOffset);
Hips.position += baseHipsOffset;
}
if (!_hipsHubs_using) return;
if (HipsHubsBlend < 0.0001f) return;
for (int h = 0; h < HipsHubs.Count; h++)
{
HipsHubs[h]._PreHipsAdjustPosition = HipsHubs[h].bone.position;
float hipsOffset = HipsHubs[h].CalculateBodyAdjust();
if (hipsOffset != 0f && float.IsNaN(hipsOffset) == false)
{
Vector3 offset = Up * (hipsOffset);
offset -= baseHipsOffset;
HipsHubs[h].bone.position += offset;
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5090e6e6712f34049af7118a4446fb09
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,72 @@
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
bool _updateHipsAdjustements = true;
protected void Hips_PreCalibrate()
{
//if (HipsAdjustingBlend * _MainBlendPlusGrounded <= 0f) { _updateHipsAdjustements = false; }
//else
_updateHipsAdjustements = true;
/*if (Calibrate) */ // Hips should be always precalibrated!
HipsSetup.PreCalibrate();
#region Precalibrate spine if used
if( Calibrate != ECalibrateMode.None )
{
if( SpineBone != null ) // Precalibrate spine
{
if( _spineBoneCalibrate.Transform == null ) _spineBoneCalibrate = new CalibrateTransform( SpineBone );
_spineBoneCalibrate.Calibrate();
if( ChestBone != null )
{
if( _ChestBoneCalibrate.Transform == null ) _ChestBoneCalibrate = new CalibrateTransform( ChestBone );
_ChestBoneCalibrate.Calibrate();
}
}
}
#endregion
Hips_Calc_PreRefreshVariables();
HipsHubs_PreCalibrate();
}
void Hips_Calc_Elasticity()
{
if( HipsSetup.HipsElasticityBlend > 0f )
{
Vector3 offsetPos = HipsSetup.HipsMuscle.Update( DeltaTime, _Hips_StabilityLocalOffset );
if( offsetPos.y > 0f )
{
offsetPos.y *= 1f - ImpulsesDampUpPushes;
}
if( HipsSetup.HipsElasticityBlend < 1f )
{
_Hips_FinalStabilityOffset = Vector3.LerpUnclamped( _Hips_StabilityLocalOffset, offsetPos, HipsSetup.HipsElasticityBlend );
}
else
{
_Hips_FinalStabilityOffset = offsetPos;
}
}
else
{
_Hips_FinalStabilityOffset = _Hips_StabilityLocalOffset;
}
_Hips_FinalStabilityOffset = RootToWorldSpaceVec( _Hips_FinalStabilityOffset );
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a72e730e8c0aec7488c91248a2769cfc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,195 @@
using System.Collections.Generic;
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
[Tooltip("If this model is created out of multiple leg bones hubs, you can define it here. Assigned transform must be parent of legs, in order to work properly!")]
public List<Transform> ExtraHipsHubs = new List<Transform>();
public enum EHipsHubsHandling
{
[Tooltip("Applying hips movement offset to the selected hub, in order to fix disconnected hips bones (rare case)")]
//[Tooltip("Basic mode is applying same offset to the hips hub like to the main hips bone")]
FixDisconnected,
[Tooltip("Detailed mode is computing hips hub offsets individually, giving more realistic effect but costs a bit more")]
Detailed
}
[Tooltip("Enter on the selected option on the right to see description")]
public EHipsHubsHandling HipsHubsHandling = EHipsHubsHandling.Detailed;
[Range(0f, 1f)] public float HipsHubsBlend = 1f;
[Tooltip("If leg hub is having backbones to compensate target rotation, you can controll the spine bend style with this curve")]
[FPD_FixedCurveWindow(0f, 0f, 1f, 3f)]
public AnimationCurve HubsBackBonesBlend = AnimationCurve.Linear(0f, 1f, 1f, 1f);
[Tooltip("Adding elasticity effect to the hub spine backbones adjustement animation")]
[Range(0f, 1f)] public float HubBackBonesElasticity = 0.1f;
public List<HipsReference> HipsHubs { get; private set; }
bool _hipsHubs_using = false;
void HipsHubs_Init()
{
_hipsHubs_using = false;
HipsHubs = new List<HipsReference>();
for (int i = 0; i < ExtraHipsHubs.Count; i++)
{
if (ExtraHipsHubs[i] == null) continue;
HipsReference hubRef = new HipsReference();
hubRef.Initialize(this, ExtraHipsHubs[i], BaseTransform);
hubRef.CopyMuscleSettingsFrom(HipsSetup);
HipsHubs.Add(hubRef);
}
if (ExtraHipsHubs.Count > 0) _hipsHubs_using = true;
HipsSetup.PrepareLegs();
for (int i = 0; i < HipsHubs.Count; i++) HipsHubs[i].PrepareHubBones();
}
void HipsHubs_PreCalibrate()
{
if (!_hipsHubs_using) return;
for (int h = 0; h < HipsHubs.Count; h++) HipsHubs[h].PreCalibrate();
}
void HipsHubs_CaptureAnimation()
{
if (!_hipsHubs_using) return;
for (int h = 0; h < HipsHubs.Count; h++) HipsHubs[h].Calibrate();
}
void HipsHubs_ApplyTransformations()
{
if (!_hipsHubs_using) return;
if (HipsHubsHandling == EHipsHubsHandling.FixDisconnected)
{
for (int h = 0; h < HipsHubs.Count; h++) HipsHubs_ApplyBasic(HipsHubs[h]);
return;
}
// Detailed Handling
for (int h = 0; h < HipsHubs.Count; h++) HipsHubs_ApplyDetailed(HipsHubs[h]);
}
void HipsHubs_ApplyBasic(HipsReference hub)
{
hub.bone.position += _LastAppliedHipsFinalOffset * HipsHubsBlend;
Quaternion targetRot = (_LastAppliedHipsFinalRotationOffset * _LastHipsRotationOffsetOutsideInfo) * hub.bone.rotation;
if (HipsHubsBlend > 0.999f)
hub.bone.rotation = targetRot;
else
{
hub.bone.rotation = Quaternion.Lerp(hub.bone.rotation, targetRot, HipsHubsBlend);
}
}
void HipsHubs_ApplyDetailed(HipsReference hub)
{
if (HipsHubsBlend < 0.0001f) return;
float blend = HipsHubsBlend * _MainBlend * IsGroundedBlend;
float stabilizingMultiplier = Mathf.LerpUnclamped(1f, StabilizeOnIsMoving, IsMovingBlend);
Vector3 hubOffset = Vector3.zero;
hubOffset += hub.CalculateCenterOfMassStability(stabilizingMultiplier);
Vector3 legMovePush = hub.CalculateGlueMovePush() * PushHipsOnLegMove;
legMovePush = hub.SmoothPushOffset(legMovePush, Mathf.LerpUnclamped(0.125f, 0.025f, PushReactionSpeed));
hubOffset += (_MainBlendPlusGrounded * RootToWorldSpaceVec(legMovePush));
Vector3 stretchPreventer = hub.CalculateStretchPreventerOffset();
hubOffset += (_MainBlendPlusGrounded * stretchPreventer * HipsStretchPreventer * stabilizingMultiplier);
hubOffset = hub.AnimateOffset(hubOffset);
hub.HipsMuscle.Update(DeltaTime, hubOffset);
hubOffset = hub.HipsMuscle.ProceduralPosition;
hubOffset += hub.ExtraNonElasticOffset;
hub.ExtraNonElasticOffset = Vector3.zero;
Vector3 extraAdjustHelper = Vector3.zero;
if (HipsSetup._Hips_LastHipsOffset > 0f) extraAdjustHelper.y -= HipsSetup._Hips_LastHipsOffset * 0.1f;
if (hub._Hips_LastHipsOffset < 0f) extraAdjustHelper.y += hub._Hips_LastHipsOffset * 0.1f;
Vector3 stretchReAdj = hub.CalculateStretchReadjust();
Vector3 hubOffsetWorld = RootToWorldSpaceVec(hubOffset + extraAdjustHelper + stretchReAdj);
Vector3 targetPos = hub.bone.position + hubOffsetWorld;
hub.bone.localPosition = hub.LastKeyframeLocalPosition;
hub.bone.position += RootToWorldSpaceVec(extraAdjustHelper);
#region Realign with parent rotation
Quaternion preRot = hub.bone.rotation;
//float diff = Vector3.Distance(targetPos, hub.LastKeyframePosition) / ScaleReference;
//diff = Mathf.InverseLerp(0.1f, 0.7f, diff);
float diff = 1f;
if (diff > 0)
if (hub.HubBackBones.Count > 0)
{
float countD = (float)hub.HubBackBones.Count - 1;
if (countD == 0f) countD = 1f;
float id = 0;
for (int b = hub.HubBackBones.Count - 1; b >= 0; b--)
{
var backBone = hub.HubBackBones[b];
// From main hub towards this modified hub position
Vector3 toHubB = (backBone.frontBone.position - backBone.bone.position).normalized;
Vector3 toHubNewB = (targetPos - backBone.bone.position).normalized;
float dot = Vector3.Dot(toHubB, toHubNewB);
float soother = 0f;
if (dot < 0.985f)
{
// Check if spine is not being rotated too much to the sides/backwards
Vector3 toHubLocal = ToRootLocalSpaceVec(toHubB);
Vector3 toHubNewLocal = ToRootLocalSpaceVec(toHubNewB);
toHubLocal.y = 0f;
toHubNewLocal.y = 0f;
float localDot = Vector3.Dot(toHubLocal.normalized, toHubNewLocal.normalized);
soother = Mathf.InverseLerp(0.985f, 0.5f, localDot);
toHubNewB = Vector3.Slerp(toHubNewB, toHubB, soother);
}
toHubNewB = backBone.AnimateTargetDirection(toHubNewB);
Vector3 finalDir = Vector3.LerpUnclamped(toHubB, toHubNewB, (diff * blend / countD) * (HubsBackBonesBlend.Evaluate(id / countD)) * (1f - soother));
Quaternion fromTo = Quaternion.FromToRotation(toHubB, finalDir);
backBone.bone.rotation = fromTo * backBone.bone.rotation;
id += 1f;
}
}
Quaternion compensateRot = Quaternion.Inverse(hub._LastHipsRotationOffsetOutsideInfo);
hub.bone.rotation = compensateRot * Quaternion.SlerpUnclamped(hub.bone.rotation, preRot, 0.75f * HipsHubsBlend);
hub._LastHipsRotationOffsetOutsideInfo = Quaternion.identity;
#endregion
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d441d41f8dc2aa14fb9d3522f416c724
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,394 @@
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
public partial class HipsReference
{
Vector3 _Hips_StabilityLocalAdjustement = Vector3.zero;
Vector3 _Hips_sd_StabilAdjustm = Vector3.zero;
public Vector3 _Get_Hips_StabilityLocalAdjustement { get { return _Hips_StabilityLocalAdjustement; } }
/// <summary> Local Space Stability Adjust </summary>
public Vector3 CalculateCenterOfMassStability( float stabilizingMultiplier )
{
if( Owner.StabilizeCenterOfMass > 0f )
{
// Compute center of weight pose difference (local space)
Vector3 stabilityDiff = new Vector3( 0f, 0f, 0f );
float legsDiv = ChildLegs.Count;
if( Owner.StabilityAlgorithm == EStabilityMode.Biped_Deprecated )
{
for( int l = 0; l < ChildLegs.Count; l++ )
{
var leg = ChildLegs[l];
Vector3 footmiddleOff = leg.AnkleH.Bone.TransformVector( leg.AnkleToFeetEnd * 0.6f );
Vector3 legIKReferencePos = leg._PreviousFinalIKPosForStability;
Vector3 footLocalPos = Owner.ToRootLocalSpace( legIKReferencePos + footmiddleOff );
Vector3 initRefPose = leg.InitialPosInRootSpace;
initRefPose.y += _Hips_LastHipsOffset;
Vector3 stablePose;
if( Owner.AnimationIsStablePose >= 1f ) stablePose = leg.AnkleH.LastKeyframeRootPos;
else if( Owner.AnimationIsStablePose <= 0f ) stablePose = initRefPose;
else stablePose = Vector3.LerpUnclamped( initRefPose, leg.AnkleH.LastKeyframeRootPos, Owner.AnimationIsStablePose );
Vector3 target = footLocalPos - stablePose;
target.y *= 0.25f;
stabilityDiff += target * leg.BlendWeight * 0.5f * ( stabilizingMultiplier * Owner.StabilizeCenterOfMass );
}
stabilityDiff.y /= legsDiv;
}
else if( Owner.StabilityAlgorithm == EStabilityMode.Universal )
{
Vector3 pelvinInLocal = LastRootLocalPos;
//float lowestLegLocalPos = float.MaxValue;
for( int l = 0; l < ChildLegs.Count; l++ )
{
var leg = ChildLegs[l];
#region Reference Local Foot Position (keyframe or initial TPose) - stablePose
Vector3 initRefPose = leg.InitialPosInRootSpace;
initRefPose.y += _Hips_LastHipsOffset;
Vector3 stablePose;
if( Owner.AnimationIsStablePose >= 1f ) stablePose = leg.AnkleH.LastKeyframeRootPos;
else if( Owner.AnimationIsStablePose <= 0f ) stablePose = initRefPose;
else stablePose = Vector3.LerpUnclamped( initRefPose, leg.AnkleH.LastKeyframeRootPos, Owner.AnimationIsStablePose );
#endregion
Vector3 stableDiff = pelvinInLocal - stablePose;
Vector3 footLocalPos = Owner.ToRootLocalSpace( leg._PreviousFinalIKPosForStability );
//if (footLocalPos.y < lowestLegLocalPos) lowestLegLocalPos = footLocalPos.y;
Vector3 currDiff = pelvinInLocal - footLocalPos;
Vector3 target = stableDiff - currDiff;
target.y *= 0.25f;
stabilityDiff += ( target * leg.BlendWeight * ( stabilizingMultiplier * Owner.StabilizeCenterOfMass ) ) / legsDiv;
}
}
if( stabilityDiff.y > 0f ) // Prevent feet off-ground
{
stabilityDiff.y = 0f; //*= Mathf.InverseLerp(0, 0.1f, stabilityDiff.y);
}
// Apply calculated stability offset smoothing
if( Owner.StabilizingSpeed < 1f )
{
float targetDuration = 0f;
if( Owner.StabilizingSpeed < 1f ) targetDuration = 0.001f + ( 1f - Owner.StabilizingSpeed ) * 0.4f;
Owner.ValueTowards( ref _Hips_StabilityLocalAdjustement, stabilityDiff, ref _Hips_sd_StabilAdjustm, targetDuration );
}
else
{
_Hips_StabilityLocalAdjustement = stabilityDiff;
}
}
else
{
_Hips_StabilityLocalAdjustement = Vector3.zero;
}
return _Hips_StabilityLocalAdjustement;
}
Vector3 _stretchPreventerOff = Vector3.zero;
/// <summary> Stretcher offset in local space </summary>
public Vector3 CalculateStretchPreventerOffset()
{
if( Owner.HipsStretchPreventer < 0.0001f ) return Vector3.zero;
Vector3 stretchPreventerOffset = Vector3.zero;
float stretched = 0f;
Vector3 hubFloorPos = LastRootLocalPos;
hubFloorPos.y = 0f;
hubFloorPos = Owner.baseTransform.TransformPoint( hubFloorPos );
for( int l = 0; l < ChildLegs.Count; l++ )
{
var leg = ChildLegs[l];
float stretchFactor = leg.IKProcessor.GetStretchValue( leg._PreviousFinalIKPosForStability );
if( stretchFactor > Owner.LimitLegStretch * 0.975f )
{
stretched += 1f;
float diff = stretchFactor - ( Owner.LimitLegStretch * 0.975f );
Vector3 localOffset = hubFloorPos - leg._PreviousFinalIKPosForStability;
localOffset = Owner.ToRootLocalSpaceVec( localOffset );
if( localOffset.y > 0f ) localOffset.y = 0f;
localOffset.x *= -0.6f;
localOffset.z *= -0.6f;
stretchPreventerOffset += localOffset * Mathf.Clamp( diff * 3f, 0f, 0.5f );
}
}
if( Owner.StretchPreventerSpeed < 1f )
{
float lerpSPD = Mathf.Lerp( 8f, 40f, Owner.StretchPreventerSpeed ) * Owner.DeltaTime;
if( stretched > 0f )
_stretchPreventerOff = Vector3.Lerp( _stretchPreventerOff, stretchPreventerOffset / stretched, lerpSPD );
else
_stretchPreventerOff = Vector3.Lerp( _stretchPreventerOff, Vector3.zero, lerpSPD * 0.7f );
}
else
{
_stretchPreventerOff = stretchPreventerOffset;
}
return _stretchPreventerOff;
}
/// <summary> Push Local Space </summary>
public Vector3 CalculateGlueMovePush()
{
Vector3 stabilityDiff = Vector3.zero;
if( Owner.GlueBlend < 0.0001f ) return stabilityDiff;
for( int l = 0; l < ChildLegs.Count; l++ )
{
var leg = ChildLegs[l];
#region Attachement and alignment blending transitioning
if( ( leg.G_Attached || leg.G_DuringLegAdjustMovement ) )
{
if( leg.G_LastLegMoveDistanceFactor > leg.LegAnimatingSettings.DoStepAnimationOnDistanceFactor )
if( leg.G_GlueInternalTransition > 0f && leg.G_GlueInternalTransition < 1f )
{
if( leg.G_HandlerExecutingLegAnimationMode == EGlueMode.Idle )
{
Vector3 footmiddleOff = leg.AnkleH.Bone.TransformVector( leg.AnkleToFeetEnd );
Vector3 footLocalPos = Owner.ToRootLocalSpace( leg._PreviousFinalIKPosForStability + footmiddleOff );
footLocalPos.z = -footLocalPos.z;
float ev = Owner.BaseLegAnimating.PushHipsOnMoveCurve.Evaluate( leg.G_GlueInternalTransition );
Vector3 legPush = -footLocalPos * ev * 1f;
legPush.y -= ev * leg.G_LastLegMoveDistanceFactor * Owner.ScaleReferenceNoScale * 0.75f;
Vector3 extraOffset;
if( Owner.NormalizePush )
{
float normFactor = Mathf.Min( 1f, legPush.magnitude / ( Owner.ScaleReferenceNoScale * 0.33f ) );
normFactor *= normFactor;
extraOffset = legPush.normalized * Owner.ScaleReferenceNoScale * 0.33f * normFactor;
}
else
extraOffset = legPush;
extraOffset.y *= Owner.PushYBlend;
stabilityDiff += extraOffset * leg.BlendWeight;
}
}
}
#endregion
}
return stabilityDiff;
}
/// <summary> Last applied height offset (with blending) </summary>
public float _Hips_LastHipsOffset { get; private set; } = 0f;
/// <summary> Not blended height offset </summary>
public float _Hips_StepHeightAdjustOffset { get; private set; } = 0f;
/// <summary> Extra offset to apply which is ignoring elastic muscle motion </summary>
public Vector3 ExtraNonElasticOffset { get; internal set; }
public Vector3 _PreHipsAdjustPosition { get; internal set; }
float _sd_Hips_StepHeightAdjustOffset = 0f;
void AnimateStepAdjustTo( float yOffset )
{
if( Owner.HipsHeightStepSpeed >= 1f )
{
_Hips_StepHeightAdjustOffset = yOffset;
return;
}
float landingBoost = Owner.GetLandingBoost();
// Leg height follow adjust hips
if( Owner.HipsAdjustStyle == EHipsAdjustStyle.FollowLegHeight )
if( yOffset < _Hips_StepHeightAdjustOffset )
{
if( _h_lowestHitLeg != -1 )
{
Vector3 localPos = Owner.Legs[_h_lowestHitLeg]._PreviousFinalIKPos;
localPos = Owner.ToRootLocalSpace( localPos );
localPos.y -= Owner.ScaleReferenceNoScale * 0.325f;
if( localPos.y > yOffset )
{
yOffset = localPos.y;
}
}
}
_Hips_StepHeightAdjustOffset = Mathf.SmoothDamp( _Hips_StepHeightAdjustOffset,
yOffset, ref _sd_Hips_StepHeightAdjustOffset,
Mathf.LerpUnclamped( 0.4f, 0.01f, landingBoost )
, 1000000f, Owner.DeltaTime );
_h_lowestHitLeg = -1;
}
int _h_lowestHitLeg = -1;
public float CalculateBodyAdjust()
{
_Hips_LastHipsOffset = 0f;
if( Owner.HipsHeightStepBlend <= 0f ) return 0f;
if( Owner.IsGrounded )
{
Vector3 lowestHitLocal = new Vector3( float.MaxValue, float.MaxValue, float.MaxValue );
Vector3 lowestHitLocalStepUp = new Vector3( float.MaxValue, float.MaxValue, float.MaxValue );
// Finding lowest raycast hit in max body step down range
for( int l = 0; l < ChildLegs.Count; l++ )
{
var leg = ChildLegs[l];
if( leg.RaycastHitted == false ) continue;
Vector3 groundHit = leg.LastGroundHit.point;
groundHit = Owner.ToRootLocalSpace( groundHit );
if( groundHit.y <= 0f ) // Below Ground
{
if( -groundHit.y < Owner.BodyStepDown * Owner.ScaleReferenceNoScale )
{
if( groundHit.y < lowestHitLocal.y )
{
lowestHitLocal = groundHit;
_h_lowestHitLeg = l;
}
}
}
else // Above Ground
{
if( groundHit.y < Owner.MaxBodyStepUp * Owner.ScaleReferenceNoScale )
{
if( groundHit.y < lowestHitLocal.y ) lowestHitLocalStepUp = groundHit;
}
}
}
bool hipsAdjusted = false;
if( lowestHitLocal.x != float.MaxValue ) // Adjust hips down
{
if( Owner.BodyStepDown > 0f )
if( lowestHitLocal.y <= 0f )
{
AnimateStepAdjustTo( lowestHitLocal.y );
hipsAdjusted = true;
}
}
if( !hipsAdjusted ) // Adjust hips up
{
if( Owner.MaxBodyStepUp > 0f )
if( lowestHitLocalStepUp.x != float.MaxValue )
{
AnimateStepAdjustTo( lowestHitLocalStepUp.y );
hipsAdjusted = true;
}
}
if( !hipsAdjusted ) // Return to default hips pose
{
AnimateStepAdjustTo( 0f );
}
}
else
{
AnimateStepAdjustTo( 0f );
}
float hipsWeight = Owner.HipsBlendWeight * Owner._MainBlend * Owner.IsGroundedBlend * Owner.RagdolledDisablerBlend;
_Hips_LastHipsOffset = ( _Hips_StepHeightAdjustOffset * Owner.baseTransform.lossyScale.y ) * hipsWeight;
return _Hips_LastHipsOffset;
}
Vector3 _reAdjustLocal = Vector3.zero;
Vector3 _sd_readj = Vector3.zero;
public Vector3 CalculateStretchReadjust()
{
Vector3 stretchReAdjust = Vector3.zero;
for( int l = 0; l < ChildLegs.Count; l++ )
{
var leg = ChildLegs[l];
Vector3 ikRefPos = leg._FinalIKPos - stretchReAdjust;
float legStretch = leg.IKProcessor.GetStretchValue( ikRefPos );
if( legStretch > Owner.LimitLegStretch )
{
Vector3 nonStretchedPos = leg.IKProcessor.GetNotStretchedPositionTowards( ikRefPos, Owner.LimitLegStretch );
Vector3 diff = ikRefPos - nonStretchedPos;
stretchReAdjust += diff;
}
}
stretchReAdjust = Owner.ToRootLocalSpaceVec( stretchReAdjust );
_reAdjustLocal = Vector3.SmoothDamp( _reAdjustLocal, stretchReAdjust, ref _sd_readj, 0.1f, 10000000f, Owner.DeltaTime );
return _reAdjustLocal;
}
Vector3 _pushSmoothed = Vector3.zero;
Vector3 _sd_pushSmoothed = Vector3.zero;
public Vector3 SmoothPushOffset( Vector3 pushLocalOffset, float pushDuration )
{
Owner.ValueTowards( ref _pushSmoothed, pushLocalOffset, ref _sd_pushSmoothed, pushDuration );
return _pushSmoothed;
}
public Vector3 AnimateOffset( Vector3 hubOffset )
{
return hubOffset;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 504ee82cb24632d4a81615c61089a0d9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,267 @@
using FIMSpace.FTools;
using System;
using System.Collections.Generic;
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
public HipsReference HipsSetup = new HipsReference();
[System.Serializable]
public partial class HipsReference
{
public LegsAnimator Owner { get; private set; }
[Tooltip("Applying elasticity algorithm on the pelvis bone align motion, to make it look more organic.")]
[FPD_Suffix(0f, 1f)] public float HipsElasticityBlend = 1f;
//[Range(0f, 1f)] public float HipsMotionInfluence = 1f;
public FMuscle_Vector3 HipsMuscle;
[FPD_Suffix(0f, 1f)] public float HipsRotElasticityBlend = 0f;
public FMuscle_Quaternion HipsRotMuscle;
[NonSerialized] public Vector3 LastKeyframePosition;
[NonSerialized] public Vector3 LastKeyframeLocalPosition;
[NonSerialized] public Quaternion LastKeyframeRotation;
[NonSerialized] public Quaternion LastKeyframeLocalRotation;
/// <summary> Keyframe, legs animator local space </summary>
[NonSerialized] public Vector3 LastRootLocalPos;
public List<Leg> ChildLegs { get; private set; }
/// <summary> Its one when hips is in the same height as in initial pose, zero when hips are in zero local position </summary>
[NonSerialized] public float LastHipsHeightDiff;
[NonSerialized] public Vector3 InitHipsPositionRootSpace;
[NonSerialized] public float InitialHipsHeightLocal;
[NonSerialized] internal Quaternion _LastHipsRotationOffsetOutsideInfo = Quaternion.identity;
Transform root;
public Transform bone { get; private set; }
private Vector3 initLocalPos;
private Quaternion initLocalRot;
public UniRotateBone UniRotate { get; private set; }
#region Setup
public void Initialize(LegsAnimator owner, Transform bone, Transform root)
{
Owner = owner;
this.bone = bone;
this.root = root;
initLocalPos = bone.localPosition;
initLocalRot = bone.localRotation;
ExtraNonElasticOffset = Vector3.zero;
_Hips_StabilityLocalAdjustement = Vector3.zero;
_Hips_sd_StabilAdjustm = Vector3.zero;
InitHipsPositionRootSpace = root.InverseTransformPoint(bone.position);
InitialHipsHeightLocal = InitHipsPositionRootSpace.y;
if (HipsMuscle == null) HipsMuscle = new FMuscle_Vector3();
if (HipsRotMuscle == null) HipsRotMuscle = new FMuscle_Quaternion();
HipsMuscle.Initialize(Vector3.zero);
HipsRotMuscle.Initialize(Quaternion.identity);
UniRotate = new UniRotateBone(bone, root);
Calibrate();
}
internal void PrepareLegs()
{
ChildLegs = new List<Leg>();
if (Owner._hipsHubs_using == false) ChildLegs = Owner.Legs;
else
{
// Individual child legs for leg hubs
for (int l = 0; l < Owner.Legs.Count; l++)
{
bool? isPar = IsFirstParent(Owner.Legs[l], bone);
if (isPar == true)
{
ChildLegs.Add(Owner.Legs[l]);
}
else if (isPar == null) // Not found any hub to be the parent - add to the main hub
{
if ( this == Owner.HipsSetup)
ChildLegs.Add(Owner.Legs[l]);
}
}
}
for (int l = 0; l < ChildLegs.Count; l++)
{
ChildLegs[l].AssignParentHub(this);
}
}
public class HipsHubBackbone
{
public LegsAnimator Owner { get; private set; }
public Transform bone { get; private set; }
public Quaternion initialLocalRotation { get; private set; }
public Vector3 keyframePosition { get; private set; }
public Transform frontBone;
public Quaternion TargetRotation { get; internal set; }
public HipsHubBackbone(LegsAnimator owner, Transform b)
{
Owner = owner;
bone = b;
initialLocalRotation = b.localRotation;
_FMuscle = new FMuscle_Vector3();
_FMuscle.Initialize(Vector3.zero);
}
public void PreCalibrate()
{
bone.localRotation = initialLocalRotation;
}
public void Calibrate()
{
keyframePosition = bone.position;
}
Vector3 _dir = Vector3.zero;
Vector3 _sd_dir = Vector3.zero;
FMuscle_Vector3 _FMuscle;
public Vector3 AnimateTargetDirection(Vector3 toHubNewB)
{
if (Owner.HubBackBonesElasticity < 0.0001f) return toHubNewB;
else
{
if ( Owner.HubBackBonesElasticity <= 0.1f)
{
_dir = Vector3.SmoothDamp(_dir, toHubNewB, ref _sd_dir, 0.001f + Owner.HubBackBonesElasticity, 10000000f, Owner.DeltaTime);
}
else
{
_dir = Vector3.LerpUnclamped(toHubNewB, _FMuscle.Update(Owner.DeltaTime, toHubNewB), Owner.HubBackBonesElasticity);
}
}
return _dir;
}
}
public List<HipsHubBackbone> HubBackBones { get; private set; }
internal void PrepareHubBones()
{
PrepareLegs();
HubBackBones = new List<HipsHubBackbone>();
Transform preBone = bone;
Transform parent = bone.parent;
while (parent != null)
{
bool hardBreak = false;
for (int o = 0; o < Owner.HipsHubs.Count; o++)
{
if (parent == Owner.HipsHubs[o].bone) { hardBreak = true; break; }
}
if (hardBreak) break;
HipsHubBackbone bBone = new HipsHubBackbone(Owner, parent);
bBone.frontBone = preBone;
HubBackBones.Add(bBone);
if (parent == Owner.HipsSetup.bone) break;
preBone = parent;
parent = parent.parent;
}
}
bool? IsFirstParent(Leg leg, Transform hub)
{
if ( leg.BoneStart == null ) return false;
Transform t = leg.BoneStart;
while(t != null)
{
if (t == hub) return true;
else
{
if (t == Owner.Hips) return false;
for (int i = 0; i < Owner.ExtraHipsHubs.Count; i++)
if (t == Owner.ExtraHipsHubs[i]) return false;
}
t = t.parent;
}
return null;
}
public void Reset()
{
Calibrate();
_Hips_LastHipsOffset = 0f;
}
public void PreCalibrate()
{
UniRotate.PreCalibrate();
//bone.localPosition = initLocalPos;
//bone.localRotation = initLocalRot;
if( Owner.Calibrate != ECalibrateMode.FixedCalibrate )
UniRotate.PreCalibrate();
else
{
bone.localPosition = LastKeyframeLocalPosition;
bone.localRotation = LastKeyframeLocalRotation;
}
if ( HubBackBones != null) for (int h = 0; h < HubBackBones.Count; h++) HubBackBones[h].PreCalibrate();
}
public void Calibrate()
{
LastKeyframePosition = bone.position;
LastKeyframeLocalPosition = bone.localPosition;
LastKeyframeLocalRotation = bone.localRotation;
LastKeyframeRotation = bone.rotation;
LastRootLocalPos = Owner.ToRootLocalSpace(LastKeyframePosition);
LastHipsHeightDiff = GetHeightDiff(LastRootLocalPos.y);
if (HubBackBones != null) for (int h = 0; h<HubBackBones.Count; h++) HubBackBones[h].Calibrate();
}
/// <summary> Its one when rootSpaceHeight is in the same height as in initial pose hips height, zero when rootSpaceHeight is in zero local position height </summary>
public float GetHeightDiff(float rootSpaceHeight)
{
return Mathf.InverseLerp(0f, InitialHipsHeightLocal, rootSpaceHeight);
}
#endregion
public void CopyMuscleSettingsFrom(HipsReference hipsSetup)
{
HipsMuscle.Acceleration = hipsSetup.HipsMuscle.Acceleration;
HipsMuscle.AccelerationLimit = hipsSetup.HipsMuscle.AccelerationLimit;
HipsMuscle.Damping = hipsSetup.HipsMuscle.Damping;
HipsMuscle.BrakePower = hipsSetup.HipsMuscle.BrakePower;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7a15567849024d14a9b53feafc6be386
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,103 @@
using FIMSpace.FTools;
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
public enum EStabilityMode
{
Biped_Deprecated, Universal
}
public EStabilityMode StabilityAlgorithm = EStabilityMode.Universal;
Vector3 _Hips_StabilityLocalOffset = Vector3.zero;
FMuscle_Eulers _Hips_RotationMuscle;
public Vector3 _Get_Hips_StabilityLocalOffset { get { return _Hips_StabilityLocalOffset; } }
Vector3 _Hips_FinalStabilityOffset = Vector3.zero;
//Vector3 _Hips_StabilityLocalAdjustement = Vector3.zero;
public Vector3 _Get_Hips_StabilityLocalAdjustement { get { return HipsSetup._Get_Hips_StabilityLocalAdjustement; } }
//public Vector3 _Get_Hips_StabilityLocalAdjustement { get { return _Hips_StabilityLocalAdjustement; } }
//Vector3 _Hips_sd_StabilAdjustm = Vector3.zero;
//Vector3 _Hips_PushLocalOffset = Vector3.zero;
//Vector3 _Hips_sd_PushOffset = Vector3.zero;
void Initialize_Stability()
{
_Hips_RotationMuscle = new FMuscle_Eulers();
_Hips_RotationMuscle.Initialize(Vector3.zero);
}
void Hips_Calc_PreRefreshVariables()
{
_Hips_StabilityLocalOffset = Vector3.zero; // Reset before adjustement calculations
_Hips_Modules_ExtraRotOffset = Vector3.zero; // Reset before adjustement calculations
}
void Hips_Calc_Stabilize()
{
float baseBlend = _MainBlendPlusGrounded;
float stabilizingMultiplier = Mathf.LerpUnclamped(1f, StabilizeOnIsMoving, IsMovingBlend);
HipsSetup.CalculateCenterOfMassStability(stabilizingMultiplier);
// Push hips on leg move hips motion smoothing
float pushingDuration = 0f;
if (PushReactionSpeed < 1f) pushingDuration = Mathf.LerpUnclamped(0.125f, 0.025f, PushReactionSpeed);
// Hips motion basing on idle glue leg step animation
Vector3 legMovePush = HipsSetup.CalculateGlueMovePush();
//ValueTowards(ref _Hips_PushLocalOffset, legMovePush, ref _Hips_sd_PushOffset, pushingDuration);
Vector3 legPushLocalOffset = HipsSetup.SmoothPushOffset(legMovePush, pushingDuration);
// Finalize
if (HipsStretchPreventer > 0f)
{
Vector3 stretchOffset = HipsSetup.CalculateStretchPreventerOffset();
_Hips_StabilityLocalOffset += (_MainBlendPlusGrounded * stretchOffset * HipsStretchPreventer * stabilizingMultiplier);
}
// Push effect - base object translation based
if (MotionInfluence.AdvancedInfluence || MotionInfluence.AxisMotionInfluence.x > 0f)
_Hips_StabilityLocalOffset += MotionInfluence.CalculateInversedInfluence();
// Apply stability and push offsets
_Hips_StabilityLocalOffset += _Get_Hips_StabilityLocalAdjustement;
_Hips_StabilityLocalOffset += legPushLocalOffset * PushHipsOnLegMove;
// Custom local offsets apply
if (ExtraPelvisOffset != Vector3.zero) if (IsGroundedBlend > 0f)
Hips.position += RootToWorldSpaceVec(ExtraPelvisOffset * baseBlend);
}
void ValueTowards(ref Vector3 value, Vector3 target, ref Vector3 sd, float duration)
{
ValueTowards(ref value, target, ref sd, duration, DeltaTime);
}
void ValueTowards(ref Vector3 value, Vector3 target, ref Vector3 sd, float duration, float delta)
{
if (duration < 1f)
{
value = Vector3.SmoothDamp(value,
target, ref sd, duration, 10000000f, delta);
}
else
{
value = target;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f5aeac32b04c493468a2b46c130c53c0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,159 @@
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
[Tooltip("Use hips step adjustements and the stability algorithms")]
public bool UseHips = true;
//[Tooltip("Overall blend value for the hips step adjustements and for the stability algorithms")]
//[FPD_Suffix(0f, 1f)] public float HipsAdjustingBlend = 1f;
public float HipsAdjustingBlend
{
get { return UseHips ? (1f) : 0f; }
}
[Tooltip("Whole body lift effect blend")]
[FPD_Suffix(0f, 1f)] public float HipsHeightStepBlend = 1f;
[Tooltip("How fast body should adjust up/down")]
[Range(0f, 1f)] public float HipsHeightStepSpeed = 0.7f;
public enum EHipsAdjustStyle
{
SmoothDamp, FollowLegHeight
}
public EHipsAdjustStyle HipsAdjustStyle = EHipsAdjustStyle.SmoothDamp;
[Tooltip("Adjusting hips to keep body balance pose")]
[FPD_Suffix(0f, 1f)] public float StabilizeCenterOfMass = 0.45f;
[Tooltip("Blend stability pose reference from: initial pose to: current animator pose")]
[Range(0f, 1f)] public float AnimationIsStablePose = 0.75f;
[Tooltip("How fast body should adjust to the stability pose / to stretch preventer pose")]
[Range(0f, 1f)] public float StabilizingSpeed = 0.375f;
[Tooltip("Simulating body behaviour when doing leg steps")]
[Range(0f, 1f)] public float PushHipsOnLegMove = 0.1f;
[Tooltip("If your setup contains more than 2 legs it can be helpful to prevent overlapping pushes of multiple legs")]
public bool NormalizePush = false;
[Tooltip("Related with 'Push Hips On Leg Move' parameter above. How rapidly the pelvis push effect should be animated.")]
[Range(0f, 1f)] public float PushReactionSpeed = 0.3f;
[Tooltip("If Push in Y axis seems to be too strong, you can calm it down with this parameter")]
[Range(0f, 2f)] public float PushYBlend = 1f;
[Space(3)]
[Tooltip("Auto adjust hips to prevent leg stretching poses")]
[Range(0f, 1f)] public float HipsStretchPreventer = 0.15f;
public float StretchPreventerSpeed = 0.8f;
[Space(7)]
[Tooltip("Some of the stabilizing features may be not wanted when your character is running, you can blend them automatically to desired amount with this slider (you need to implement IsGrounded/IsMoving controls to give Legs Animator information about your character movement state)")]
[FPD_Suffix(0f, 1f)] public float StabilizeOnIsMoving = 0.5f;
//[Tooltip("Rotate hips accordingly to the stability offsets")]
//[FPD_Suffix(-1f, 1f)] public float UseHipsRotation = 0.0f;
//[Tooltip("Helper spine bone to restore it after hips rotations")]
//public Transform HipsChildSpineBone = null;
//[Tooltip("How much spine pose should be restored after hips rotation")]
//[FPD_Suffix(0f, 1f)] public float CompensateChildBone = 0.5f;
public void Hips_PreLateUpdate()
{
if (!_updateHipsAdjustements) return;
HipsSetup.Calibrate();
HipsHubs_CaptureAnimation();
}
public void Hips_LateUpdate()
{
if (!_updateHipsAdjustements) return;
Hips_Calc_BodyAdjust();
}
public void Hips_PostLateUpdate()
{
if (!_updateHipsAdjustements) return;
Hips_Calc_Stabilize();
Hips_Calc_UpdateImpulses();
Hips_Calc_ApplyImpulsesInherit();
Hips_Calc_Elasticity();
Hips_Calc_Apply();
Hips_Calc_ApplyImpulses();
Hips_ApplyTransformations();
}
protected virtual void Hips_ApplyTransformations()
{
if (float.IsNaN(_LastAppliedHipsFinalPosition.x) || float.IsNaN(_LastAppliedHipsFinalPosition.y) || float.IsNaN(_LastAppliedHipsFinalPosition.z))
{
// Reset hips if some unexepcted NaN exception occurs
_LastAppliedHipsFinalPosition = RootToWorldSpace(HipsSetup.InitHipsPositionRootSpace);
if (float.IsNaN(_LastAppliedHipsFinalPosition.x) || float.IsNaN(_LastAppliedHipsFinalPosition.y) || float.IsNaN(_LastAppliedHipsFinalPosition.z))
// If there is still NaN, there is something wrong in the init setup, so let's just hard reset it
_LastAppliedHipsFinalPosition = Vector3.zero;
}
if (_Hips_Modules_ExtraRotOffset != Vector3.zero)
{
float blend = _MainBlend * ImpulsesPowerMultiplier;
Vector3 angles = _Hips_Modules_ExtraRotOffset;
Quaternion hipsRotationOffset = Quaternion.identity;
if (angles.z != 0) hipsRotationOffset *= Quaternion.AngleAxis(angles.z * blend, BaseTransform.right);
if (angles.x != 0) hipsRotationOffset *= Quaternion.AngleAxis(angles.x * blend, BaseTransform.forward);
if (angles.y != 0) hipsRotationOffset *= Quaternion.AngleAxis(angles.y * blend, BaseTransform.up);
_LastAppliedHipsFinalRotationOffset = hipsRotationOffset;
Quaternion newHipsRot = hipsRotationOffset * Hips.rotation;
Hips.SetPositionAndRotation(_LastAppliedHipsFinalPosition, newHipsRot);
}
else
{
Hips.position = _LastAppliedHipsFinalPosition;
}
_LastAppliedHipsFinalOffset = _LastAppliedHipsFinalPosition - HipsSetup.LastKeyframePosition;
HipsHubs_ApplyTransformations();
Hips_Finalize();
}
protected virtual void Hips_Finalize()
{
_LastHipsRotationOffsetOutsideInfo = Quaternion.identity;
}
Vector3 _LastAppliedHipsStabilityOffset = Vector3.zero;
internal Vector3 _LastAppliedHipsFinalPosition = Vector3.zero;
internal Vector3 _LastAppliedHipsFinalOffset = Vector3.zero;
internal Quaternion _LastAppliedHipsFinalRotationOffset = Quaternion.identity;
internal Quaternion _LastHipsRotationOffsetOutsideInfo = Quaternion.identity;
public Vector3 _Hips_Modules_ExtraWOffset = Vector3.zero;
public Vector3 _Hips_Modules_ExtraRotOffset = Vector3.zero;
void Hips_Calc_Apply()
{
_LastAppliedHipsFinalOffset = Vector3.zero;
_LastAppliedHipsFinalRotationOffset = Quaternion.identity;
_LastAppliedHipsFinalPosition = Hips.position;
_LastAppliedHipsStabilityOffset = _Hips_FinalStabilityOffset * _MainBlendPlusGrounded * HipsAdjustingBlend;
_LastAppliedHipsFinalPosition += _LastAppliedHipsStabilityOffset;
_LastAppliedHipsFinalPosition += _Hips_Modules_ExtraWOffset;
_Hips_Modules_ExtraWOffset = Vector3.zero;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cfbff11914a1d5a448404933a1674af0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 64e07793abdb47f4da000e504e773824
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d3e105863ccc31141b318c74ac31631f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,81 @@
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
public partial class Leg
{
private GlueAttachement G_Attachement;
struct GlueAttachement
{
public RaycastHit AttachHit;
public Transform AttachedTo;
public Vector3 PosInAttachementLocal;
public Vector3 NormalInAttachementLocal;
public Quaternion RotInAttachementLocal;
public bool NoTransform { get; private set; }
public bool IsValid => (NoTransform == false && AttachedTo != null);
public GlueAttachement(Leg leg, RaycastHit legGroundHit)
{
AttachHit = legGroundHit;
AttachedTo = legGroundHit.transform;
if (legGroundHit.transform == null)
{
NoTransform = true;
PosInAttachementLocal = legGroundHit.point;
NormalInAttachementLocal = legGroundHit.normal;
RotInAttachementLocal = leg._PreviousFinalIKRot;
}
else
{
NoTransform = false;
PosInAttachementLocal = legGroundHit.transform.InverseTransformPoint(legGroundHit.point);
NormalInAttachementLocal = legGroundHit.transform.InverseTransformDirection(legGroundHit.normal);
if (!leg.Owner.AnimateFeet) RotInAttachementLocal = Quaternion.identity;
else RotInAttachementLocal = FEngineering.QToLocal(AttachedTo.rotation, leg.GetAlignedOnGroundHitRot(leg._SourceIKRot, legGroundHit.normal));
}
}
internal Vector3 GetRelevantAlignedHitPoint(Leg leg)
{
Vector3 hit = GetRelevantHitPoint();
return leg.GetAlignedOnGroundHitPos(leg.ToRootLocalSpace(hit), hit, GetRelevantNormal());
}
internal Vector3 GetRelevantHitPoint()
{
if (NoTransform) return PosInAttachementLocal;
return AttachedTo.TransformPoint(PosInAttachementLocal);
}
internal Vector3 GetRelevantNormal()
{
if (NoTransform) return NormalInAttachementLocal;
return AttachedTo.TransformDirection(NormalInAttachementLocal);
}
internal Quaternion GetRelevantAttachementRotation()
{
if (NoTransform) return RotInAttachementLocal;
return FEngineering.QToWorld(AttachedTo.rotation, RotInAttachementLocal);
}
internal void OverwritePosition(Vector3 legAnimPos)
{
if (AttachedTo == null)
PosInAttachementLocal = legAnimPos;
else
PosInAttachementLocal = AttachedTo.transform.InverseTransformPoint(legAnimPos);
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c4f8c2bb3a36f4c42943655055da245d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,267 @@
using System;
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
public partial class Leg
{
/// <summary> Check if there is raycast ground hit and if the hit is in gluing foot range </summary>
public bool G_AttachPossible
{
get { return RaycastHitted && C_Local_MidFootPosVsGroundHit.y < BelowFootRange * Owner.AllowGlueBelowFoot + FloorLevel; }
}
public bool _Glue_AskingForDetach { get; private set; }
public bool Glue_CheckDetachement()
{
bool detach = Glue_Conditions_Detach();
if (!detach) detach = Glue_Conditions_DetachForced(); // If detaching is forced, detach anyway
_Glue_AskingForDetach = detach;
return detach;
}
/// <summary> Confirm that leg can be detached right now.
/// It can be restricted by idle glue mode with opposite leg during transition etc. </summary>
public bool Glue_CheckIdleDetachementConfirm()
{
if (Owner._glueModeExecuted != EGlueMode.Idle) return true;
if (hasOppositeleg)
{
Leg oppositeLeg = GetOppositeLeg();
if (Glue_CheckOppositeLegMovementRestriction(oppositeLeg))
{
return false; // Prevent detaching when other leg is adjusting
}
}
return true;
}
/// <summary> Basically ScaleReference but smaller </summary>
public float BelowFootRange
{
get
{
return ScaleRef * _C_DynamicYScale * 0.2f;
}
}
bool Glue_Conditions_Attach()
{
if (Owner.IsGrounded == false) { /*_Editor_Label += "!IsGrounded!";*/ return false; }
if (_glueTargetBlend < 0.0001f) { /*_Editor_Label += "!TargetBlend!";*/ return false; }
if (!RaycastHitted) { /*_Editor_Label += "!RaycastHitted!";*/ return false; }
if (G_CustomForceNOTDetach /*|| G_StepHeatmapForceNOTDetach*/) { return true; }
if (_gluingCulldown > 0f) { return false; }
if (G_CustomForceAttach) { return true; }
//if (Owner._usingStepHeatmap == false)
if (Owner.DontGlueAttachIfTooNearOppositeLeg > 0f)
if (hasOppositeleg)
{
Vector3 lastFinalLocal = ToRootLocalSpace(_PreviousFinalIKPos);
var oppositeLeg = GetOppositeLeg();
Vector3 oppositeLastFinalLocal = ToRootLocalSpace(oppositeLeg._PreviousFinalIKPos);
float preventOnLowerThan = Owner.DontGlueAttachIfTooNearOppositeLeg * Owner.ScaleReference;
if (Vector2.Distance(new Vector2(lastFinalLocal.x, lastFinalLocal.z), new Vector2(oppositeLastFinalLocal.x, oppositeLastFinalLocal.z)) < preventOnLowerThan)
return false;
}
// Foot height in animation on grounded level
//if (IsFootGroundedInAnimation == false) { /*_Editor_Label += "!GroundedInAnimation!";*/ return false; }
if (G_HandlerExecutingLegAnimationMode == EGlueMode.Moving)
{
#region Foot Y Diff Condition
bool condition_YDiff = false;
float yDiff = C_Local_MidFootPosVsGroundHit.y;
if (yDiff > FloorLevel) // hit below foot - foot above ground
{
if (yDiff < BelowFootRange * Owner.AllowGlueBelowFoot + FloorLevel) // Still in range for gluing
{
condition_YDiff = true;
}
else
{
//UnityEngine.Debug.Log("floor too low - detaching");
}
}
else // Hit above foot
condition_YDiff = true;
#endregion
if (!condition_YDiff) { /*_Editor_Label += "!YDiff!";*/ return false; }
}
if (G_CustomForceNOTAttach) return false;
#region Desired leg animation swinging direction
if (Owner._glueModeExecuted == EGlueMode.Moving)
{
if (Owner.SwingHelper > 0f)
if (Owner.DesiredMovementDirection != Vector3.zero)
{
Vector3 desiredLocal = ToRootLocalSpaceDir(Owner.DesiredMovementDirection);
Vector3 legSwingLocal = _G_RefernceSwing;
float swingDot = Vector3.Dot(desiredLocal.normalized, legSwingLocal.normalized);
if (swingDot > 1f - Owner.SwingHelper) return false; // Dont allow attach when swinging foot in the same direction as desired direction
}
}
#endregion
return true;
}
/// <summary> Returnting true when leg glue transition is not yet compleated (within progress range) </summary>
bool Glue_CheckOppositeLegMovementRestriction(Leg oppositeLeg)
{
if (RaycastHitted == false) return false;
if (!Owner.IsGrounded) return false;
//if (!A_WasAligning) return false; // Raycast treshold check instead, see below line:
if (C_Local_MidFootPosVsGroundHit.y > BelowFootRange) return false;
if (G_CustomForceNOTDetach /*|| G_StepHeatmapForceNOTDetach*/) return true; // Prevent detach
if (oppositeLeg.RaycastHitted == false) return false;
if (oppositeLeg.C_Local_MidFootPosVsGroundHit.y > oppositeLeg.BelowFootRange) return false;
//if (oppositeLeg.G_AttachementHandler.StartingTransition) return true; // Prevent detach
//if (!Owner._usingStepHeatmap)
float transitionProgr = oppositeLeg.G_GlueInternalTransition;
if (/*transitionProgr > 0.01f && */transitionProgr < LegAnimatingSettings.AllowDetachBefore)
{
return true; // Prevent detach
}
return false; // Allow detach
}
/// <summary> Returns null if no opposite leg detected </summary>
Leg GetOppositeLeg()
{
if (OppositeLegIndex < 0) return null;
if (OppositeLegIndex >= Owner.Legs.Count) return null;
return Owner.Legs[OppositeLegIndex];
}
void Gluing_SetCulldown(float minDuration = 0.01f)
{
_gluingCulldown = Mathf.Max(_gluingCulldown, minDuration + (0.02f - Owner.GlueFadeOutSpeed * 0.03f));
}
public bool Glue_Conditions_Detach()
{
bool detach = false;
if (G_CustomForceNOTDetach /*|| G_StepHeatmapForceNOTDetach*/) { return detach; } // Prevent detach
// Prevent detach when leg adjustement is being executed
if (G_AttachementHandler.legMoveAnimation.duringLegAdjustMovement) { return false; }
// If attaching conditions met, don't detach to prevent re-attaching
if (Glue_Conditions_Attach() == false)
{
detach = true;
}
if (Owner.AnimateFeet) if (lastFootForwardAngleDiffABS > Owner.UnglueOn)
{
if (!G_JustLanded)
{
if (Owner._glueModeExecuted != EGlueMode.Idle) Gluing_SetCulldown();
//if (G_AttachementHandler.lastAttachingGlueMode != EGlueMode.Idle) Gluing_SetCulldown();
detach = true;
}
}
if (!detach)
{
//Vector2 flatCurrentPos = new Vector2();
//flatCurrentPos.x = ankleAlignedOnGroundHitRootLocal.x;
//flatCurrentPos.y = ankleAlignedOnGroundHitRootLocal.z;
//float distanceToAttachement = Vector2.Distance(flatCurrentPos, new Vector2(_GlueLastAttachPositionRootLocal.x, _GlueLastAttachPositionRootLocal.z));
if (!G_JustLanded)
{
Vector3 _off = Vector3.zero;
if (GluePointOffset != Vector2.zero) _off = -GetGluePointOffset();
float distanceToAttachement = Vector3.Distance(ankleAlignedOnGroundHitRootLocal + _off, _GlueLastAttachPositionRootLocal);
if (distanceToAttachement > G_GlueTesholdRange) // Foot too far from glue position
{
if (Owner._glueModeExecuted != EGlueMode.Idle) Gluing_SetCulldown();
detach = true;
//if (G_AttachementHandler.lastAttachingGlueMode != EGlueMode.Idle) Gluing_SetCulldown();
}
}
}
return detach;
}
public Vector3 GetGluePointOffset()
{
float scaleOff = Owner.ScaleReferenceNoScale * Owner.GlueRangeThreshold;
return Owner.RootToWorldSpaceVec(new Vector3(GluePointOffset.x * scaleOff, 0, GluePointOffset.y * scaleOff));
}
//public bool G_StepHeatmapForceDetach = false;
//public bool G_StepHeatmapForceNOTDetach = false;
/// <summary> Resetted each frame </summary>
public bool G_CustomForceAttach = false;
/// <summary> Resetted each frame </summary>
public bool G_CustomForceNOTDetach = false;
/// <summary> Resetted each frame </summary>
public bool G_CustomForceDetach = false;
/// <summary> Resetted each frame </summary>
public bool G_CustomForceNOTAttach = false;
bool Glue_Conditions_DetachForced()
{
if (G_CustomForceDetach) return true;
//if (G_StepHeatmapForceDetach) return true;
#region Request Repose
if (G_RequestRepose != GlueReposeRequest.None)
{
if (G_RequestRepose == GlueReposeRequest.ReposeIfFar)
{
G_RequestRepose = GlueReposeRequest.None;
if (G_Attached)
{
if (Vector3.Distance(_GluePosition, ankleAlignedOnGroundHitWorldPos) > ScaleRef * 0.1f) return true;
}
}
else
{
G_RequestRepose = GlueReposeRequest.None;
return true;
}
}
#endregion
return false;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 09fedc6770243dd488b1e06226b9a682
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,117 @@
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
public partial class Leg
{
float lastFootForwardAngleDiffABS = 0f;
void ExtraProcessingApply()
{
// Applying computed leg IK height animation offset
if (G_LegAnimation.LegAdjustementYOffset != 0f)
{
_FinalIKPos += RootSpaceToWorldVec(new Vector3(0f, G_LegAnimation.LegAdjustementYOffset * LegRaiseMultiplier * _glueTargetBlend, 0f));
}
// Limit foot yaw if using animated foots
if (Owner.AnimateFeet) PostCalculate_LimitFootYaw();
}
void ExtraIKPostProcessingApply()
{
// Steps overlap pushing
if (Owner._stepPointsOverlapRadius > 0f) PostCalculate_FeetOverlapRadius();
// Feet y offset - bring up
if (Owner.FeetYOffset != 0f) PostCalculate_FeetYOffset();
}
public void PostCalculate_LimitFootYaw()
{
//if (_noRaycast_skipFeetCalcs)
//{
// //lastFootForwardAngleDiff = 0f;
// lastFootForwardAngleDiffABS = 0f;
//}
//else
{
Vector3 originalFootForward = ankleAlignedOnGroundHitRotation * AnkleIK.forward;
// Prevent sudden angling on uneven steps
originalFootForward = ToRootLocalSpaceDir(originalFootForward);
originalFootForward.y = 0f; originalFootForward = RootSpaceToWorldVec(originalFootForward);
Vector3 currentFootForward = _FinalIKRot * AnkleIK.forward;
originalFootForward = ToRootLocalSpaceDir(originalFootForward);
originalFootForward.y = 0f;
currentFootForward = ToRootLocalSpaceDir(currentFootForward);
currentFootForward.y = 0f;
float angle = Vector3.SignedAngle(originalFootForward.normalized, currentFootForward.normalized, Vector3.up/* -(ankleAlignedOnGroundHitRotation * IKProcessor.EndParentIKBone.up)*/);
float angleABS = Mathf.Abs(angle);
//lastFootForwardAngleDiff = angle;
lastFootForwardAngleDiffABS = angleABS;
if (Owner.LimitFeetYaw > 0f)
{
if (Owner.LimitFeetYaw < 90f)
if (angleABS > Owner.LimitFeetYaw)
{
float angleDiff = (angleABS - Owner.LimitFeetYaw);
Quaternion from = (A_WasAligning == true) ? ankleAlignedOnGroundHitRotation : AnkleIK.srcRotation;
_FinalIKRot = Quaternion.LerpUnclamped(from, _FinalIKRot, (1f - angleDiff / (90f - Owner.LimitFeetYaw)));
}
}
}
}
void PostCalculate_FeetOverlapRadius()
{
float radius = Owner._stepPointsOverlapRadius * GlueThresholdMultiplier;
Vector3 ikPosLoc = ToRootLocalSpace(IKProcessor.IKTargetPosition);
// Check overlapping with other legs
var leg = Owner.Legs[0];
while (leg != null)
{
if (leg == this)
{
leg = leg.NextLeg;
continue;
}
Vector3 otherLastFinalLoc = ToRootLocalSpace(leg.IKProcessor.IKTargetPosition);
Vector2 diff = new Vector2(otherLastFinalLoc.x, otherLastFinalLoc.z) - new Vector2(ikPosLoc.x, ikPosLoc.z);
float distance = diff.magnitude;
if (distance < radius)
{
Vector2 offset = -diff * (radius - distance) * 2f;
IKProcessor.IKTargetPosition += RootSpaceToWorldVec(new Vector3(offset.x, 0f, offset.y));
}
leg = leg.NextLeg;
}
}
void PostCalculate_FeetYOffset()
{
IKProcessor.IKTargetPosition += _FinalIKRot * ((Owner.FeetYOffset * Owner.Scale * (A_AligningHelperBlend)) * AnkleIK.up);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 681d575499d214441b5f877b0b099fd4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,552 @@
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
public partial class Leg
{
partial class GlueAttachementHandler
{
public LegTransitionAnimation legMoveAnimation { get; private set; }
/// <summary>
/// Class responsitve for transitioning IK target position between two positions.
/// Animation mode is doign simple "move towards" transition
/// but Idle Mode is animating leg ik with use of space curves.
/// </summary>
public class LegTransitionAnimation
{
private GlueAttachementHandler handler;
LegsAnimator Owner { get { return handler.Owner; } }
Leg leg { get { return handler.leg; } }
#region Leg Adjust Animation Parameters
public float LegAdjustementYOffset = 0f; // Leg movement from to, y mod
public float LegAdjustementFootAngleOffset = 0f; // Leg movement foot pitch angle extra animation
Vector3 _legSpherizeLocalVector = Vector3.zero; // Leg movement from to, z mod
float _legMoveDurMul = 1f;
Quaternion baseRotationOnStepUp;
public float legMoveDistanceFactor = 0f;
float sd_trProgress = 0f;
#endregion
public bool duringLegAdjustMovement { get; private set; }
public bool wasAttaching { get; private set; }
public bool attached { get; private set; }
public float transitionProgress { get; private set; }
public float lastAttachCompleteTime { get; private set; }
public float transitionProgressLastFrame { get; private set; }
Vector3 previousPositionLocal;
Vector3 previousPositionWorld;
Quaternion previousRotationWorld;
Vector3 lastAppliedGluePosition;
Vector3 lastAppliedGluePositionLocal;
Quaternion lastAppliedGlueRotation;
float lastSpeedup = 0f;
enum EMoveType { FromAnimation, FromLastAttachement }
EMoveType animationMoveType;
public EGlueMode LastAnimationGlueMode { get { return ( animationMoveType == EMoveType.FromAnimation ) ? EGlueMode.Moving : EGlueMode.Idle; } }
public LegTransitionAnimation( GlueAttachementHandler glueTransitionHelper )
{
handler = glueTransitionHelper;
Reset();
}
public void Reset()
{
animationMoveType = EMoveType.FromAnimation;
transitionProgress = 0f;
transitionProgressLastFrame = 0f;
baseRotationOnStepUp = Owner.BaseTransform.rotation;
duringLegAdjustMovement = false;
wasAttaching = false;
attached = false;
_legSpherizeLocalVector = Vector3.zero;
ReInitialize();
}
public void ReInitialize()
{
lastAppliedGluePosition = leg._SourceIKPos;
lastAppliedGlueRotation = leg._SourceIKRot;
previousPositionWorld = leg._SourceIKPos;
previousRotationWorld = leg._SourceIKRot;
previousPositionLocal = leg.ToRootLocalSpace( leg._SourceIKPos );
}
#region Instant Transition
bool _instantTransition = false;
internal void ScheduleInstantTransition()
{
_instantTransition = true;
}
#endregion
internal void DoAttaching( bool canAttach )
{
if( canAttach != wasAttaching )
{
wasAttaching = canAttach;
if( canAttach )
{
OnChangeTargetPosition();
}
else
{
attached = false;
if( transitionProgress != 0f ) OnChangeTargetPosition();
}
}
if( duringLegAdjustMovement )
{
if( transitionProgress >= 1f )
{
duringLegAdjustMovement = false;
}
}
}
bool _wasAnimatingLeg = false;
/// <summary>
/// Ensure that current leg height is above ground level (preventing floor clipping on animation transition)
/// </summary>
internal Vector3 EnsureAnkleNotOverlappingGroundLevel( Vector3 legAnimPos )
{
if( leg.A_PreWasAligning && leg.A_WasAligningFrameBack )
{
Vector3 animPosLocal = Owner.ToRootLocalSpace( legAnimPos );
Vector3 refLocal;
if( Owner.SmoothSuddenSteps < 0.0001f )
refLocal = leg.ankleAlignedOnGroundHitRootLocal;
else
refLocal = ( leg.A_WasSmoothing ) ? leg.A_LastSmoothTargetedPosLocal : leg.ankleAlignedOnGroundHitRootLocal;
if( animPosLocal.y < refLocal.y )
{
animPosLocal.y = refLocal.y;
//UnityEngine.Debug.Log("Old Pos = " + legAnimPos + " new Pos = " + (Owner.RootToWorldSpace(animPosLocal)));
//UnityEngine.Debug.DrawLine(legAnimPos, (Owner.RootToWorldSpace(animPosLocal)), Color.green, 1.01f);
legAnimPos = Owner.RootToWorldSpace( animPosLocal );
}
}
return legAnimPos;
}
/// <summary> Idle Gluing Leg Animation </summary>
public Vector3 CalculateAnimatedLegPosition( Vector3 a, Vector3 b )
{
var sett = leg.LegAnimatingSettings;
Vector3 legAnimPos = Vector3.LerpUnclamped( a, b, sett.MoveToGoalCurve.Evaluate( transitionProgress ) );
// Spherize side offset animation compute
if( sett.SpherizeTrack.length > 1 )
{
float transitEval = sett.SpherizeTrack.Evaluate( transitionProgress ) * sett.SpherizePower * Owner.BaseTransform.lossyScale.x;
// Limit spherize offset
legAnimPos += leg.RootSpaceToWorldVec( _legSpherizeLocalVector * ( transitEval * 12f ) );
}
// Feet animation info value compute
if( Owner.AnimateFeet )
{
LegAdjustementFootAngleOffset = sett.FootRotationCurve.Evaluate( transitionProgress ) * 90f * Mathf.Min( 0.5f, legMoveDistanceFactor * 1.1f );
LegAdjustementFootAngleOffset /= lastSpeedup;
}
// Prepare foot height offset value
float scaleRef = Owner.ScaleReferenceNoScale * 0.75f;
float height = Mathf.Lerp( sett.MinFootRaise, sett.MaxFootRaise, legMoveDistanceFactor );
height *= scaleRef;
LegAdjustementYOffset = height * sett.RaiseYAxisCurve.Evaluate( transitionProgress );
_wasAnimatingLeg = true;
return legAnimPos;
}
/// <summary> Compute target position for the next glue attachement </summary>
internal Vector3 GetTargetPosition()
{
float attachBlend = handler.glueAnimationBlend;
if( animationMoveType == EMoveType.FromAnimation ) // From animation to attachement
{
if( attachBlend < 0.0001f ) return Owner.RootToWorldSpace( previousPositionLocal );
Vector3 a = Owner.RootToWorldSpace( previousPositionLocal );
if( transitionProgress < 0.0001f ) return a;
Vector3 b;
if( attached ) // fading from last glue
{
if( attachBlend > 0.9995f )
b = leg._GlueLastAttachPosition;
else
{
if( leg.Owner.OnlyLocalAnimation )
{ b = leg.RootSpaceToWorld( leg._GlueLastAttachPositionRootLocal ); }
else
{
// Helping animation flow with world-local space manipulation
b = Vector3.LerpUnclamped( leg.RootSpaceToWorld( leg._GlueLastAttachPositionRootLocal ), leg._GlueLastAttachPosition, attachBlend );
}
}
}
else // Pinning towards grounded position
{
b = leg.ankleAlignedOnGroundHitWorldPos;
}
if( transitionProgress > .9995f ) return b;
else return Vector3.LerpUnclamped( a, b, transitionProgress );
}
else // From attachement to attachement
{
Vector3 a;
if( leg.Owner.OnlyLocalAnimation )
{
a = Owner.RootToWorldSpace( previousPositionLocal );
if( transitionProgress < 0.0001f ) return a;
}
else
{
a = previousPositionWorld;
if( transitionProgress < 0.0001f ) return a;
// From world to local initial point to compensate dynamic character aligning
a = Vector3.LerpUnclamped( previousPositionWorld, Owner.RootToWorldSpace( previousPositionLocal ), transitionProgress );
}
Vector3 b;
if( transitionProgress > 0.9995f ) b = leg._GlueLastAttachPosition;
else b = CalculateAnimatedLegPosition( a, leg.ankleAlignedOnGroundHitWorldPos );
if( transitionProgress >= 1f )
{
return b;
}
else
{
float om = 1f - transitionProgress;
b = Vector3.LerpUnclamped( a, b, 1f - ( om * om ) );
return b;
}
}
}
internal void RequireRepose()
{
if( attached )
{
attached = false;
OnChangeTargetPosition();
}
}
internal Quaternion GetTargetRotation()
{
Quaternion a = previousRotationWorld;
Quaternion finRot;
if( transitionProgress < 0.001f )
{
finRot = a;
return finRot;
}
Quaternion b;
if( attached ) // fading from last glue
{
b = leg._GlueLastAttachRotation;
}
else // Pinning towards grounded rotation
b = leg.ankleAlignedOnGroundHitRotation; // IMPORTANT
if( transitionProgress > .9995f )
finRot = b;
else
finRot = Quaternion.LerpUnclamped( a, b, transitionProgress );
return finRot;
}
internal void OnChangeTargetPosition()
{
handler.lasGlueModeOnAttaching = Owner._glueModeExecuted;
baseRotationOnStepUp = Owner.BaseTransform.rotation;
#region Determinate type of gluing animation to execute on change
if( handler.glueAnimationBlend < 0.2f )
{
animationMoveType = EMoveType.FromAnimation;
}
else
{
if( handler.lasGlueModeOnAttaching == EGlueMode.Moving )
{
animationMoveType = EMoveType.FromAnimation;
}
else
{
if( animationMoveType == EMoveType.FromLastAttachement )
{
animationMoveType = EMoveType.FromLastAttachement;
}
else
{
if( handler.glueAnimationBlend > 0.75f )
{
if( transitionProgress < 0.1f || transitionProgress > 0.9f )
{
animationMoveType = EMoveType.FromLastAttachement;
}
else
{
animationMoveType = EMoveType.FromAnimation;
}
}
else
{
animationMoveType = EMoveType.FromAnimation;
}
}
}
}
#endregion
if( leg.Owner.OnlyLocalAnimation )
previousPositionWorld = leg.RootSpaceToWorld( lastAppliedGluePositionLocal );
else
previousPositionWorld = lastAppliedGluePosition;
previousRotationWorld = lastAppliedGlueRotation;
previousPositionLocal = Owner.ToRootLocalSpace( previousPositionWorld );
#region Computing idle gluing leg animation parameters
if( animationMoveType == EMoveType.FromLastAttachement )
{
if( transitionProgress > 0.1f && transitionProgress < 0.9f ) // Break currently executed transitioning
{
//UnityEngine.Debug.Log("break");
//breakIdleGlueTime = Time.time;
//previousBreakLocal = Owner.ToRootLocalSpace(leg._PreviousFinalIKPos);
//transitionProgress = 1f;
}
else // Transitioning start over
{
transitionProgress = 0f;
}
Vector3 from = previousPositionWorld;
Vector3 to = leg.ankleAlignedOnGroundHitWorldPos;
Vector3 diff = to - from;
float fromToDistance = diff.magnitude;
legMoveDistanceFactor = ( fromToDistance ) / ( Owner.ScaleReference * 0.6f );
legMoveDistanceFactor = Mathf.Clamp( legMoveDistanceFactor, 0.05f, 1f );
Vector3 towards = diff.normalized;
towards = Vector3.ProjectOnPlane( towards, Owner.Up );
towards.Normalize();
leg.SendRaiseEvent( fromToDistance );
if( legMoveDistanceFactor > leg.LegAnimatingSettings.DoStepAnimationOnDistanceFactor * 0.82f )
{
_legMoveDurMul = Mathf.Lerp( 1.55f, .85f, legMoveDistanceFactor * 2f );
Vector3 cross = Vector3.Cross( towards, Owner.Up );
cross.Normalize();
_legSpherizeLocalVector = leg.ToRootLocalSpaceDir( cross ) * Owner.ScaleReferenceNoScale * -0.03f;
duringLegAdjustMovement = true;
}
else // If step distance if very small, skip leg move animation and slide foots towards target position in a subtle way
{
animationMoveType = EMoveType.FromAnimation;
_legSpherizeLocalVector = Vector3.zero;
duringLegAdjustMovement = false;
}
}
else
{
duringLegAdjustMovement = false;
transitionProgress = 0f;
}
#endregion
}
public void UpdateAnimation()
{
float boostSD = ( Owner.JustGrounded ) ? 0.2f : 1f;
float boostLrp = ( Owner.JustGrounded ) ? 5f : 1f;
transitionProgressLastFrame = transitionProgress;
if( _instantTransition )
{
_instantTransition = false;
transitionProgress = 1f;
lastAttachCompleteTime = Time.time;
}
//else
//{
// Fast fade on landing
//if (leg.G_JustLanded)
//{
// animationMoveType = EMoveType.FromAnimation;
// transitionProgress = Mathf.MoveTowards(transitionProgress, 1f, Owner.DeltaTime * 5f);
//}
//}
if( !Owner.IsGrounded ) return;
if( animationMoveType == EMoveType.FromLastAttachement )
{
float animTime = 1f / ( leg.LegAnimatingSettings.StepMoveDuration * 0.8f );
#region Speedups
float speedup = 1f;
lastSpeedup = 1f;
if( leg.LegAnimatingSettings.AllowSpeedups > 0f )
{
if( leg.hasOppositeleg )
{
var oppositeleg = leg.GetOppositeLeg();
Vector3 prePos = oppositeleg._PreviousFinalIKPos;
if( leg.Owner.OnlyLocalAnimation ) prePos = leg.RootSpaceToWorld( oppositeleg._PreviousFinalIKPosRootLocal );
float stretch = oppositeleg.IKProcessor.GetStretchValue( prePos );
if( stretch > leg.LegStretchLimit * 0.95f )
{
float diff = ( stretch - leg.LegStretchLimit * 0.95f ) * 2.0f;
if( diff < 0f ) diff = 0f;
speedup += diff;
}
if( oppositeleg._UsingCustomRaycast == false )
if( oppositeleg.G_AttachementHandler.legMoveAnimation.attached && oppositeleg.G_Attachement.IsValid )
{
float distToAttach = ( leg.RootSpaceToWorld( oppositeleg.AnkleH.LastKeyframeRootPos ) - oppositeleg.G_Attachement.GetRelevantHitPoint() ).magnitude;
float scaleRef = Owner.ScaleReference * 0.4f;
if( distToAttach > scaleRef )
{
float diff = distToAttach - scaleRef;
speedup += ( diff / scaleRef ) * 2f;
}
}
}
if( leg.LegAnimatingSettings.AllowSpeedups > 0.25f )
{
float diff = Quaternion.Angle( baseRotationOnStepUp, Owner.BaseTransform.rotation );
if( diff > 12f )
{
float angularFactor = Mathf.InverseLerp( 30f, 135f, diff );
angularFactor = Mathf.LerpUnclamped( 0.5f, 2f, angularFactor ) * ( 0.4f + leg.LegAnimatingSettings.AllowSpeedups * 0.6f );
transitionProgress += Owner.DeltaTime * angularFactor * boostLrp;
}
}
speedup = Mathf.LerpUnclamped( 1f, speedup, leg.LegAnimatingSettings.AllowSpeedups );
}
lastSpeedup = speedup;
#endregion
transitionProgress = Mathf.MoveTowards( transitionProgress, 1f, animTime * speedup * _legMoveDurMul * leg.LegMoveSpeedMultiplier * Owner.DeltaTime * boostLrp );
if( transitionProgress > .9995f )
{
if( duringLegAdjustMovement )
{
TriggerAttach();
}
}
return;
}
if( transitionProgress > .9995f && handler.glueAnimationBlend > 0.95f )
{
TriggerAttach();
}
else
transitionProgress = Mathf.SmoothDamp( transitionProgress, 1.001f, ref sd_trProgress, ( 0.01f + Mathf.LerpUnclamped( 0.225f, 0.01f, wasAttaching ? Owner.GlueFadeInSpeed : Owner.GlueFadeOutSpeed ) ) * boostSD, 10000000f, Owner.DeltaTime );
}
void TriggerAttach()
{
if( !attached )
{
transitionProgress = 1f;
lastAttachCompleteTime = Time.time;
attached = leg.Glue_TriggerFinalAttach();
duringLegAdjustMovement = false;
}
}
public void PostUpdate()
{
lastAppliedGluePosition = leg._GluePosition;
lastAppliedGluePositionLocal = leg.ToRootLocalSpace( lastAppliedGluePosition );
lastAppliedGlueRotation = leg._GlueRotation;
if( _wasAnimatingLeg == false ) // Fade off in case of broken transition animation
{
LegAdjustementFootAngleOffset = Mathf.MoveTowards( LegAdjustementFootAngleOffset, 0f, leg.DeltaTime * 20f );
LegAdjustementYOffset = Mathf.MoveTowards( LegAdjustementYOffset, 0f, leg.DeltaTime * 20f );
}
else
{
_wasAnimatingLeg = false;
}
}
}
}
GlueAttachementHandler.LegTransitionAnimation G_LegAnimation { get { return G_AttachementHandler.legMoveAnimation; } }
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 83309e8361aa05a4d98715b9703c04ec
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,191 @@
using System;
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
public partial class Leg
{
/// <summary>
/// This class is handling blend between glued and not glued leg IK position transition.
/// (This class is very simple, just forwarding methods and handling basic blend transition)
/// It's also operating LegTransitionAnimation class (more complex one)
/// which is responsive for IK transitioning between two positions.
/// </summary>
partial class GlueAttachementHandler
{
LegsAnimator Owner;
#region Leg References
Leg ParentLeg;
Leg leg { get { return ParentLeg; } }
#endregion
public float glueAnimationBlend { get; private set; }
float _sd_glueAnimationBlend = 0f;
public float attachTransitionProgress { get { return legMoveAnimation.transitionProgress; } }
public float attachTransitionProgressLastFrame { get { return legMoveAnimation.transitionProgressLastFrame; } }
public float legMoveDistanceFactor { get { return legMoveAnimation.legMoveDistanceFactor; } }
public EGlueMode lasGlueModeOnAttaching { get; private set; }
public GlueAttachementHandler(Leg leg)
{
ParentLeg = leg;
Owner = leg.Owner;
legMoveAnimation = new LegTransitionAnimation(this);
lasGlueModeOnAttaching = Owner._glueModeExecuted;
Reset(true);
}
public void Reset(bool initializing)
{
glueAnimationBlend = 0f;
_sd_glueAnimationBlend = 0f;
if (initializing)
{
lastGluePosition = leg.BoneEnd.position;
lastGlueRotation = leg.BoneEnd.rotation;
}
legMoveAnimation.Reset();
//_instantTransition = true;
}
#region Instant Transition
bool _instantTransition = false;
/// <summary>
/// Instant transition to being glued and instant transition to complete glue transition animation
/// </summary>
public void SheduleInstantTransition()
{
_instantTransition = true;
legMoveAnimation.ScheduleInstantTransition();
}
#endregion
/// <summary>
/// Found alignable placement for foot glue
/// </summary>
public void TransitionToGlueAnimation()
{
legMoveAnimation.DoAttaching(true);
ChangeGlueAnimationBlendTo(1f, Owner.GlueFadeInSpeed);
}
public void TransitionToDisableGlueAnimation()
{
legMoveAnimation.DoAttaching(false);
ChangeGlueAnimationBlendTo(0f, Owner.GlueFadeOutSpeed);
}
Vector3 lastGluePosition = Vector3.zero;
public Vector3 GetGluePosition()
{
if (glueAnimationBlend > 0.9995f) lastGluePosition = legMoveAnimation.GetTargetPosition();
else
if (glueAnimationBlend < 0.0001f) lastGluePosition = leg.A_PreIKPosForGluing;
else
lastGluePosition = Vector3.LerpUnclamped(leg.A_PreIKPosForGluing, legMoveAnimation.GetTargetPosition(), glueAnimationBlend);
return lastGluePosition;
}
Quaternion lastGlueRotation = Quaternion.identity;
public Quaternion GetGlueRotation()
{
if (glueAnimationBlend > 0.999f) lastGlueRotation = legMoveAnimation.GetTargetRotation();
else
if (glueAnimationBlend < 0f) lastGlueRotation = leg._FinalIKRot;
else
{
lastGlueRotation = Quaternion.LerpUnclamped(leg._FinalIKRot, legMoveAnimation.GetTargetRotation(), glueAnimationBlend);
}
return lastGlueRotation;
}
public void UpdateTransitioning(bool attaching)
{
legMoveAnimation.UpdateAnimation();
#region Editor Code (Label)
#if UNITY_EDITOR
//if (leg.PlaymodeIndex == 1)
//{
// //LADB
// if (Owner._EditorMotionCategory == EEditorMotionCategory.Glue)
// leg._Editor_Label += "BlendIN:" + leg.R(glueAnimationBlend, 2) + " [] " + leg.R(attachTransitionProgress, 2) + " || GAttached: " + leg.G_Attached;
//}
//if (Owner._EditorMotionCategory == EEditorMotionCategory.Glue)
// leg._Editor_Label += "\n" + (leg.G_DuringAttaching ? "During Attach" : "N") + " | " + (leg.G_Attached ? " Attached " : " No Attached");
#endif
#endregion
}
public void PostUpdate()
{
legMoveAnimation.PostUpdate();
}
internal void OnLegRequireRepose()
{
legMoveAnimation.RequireRepose();
}
void ChangeGlueAnimationBlendTo(float target, float speed)
{
if (Owner.GroundedTime < 0.0f) speed = .99f;
if (_instantTransition) if (target > 0f)
{
glueAnimationBlend = target;
_instantTransition = false;
return;
}
if (speed >= 1f)
{
glueAnimationBlend = target;
return;
}
// Fast fade on landing
if (leg.G_JustLanded)
glueAnimationBlend = Mathf.MoveTowards(glueAnimationBlend, target, Owner.DeltaTime * 3f);
glueAnimationBlend = Mathf.SmoothDamp(glueAnimationBlend, target, ref _sd_glueAnimationBlend, Mathf.LerpUnclamped(0.2f, 0.005f, speed), 100000f, Owner.DeltaTime);
if (float.IsNaN(_sd_glueAnimationBlend)) _sd_glueAnimationBlend = 0f;
}
}
GlueAttachementHandler G_AttachementHandler;
public Vector3 G_GluePosition { get { return _GluePosition; } }
public float G_GlueAnimationBlend { get { return G_AttachementHandler.glueAnimationBlend; } }
public float G_GlueInternalTransition { get { return G_AttachementHandler.attachTransitionProgress; } }
public float G_LastAttachCompleteTime { get { return G_AttachementHandler.legMoveAnimation.lastAttachCompleteTime; } }
public float G_GlueInternalTransitionLastFrame { get { return G_AttachementHandler.attachTransitionProgressLastFrame; } }
public float G_LastLegMoveDistanceFactor { get { return G_AttachementHandler.legMoveDistanceFactor; } }
public bool G_DuringLegAdjustMovement { get { return G_AttachementHandler.legMoveAnimation.duringLegAdjustMovement; } }
public EGlueMode G_HandlerExecutingLegAnimationMode { get { return G_AttachementHandler.legMoveAnimation.LastAnimationGlueMode; } }
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c98ddb3f892c49849a70856b57f95121
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,488 @@
using System;
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
public partial class Leg
{
public bool A_PreWasAligning { get; private set; }
public bool A_PreWasAligningNearGround { get; private set; }
public bool A_WasAligning { get; private set; }
public float A_AligningHelperBlend { get; private set; }
public float A_LastAlignHeightDiff { get; private set; }
public float A_LastAlignHeightCompareValue { get; private set; }
Vector3 A_PreviousRelevantAnklePos;
Vector3 A_LastApppliedAlignPos;
/// <summary> Changed only on A_PreWasAligning </summary>
Vector3 A_LastApppliedAlignPosLocal;
Vector3 A_PreIKPosForGluing;
Quaternion A_LastApppliedAlignRot;
Quaternion A_LastTargetAlignRot;
void AlignStep_Init()
{
A_PreWasAligning = false;
A_PreWasAligningNearGround = false;
A_WasAligning = false;
A_AligningHelperBlend = 0f;
A_LastTargetAlignRot = BoneEnd.rotation;
A_LastApppliedAlignRot = BoneEnd.rotation;
A_PreIKPosForGluing = _FinalIKPos;
}
void AlignStep_CheckAlignStatePre()
{
A_PreIKPosForGluing = _FinalIKPos;
if (_noRaycast_skipFeetCalcs) return;
bool align = false;
A_PreWasAligningNearGround = false;
if (RaycastHitted)
{
float hipsHelp = ParentHub._Hips_StepHeightAdjustOffset;
// Cathing ground below when standing on the big slope down
if (hipsHelp < 0f) hipsHelp *= -0.03f; else hipsHelp = 0f;
A_LastAlignHeightDiff = C_Local_MidFootPosVsGroundHit.y;
A_LastAlignHeightCompareValue = ScaleRef * (0.002f + Owner.AnimationFloorLevel) + hipsHelp;
if (Owner.FootAlignRapidity > 0.9999f)
{
if (A_LastAlignHeightDiff <= A_LastAlignHeightCompareValue)
{
align = true;
A_PreWasAligningNearGround = true;
}
}
else
{
if (A_LastAlignHeightDiff <= A_LastAlignHeightCompareValue + ScaleRef * (0.04f + (1f - Owner.FootAlignRapidity) * 0.04f))
{
A_PreWasAligningNearGround = true; // Align foot sooner, before position align to rotate on step slope before hitting ground
if (A_LastAlignHeightDiff <= A_LastAlignHeightCompareValue) align = true;
}
}
//A_PreWasAligningDeeper = A_LastAlignHeightDiff <= ScaleRef * (0.002f /*+ 0.1f * Owner.FootDeeperRange*/) + hipsHelp;
}
else
{
A_LastAlignHeightDiff = 100f;
//A_PreWasAligningDeeper = false;
}
A_PreWasAligning = align;
if (align)
{
//Vector3 hAlign = ToRootLocalSpace(_FinalIKPos);
//hAlign.y = ankleAlignedOnGroundHitRootLocal.y;
//Vector3 newPos = RootSpaceToWorld(hAlign);
Vector3 newPos = ankleAlignedOnGroundHitWorldPos;
if (A_AligningHelperBlend > 0.99f)
_FinalIKPos = newPos;
else
_FinalIKPos = Vector3.Lerp(_FinalIKPos, newPos, A_AligningHelperBlend * 8f);
A_PreIKPosForGluing = newPos;
}
else
{
if (A_AligningHelperBlend > 0.01f)
_FinalIKPos = Vector3.Lerp(_FinalIKPos, RootSpaceToWorld(A_LastApppliedAlignPosLocal), A_AligningHelperBlend);
}
//A_PreIKPosForGluing = G_AttachementHandler.legMoveAnimation.lastAppliedGluePosition;
}
void AlignStep_ValidateFootRotation()
{
if (!Owner.AnimateFeet) return;
if (_noRaycast_skipFeetCalcs) return;
if (A_PreWasAligningNearGround)
{
float blend = Owner.FootRotationBlend * A_AligningHelperBlend;
if (A_LastElevateH < 0.02f)
{
// Apply target foot rotation to aligned on raycast hit rotation
if (blend >= 1f)
A_LastTargetAlignRot = ankleAlignedOnGroundHitRotation;
else
A_LastTargetAlignRot = Quaternion.LerpUnclamped(_FinalIKRot, ankleAlignedOnGroundHitRotation, blend);
}
else // When elevating foot, we blending out rotation a bit
{
float factor = A_LastElevateH / (ScaleRef * 0.15f);
if (A_LastElevateH > 1f) A_LastElevateH = 1f;
A_LastTargetAlignRot = Quaternion.LerpUnclamped(ankleAlignedOnGroundHitRotation, _FinalIKRot, factor * blend);
}
}
else // If not aligning, refreshing target rotation
{
if (A_AligningHelperBlend < 0.001f)
A_LastTargetAlignRot = _FinalIKRot;
else
A_LastTargetAlignRot = Quaternion.Lerp(_FinalIKRot, A_LastTargetAlignRot, A_AligningHelperBlend);
}
// Rotate towards target foot rotation with smooth lerp motion
if (Owner.FootAlignRapidity >= 1f)
A_LastApppliedAlignRot = A_LastTargetAlignRot;
else
A_LastApppliedAlignRot = Quaternion.Lerp(A_LastApppliedAlignRot, A_LastTargetAlignRot, DeltaTime * (8f + Owner.FootAlignRapidity * 26f));
_FinalIKRot = A_LastApppliedAlignRot;
}
bool A_WasFullAlign = false;
float A_aligningBlendByGluing = 1f;
/// <summary> Define A_DefaultStepIKPosition </summary>
void AlignStep_OnGroundAlign()
{
if (_noRaycast_skipFeetCalcs)
{
A_WasAligning = A_PreWasAligning;
if (A_PreWasAligning)
{
if (A_AligningHelperBlend < 0.05f) A_AligningHelperBlend = 0.05f;
A_AligningHelperBlend = Mathf.MoveTowards(A_AligningHelperBlend, 1f, Owner.DeltaTime * 8f);
if (!A_WasFullAlign)
if (A_AligningHelperBlend >= 1f - Owner.EventExecuteSooner)
{
A_WasFullAlign = true;
if (Owner.UseGluing == false) SendStepEvent();
}
}
else
{
if (A_AligningHelperBlend > 0.5f) A_AligningHelperBlend = 0.5f;
A_AligningHelperBlend = Mathf.MoveTowards(A_AligningHelperBlend, 0f, Owner.DeltaTime * 14f);
}
if (A_AligningHelperBlend < 0.65f) A_WasFullAlign = false;
return;
}
A_aligningBlendByGluing = 1f;
if (Owner.UseGluing)
{
A_aligningBlendByGluing = 1f - (_glueTargetBlend * G_GlueAnimationBlend);
}
if (A_PreWasAligning)
{
if (A_WasAligning) A_PreviousRelevantAnklePos = previousAnkleAlignedOnGroundHitWorldPos;
float blend = A_aligningBlendByGluing * A_AligningHelperBlend;
if (blend >= 1f)
_FinalIKPos = ankleAlignedOnGroundHitWorldPos; // Seamless Foot Position on the ground
else
_FinalIKPos = Vector3.LerpUnclamped(_FinalIKPos, ankleAlignedOnGroundHitWorldPos, blend);
if (A_AligningHelperBlend < 0.05f) A_AligningHelperBlend = 0.05f;
A_AligningHelperBlend = Mathf.MoveTowards(A_AligningHelperBlend, 1f, Owner.DeltaTime * 8f);
if (!A_WasFullAlign)
if (A_AligningHelperBlend >= 1f - Owner.EventExecuteSooner)
{
A_WasFullAlign = true;
if (Owner.UseGluing == false) SendStepEvent();
}
A_LastApppliedAlignPosLocal = ToRootLocalSpace(_FinalIKPos);
}
else
{
A_PreviousRelevantAnklePos = _SourceIKPosUnchangedY;
if (A_AligningHelperBlend > 0.75f) A_AligningHelperBlend = 0.75f;
A_AligningHelperBlend = Mathf.MoveTowards(A_AligningHelperBlend, 0f, Owner.DeltaTime * 18f);
}
if (A_AligningHelperBlend < 0.6f) A_WasFullAlign = false;
A_LastApppliedAlignPos = _FinalIKPos;
A_WasAligning = A_PreWasAligning;
}
Vector3 A_LastElevation;
float A_LastElevateH = 0f;
float _sd_A_Elev = 0f;
/// <summary> Foots Y Offset apply </summary>
void AlignStep_LegElevation()
{
if (Owner.LegElevateBlend < 0.001f) return;
if (_noRaycast_skipFeetCalcs)
{
A_LastElevation = Vector3.zero;
return;
}
float scaleRef = ScaleRef;
// Using leg elevate on ground overlapping
float heightInGroundAlign = (groundHitRootSpacePos.y - A_LastSuddenSmoothYOffset) - ParentHub._Hips_StepHeightAdjustOffset;
float flr = Owner.AnimationFloorLevel * scaleRef;
float heightInAnim = C_Local_FootElevateInAnimation;
if (heightInAnim > flr && heightInGroundAlign > (0.001f * scaleRef + flr) + 0.1f)
{
heightInAnim -= flr;
float animationRelevantElevation = heightInAnim; // Elevate leg on top of ground hit accordingly with animation
// But make it controlled to avoid unneccesary elevating and limit it
// distanceBetweenGroundAndAnim -> above zero = foot above ground in safe position
float distanceBetweenGroundAndAnim = heightInAnim - heightInGroundAlign;
float elevateApplyTreshold = scaleRef * 0.015f;
float groundVSfootSafeDistance = scaleRef * 0.35f;
// Fade out elevation in safe height distance of foot VS ground hit
float fadeOutFactor = distanceBetweenGroundAndAnim / groundVSfootSafeDistance;
// Foot close ground -> fadeOutFactor = 0
fadeOutFactor = Mathf.Clamp01(fadeOutFactor);
// needs to be bigger than ground touching distance
if (distanceBetweenGroundAndAnim > elevateApplyTreshold)
{
//fadeOutFactor *= fadeOutFactor; // Exponential damping
animationRelevantElevation *= (1f - fadeOutFactor);
if (A_AligningFor < 0) A_AligningFor = DeltaTime;
if (A_AligningFor < 0.3f)
A_AligningFor += DeltaTime;
else
A_AligningFor = 0.3f;
}
else
{
if (A_AligningFor > 0f)
A_AligningFor -= DeltaTime;
else
A_AligningFor = 0f;
}
float targetLegElevateHeightOffset = animationRelevantElevation;
float raiseLimit = scaleRef * Mathf.LerpUnclamped(0.1f, 0.9f, Owner.LegElevateHeightLimit);
if (targetLegElevateHeightOffset > raiseLimit) targetLegElevateHeightOffset = raiseLimit;
//A_LastElevateH = Mathf.SmoothDamp(A_LastElevateH, targetLegElevateHeightOffset, ref _sd_A_Elev, 1f, float.MaxValue, DeltaTime);
//float baseDur = A_WasSmoothing ? 0.055f : 0.025f;
//float dur = 0.25f * (Mathf.LerpUnclamped(.25f, 1f, fadeOutFactor)) + baseDur;
//if (A_AligningFor > 0.1f) dur *= 0.05f;
//A_LastElevateH = Mathf.SmoothDamp(A_LastElevateH, targetLegElevateHeightOffset, ref _sd_A_Elev, dur, float.MaxValue, DeltaTime);
// Height limit apply
if (groundHitRootSpacePos.y > 0)
{
float limitFactor = ScaleRef * 0.2f;
if (limitFactor > 0f)
{
float limitRatio = groundHitRootSpacePos.y / limitFactor;
if (limitRatio > 0.8f)
{
targetLegElevateHeightOffset = Mathf.LerpUnclamped(targetLegElevateHeightOffset, 0f, Mathf.InverseLerp(0.8f, 1.1f, limitRatio));
}
}
}
float elevDiff = targetLegElevateHeightOffset - A_LastElevateH;
elevDiff = Mathf.Abs(elevDiff);
if (elevDiff > scaleRef * Adj_A_ElevateSpeedupMargin)
{
A_LastElevateH = Mathf.Lerp(A_LastElevateH, targetLegElevateHeightOffset, DeltaTime * Adj_A_ElevateLerpSpeedAfter);
}
else
{
A_LastElevateH = Mathf.Lerp(A_LastElevateH, targetLegElevateHeightOffset, DeltaTime * Adj_A_ElevateLerpSpeedStart);
}
if (A_LastElevateH < 0f) A_LastElevateH = 0f;
//UnityEngine.Debug.Log("height in anim = " + heightInAnim + " : ground align h " + heightInGroundAlign + " : ElevateH = " + A_LastElevateH);
}
else
{
A_LastElevateH = Mathf.SmoothDamp(A_LastElevateH, 0f, ref _sd_A_Elev, 0.02f, 100000f, DeltaTime);
}
A_LastElevation = RootSpaceToWorldVec(new Vector3(0f, A_LastElevateH * Owner.LegElevateBlend * A_aligningBlendByGluing, 0f));
//_Editor_Label += " Elev:" + System.Math.Round(A_LastElevateH, 2) + " inAnim = " + System.Math.Round(C_Local_FootElevateInAnimation,2);
_FinalIKPos += A_LastElevation;
}
/// <summary> Can be freely adjusted through custom module (default 8) </summary>
[NonSerialized] public float Adj_A_ElevateLerpSpeedStart = 8f;
/// <summary> Can be freely adjusted through custom module (default 5) </summary>
[NonSerialized] public float Adj_A_ElevateLerpSpeedAfter = 5f;
/// <summary> Can be freely adjusted through custom module (default 0.014) </summary>
[NonSerialized] public float Adj_A_ElevateSpeedupMargin = 0.014f;
float A_AligningFor = 0f;
Vector3 A_LastAlignRootSpacePos;
/// <summary> Assigned when A_WasSmoothing is true </summary>
Vector3 A_LastSmoothTargetedPosLocal;
float A_LastSuddenSmoothYOffset = 0f;
float A_SuddenSmoothing = 0f;
float A_lastSuddenSmoothingDiff = 0f;
bool A_WasSmoothing = false;
//float smoothingTime = -1f;
bool A_WasAligningFrameBack = false;
/// <summary> Smoothing sudden steps Y </summary>
void AlignStep_SmoothSuddenSteps()
{
if (Owner.SmoothSuddenSteps < 0.0001f) return;
if (_noRaycast_skipFeetCalcs || G_Attached)
{
A_WasAligningFrameBack = A_WasAligning;
A_WasSmoothing = false;
return;
}
float scaleRef = ScaleRef;
if (A_WasAligning || A_WasAligningFrameBack)
{
if (!A_WasAligning) A_PreviousRelevantAnklePos = previousAnkleAlignedOnGroundHitWorldPos;
Vector3 currentLocAlign = ToRootLocalSpace(ankleAlignedOnGroundHitWorldPos);
Vector3 preAlignRootY;
// If already smoothing -> check diff between last focused ik pos
// If not smoothing -> check diff between last relevant ik pos
if (A_WasSmoothing)
{
preAlignRootY = ToRootLocalSpace(previousAnkleAlignedOnGroundHitWorldPos);
}
else
{
preAlignRootY = ToRootLocalSpace(A_PreviousRelevantAnklePos);
}
float yDiff = preAlignRootY.y - currentLocAlign.y;
yDiff = Mathf.Abs(yDiff);
//_Editor_Label = "yDiff: " + R(yDiff, 4);
float smoothingTreshold = scaleRef * (0.006f); // Treshold to start next checks
if ((raycastSlopeAngle < 17f || raycastSlopeAngle > 80f) || Owner.RaycastShape == ERaycastMode.Spherecast) // To avoid smoothing all the time running on slopes (needs better solution)
if (yDiff > smoothingTreshold)
{
float diffFactor = yDiff / (scaleRef * 0.275f);
if (diffFactor > 1f) diffFactor = 1f;
//_Editor_Label = " diffFactor: " + R(diffFactor, 4);
// Treshold dictated by smooth sudden steps parameter
if (diffFactor > Mathf.LerpUnclamped(0.25f, 0.1f, Owner.SmoothSuddenSteps))
{
float smoothBooster = Mathf.LerpUnclamped(0.3f, 0.1f, Owner.SmoothSuddenSteps);
if (A_lastSuddenSmoothingDiff == 0f || A_SuddenSmoothing < diffFactor)
{
A_lastSuddenSmoothingDiff = yDiff;
A_LastAlignRootSpacePos = ToRootLocalSpace(previousAnkleAlignedOnGroundHitWorldPos); //_PreviousFinalIKPos
smoothBooster *= 0.7f;
}
else
{
if (Owner.SmoothSuddenSteps < 0.5f)
{
float reAdjust = Mathf.LerpUnclamped(0.5f, 0.0f, Owner.SmoothSuddenSteps);
A_lastSuddenSmoothingDiff = Mathf.LerpUnclamped(A_lastSuddenSmoothingDiff, yDiff, reAdjust);
}
}
A_SuddenSmoothing += Mathf.Clamp01(A_lastSuddenSmoothingDiff / (scaleRef * smoothBooster));
float maxSm = 0.85f + Owner.SmoothSuddenSteps * 0.165f;
if (A_SuddenSmoothing > maxSm) A_SuddenSmoothing = maxSm;
}
}
}
//_Editor_Label += " Smooth: " + R(A_SuddenSmoothing, 4);
if (A_SuddenSmoothing > 0f)
{
Vector3 currIKLoc = ToRootLocalSpace(_FinalIKPos);
//if (A_GlueAskForSmooth)
//{
// if (A_PreWasAligning) currIKLoc = ankleAlignedOnGroundHitRootLocal;
// A_GlueAskForSmooth = false;
//}
A_LastSuddenSmoothYOffset = currIKLoc.y;
A_SuddenSmoothing -= Owner.DeltaTime * Mathf.LerpUnclamped(60f, 7.5f, Owner.SmoothSuddenSteps);
// Shift IK local Y pos before sudden change -> towards current local Y (currIKLoc.Y)
currIKLoc.y = Mathf.Lerp(currIKLoc.y, A_LastAlignRootSpacePos.y, A_SuddenSmoothing);
// When foot is pushed down to compensate the value is < 0 -> leg is moving up
// with value > 0 leg is moving down (y is compensating it up)
A_LastSuddenSmoothYOffset = currIKLoc.y - A_LastSuddenSmoothYOffset;
//_Editor_Label += " SMY: " + A_LastSuddenSmoothYOffset;
A_LastSmoothTargetedPosLocal = currIKLoc;
A_SmoothedIKPos = RootSpaceToWorld(currIKLoc);
_FinalIKPos = A_SmoothedIKPos;
if (A_SuddenSmoothing < 0f) A_SuddenSmoothing = 0f;
A_WasSmoothing = true;
}
else
{
A_LastSuddenSmoothYOffset = 0f;
A_WasSmoothing = false;
}
}
/// <summary> Changed only on A_WasSmoothing </summary>
Vector3 A_SmoothedIKPos;
void AlignStep_Complete()
{
A_WasAligningFrameBack = A_WasAligning;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 75bf60bb2232bf74482edd489680368c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,144 @@
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
public partial class Leg
{
#if UNITY_EDITOR
public void _Editor_Align_DrawControls()
{
if (Application.isPlaying == false) return;
if (A_PreWasAligning) Handles.color = Color.green;
else Handles.color = Color.red;
Handles.SphereHandleCap(0, _SourceIKPos, Quaternion.identity, ScaleRef * 0.02f, EventType.Repaint); ;
Handles.DrawLine(C_LastMidRefFootWorldPos, _SourceIKPos);
Handles.DrawWireDisc(C_LastMidRefFootWorldPos, Root.right, ScaleRef * 0.05f);
Handles.DrawLine(LastGroundHit.point, _SourceIKPos);
if (!string.IsNullOrWhiteSpace(_Editor_Label))
{
Handles.Label(_FinalIKPos, _Editor_Label);
}
Handles.color *= 0.6f;
Handles.DrawDottedLine(_FinalIKPos, _SourceIKPos, 2f);
if (A_LastElevation.sqrMagnitude > 0.05f) Handles.DrawAAPolyLine(2f, ankleAlignedOnGroundHitWorldPos, ankleAlignedOnGroundHitWorldPos + A_LastElevation);
if (Owner.AnimateFeet) Handles.DrawWireDisc(C_LastMidRefFootWorldPos, Owner.Up, ScaleRef * 0.1f);
Handles.color *= 0.7f;
Handles.SphereHandleCap(0, _FinalIKPos, Quaternion.identity, ScaleRef * 0.02f, EventType.Repaint); ;
}
public void _Editor_Raycasting_DrawSwingReference()
{
if (Application.isPlaying == false) return;
Vector3 lOff = new Vector3(1f, 1f, 0f);
if (Side == ELegSide.Left) lOff = new Vector3(-1f, 1f, 0f);
Vector3 origPos = Owner.BaseTransform.TransformPoint(lOff);
Handles.SphereHandleCap(0, origPos, Quaternion.identity, ScaleRef * 0.02f, EventType.Repaint);
Handles.DrawLine(origPos, origPos + Owner.BaseTransform.TransformVector(_G_RefernceSwing));
Handles.SphereHandleCap(0, origPos, Quaternion.identity, ScaleRef * 0.02f, EventType.Repaint);
}
public void _Editor_Raycasting_DrawControls()
{
if (Application.isPlaying == false) return;
if (!RaycastHitted) return;
Handles.color = Color.yellow * 0.8f;
Handles.SphereHandleCap(0, legGroundHit.point, Quaternion.identity, ScaleRef * 0.02f, EventType.Repaint);
Handles.DrawWireDisc(legGroundHit.point, legGroundHit.normal, ScaleRef * 0.05f);
if (Owner.RaycastStyle == LegsAnimator.ERaycastStyle.NoRaycasting) return;
Handles.DrawDottedLine(legGroundHit.point, lastRaycastingOrigin, 2f);
Handles.color *= 0.7f;
Handles.SphereHandleCap(0, lastRaycastingOrigin, Quaternion.identity, ScaleRef * 0.02f, EventType.Repaint);
}
string _Editor_Label = "";
public void _Editor_Hips_DrawControls()
{
if (Application.isPlaying == false) return;
if (!RaycastHitted) return;
Handles.color = new Color(0.7f, 0.7f, 0.2f, 0.8f);
}
public void _Editor_Glue_DrawControls()
{
if (!BoneEnd) return;
Handles.color = (Color.yellow * 0.8f).ChangeColorAlpha(0.5f);
float gRange = G_GlueTesholdRange * Owner.BaseTransform.lossyScale.x;
Vector3 heel = BoneEnd.TransformPoint(AnkleToHeel);
Vector3 off = Vector3.zero;
if (!Application.isPlaying)
{
off = Owner.BaseTransform.TransformVector(new Vector3(GluePointOffset.x, 0, GluePointOffset.y) * ScaleRef * Owner.GlueRangeThreshold );
}
else
off = GetGluePointOffset();
heel += off;
if (Application.isPlaying && Owner.LegsInitialized)
{
Handles.color *= G_Attached ? 0.1f : 0.7f;
if (G_DuringAttaching) Handles.color = new Color(0.8f, 0.5f, 0.1f, 1f);
}
if (Owner.AllowGlueBelowFoot > 0f)
{
Vector3 glueRange = -Owner.Up * (BelowFootRange * Owner.AllowGlueBelowFoot);
Handles.DrawWireDisc(heel + glueRange, Owner.BaseTransform.up, gRange);
Handles.SphereHandleCap(0, heel + glueRange, Quaternion.identity, gRange * 0.2f, EventType.Repaint);
Handles.DrawDottedLine(heel, heel + glueRange, 3f);
}
//Handles.DrawWireDisc(heel, Root.up, gRange);
Handles.color *= 0.7f;
Handles.SphereHandleCap(0, heel, Quaternion.identity, gRange * 0.4f, EventType.Repaint);
if (G_Attached)
{
Handles.color = new Color(0.8f, 0.5f, 0.1f, 1f);
Handles.DrawWireDisc(G_Attachement.GetRelevantHitPoint() + off, G_Attachement.GetRelevantNormal(), gRange);
//Handles.DrawWireDisc(G_Attachement.GetRelevantHitPoint(), G_Attachement.GetRelevantNormal(), gRange);
Handles.color *= 0.7f;
Handles.SphereHandleCap(0, heel, Quaternion.identity, gRange * 0.4f, EventType.Repaint);
}
if (!string.IsNullOrWhiteSpace(_Editor_Label))
{
Handles.Label(_FinalIKPos, _Editor_Label);
}
}
#endif
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a0b5a9b076fcfad44a31ca960521b466
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,382 @@
using System;
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
public partial class Leg
{
[NonSerialized] public bool G_InstantReglue = false;
public bool G_Attached { get; private set; }
public bool G_DuringAttaching { get { return G_LegAnimation.duringLegAdjustMovement; } }
public bool G_FadingIn { get { return G_LegAnimation.duringLegAdjustMovement; } }
/// <summary> Main glue blend for activation/deactivation </summary>
float _glueTargetBlend = 1f;
float _gluingCulldown = 0f;
protected bool G_JustLanded = false;
/// <summary> Can be used by custom modules </summary>
[NonSerialized] public float ExtraGluingBlend = 1f;
Vector3 _GlueLastAttachPosition;
Vector3 _GlueLastAttachPositionRootLocal;
Quaternion _GlueLastAttachRotation;
Vector3 _GluePosition;
Quaternion _GlueRotation;
/// <summary> Temporary variable to switch some optional calculations on and off </summary>
//int qualityLOD = 0;
#region Reference Swing
/// <summary> Variable for reference swing calculations </summary>
Vector3 _G_LastPreGlueSourceLocalIKPos;
/// <summary> Variable for reference swing calculations </summary>
Vector3 _G_PreGlueSourceLocalIKPos;
/// <summary> Variable for reference swing calculations </summary>
Vector3 _G_sd_RefSwing = Vector3.zero;
public Vector3 _G_RefernceSwing { get; private set; }
#endregion
bool _G_WasDisabled = true;
public enum GlueReposeRequest { None, Repose, ReposeIfFar }
[NonSerialized] public GlueReposeRequest G_RequestRepose = GlueReposeRequest.None;
/// <summary> !Without object scale multiplier! </summary>
float G_GlueTesholdRange { get { return Owner.ScaleReferenceNoScale * GlueThresholdMultiplier * Owner.GlueRangeThreshold * 0.5f; } }
void Gluing_Init()
{
G_AttachementHandler = new GlueAttachementHandler(this);
Glue_Reset(true);
}
public void Glue_Reset(bool initializing)
{
G_Attached = false;
if (initializing)
{
_GlueLastAttachPosition = BoneEnd.position;
_GlueLastAttachRotation = BoneEnd.rotation;
_GluePosition = BoneEnd.position;
_GlueLastAttachPositionRootLocal = ToRootLocalSpace(BoneEnd.position);
_G_LastPreGlueSourceLocalIKPos = _GlueLastAttachPosition;
_G_PreGlueSourceLocalIKPos = _SourceIKPos;
A_PreIKPosForGluing = BoneEnd.position;
_G_LasGroundedPosLocal = _GlueLastAttachPositionRootLocal;
}
var attach = new GlueAttachement();
attach.PosInAttachementLocal = _FinalIKPos;
attach.RotInAttachementLocal = _FinalIKRot;
G_Attachement = attach;
_G_RefernceSwing = Vector3.zero;
_G_WasDisabled = true;
G_AttachementHandler.Reset(initializing);
}
bool _G_WasGrounded = true;
Vector3 _G_LasGroundedPosLocal;
Quaternion _G_LasGroundedRotLocal;
void Gluing_Update()
{
#region Gluing blending switch, deactivation, reactivation etc.
_glueTargetBlend = Owner.GlueBlend * ExtraGluingBlend * Owner.RagdolledDisablerBlend * Owner.NotSlidingBlend;
if (Owner.GlueOnlyOnIdle) _glueTargetBlend *= 1f - Owner.IsMovingBlend;
if (Owner.IsGrounded)
{
if (Owner.GroundedTime < 0.25f)
{
G_JustLanded = true;
_glueTargetBlend *= 0.1f + Mathf.InverseLerp(0f, 0.25f, Owner.GroundedTime) * 0.9f;
}
else
G_JustLanded = false;
}
else
{
G_JustLanded = false;
_glueTargetBlend *= Owner.IsGroundedBlend;
}
if (_glueTargetBlend < 0.0001f)
{
_glueTargetBlend = 0f;
_G_WasDisabled = true;
return;
}
if (_G_WasDisabled)
{
Glue_Reset(false);
_G_WasDisabled = false;
}
if (_gluingCulldown > 0f) _gluingCulldown -= Owner.DeltaTime;
#endregion
#region Is Ungrounding Handling
if (!Owner.IsGrounded)
{
if (_G_WasGrounded)
{
_G_WasGrounded = false;
_G_LasGroundedPosLocal = ToRootLocalSpace(_GluePosition);
_G_LasGroundedRotLocal = _GlueRotation;
G_AttachementHandler.legMoveAnimation.Reset();
}
_GluePosition = RootSpaceToWorld(_G_LasGroundedPosLocal);
_GlueRotation = _G_LasGroundedRotLocal;
return;
}
_G_WasGrounded = true;
#endregion
#region Reference Swing Calculate
if (Owner._glueModeExecuted == EGlueMode.Moving && Owner.SwingHelper > 0f)
{
Vector3 swingVelo = AnkleH.LastKeyframeRootPos - _G_LastPreGlueSourceLocalIKPos;
if (swingVelo.magnitude > Owner.ScaleReferenceNoScale * 0.001f)
{
_G_LastPreGlueSourceLocalIKPos = _G_PreGlueSourceLocalIKPos;
}
_G_PreGlueSourceLocalIKPos = AnkleH.LastKeyframeRootPos;
_G_RefernceSwing = Vector3.SmoothDamp(_G_RefernceSwing, swingVelo * 2f, ref _G_sd_RefSwing, 0.04f, 100000f, Owner.DeltaTime);
}
else
{
_G_RefernceSwing = Vector3.zero;
_G_sd_RefSwing = Vector3.zero;
}
#endregion
#region Attaching / Detaching check
_Glue_AskingForDetach = false;
if (G_Attached) // Checking glue transition during being attached
{
bool attach = !Glue_CheckDetachement();
if (attach == false) attach = !Glue_CheckIdleDetachementConfirm();
if (attach == false)
{
G_Attached = attach; // Update the attached leg state
G_AttachementHandler.OnLegRequireRepose();
// Check for re-attach
attach = Glue_Conditions_Attach();
}
else
{
// If can't attach anyway, dont do it
if (!Glue_Conditions_Attach()) attach = false;
}
if (attach) G_AttachementHandler.TransitionToGlueAnimation();
else G_AttachementHandler.TransitionToDisableGlueAnimation();
//G_DuringAttaching = attach;
}
else // Checking glue transition target conditions when not yet fully attached
{
bool attach = Glue_Conditions_Attach();
if (attach)
{
G_AttachementHandler.TransitionToGlueAnimation();
}
else
{
//G_AttachementHandler.TriggerDetach();
G_AttachementHandler.TransitionToDisableGlueAnimation();
}
//G_DuringAttaching = attach;
}
#endregion
if (G_InstantReglue)
{
G_AttachementHandler.SheduleInstantTransition();
G_InstantReglue = false;
}
G_AttachementHandler.UpdateTransitioning(G_DuringAttaching);
Gluing_UpdateAttachement();
}
bool Glue_TriggerFinalAttach()
{
if (legGroundHit.transform || _UsingEmptyRaycast)
{
G_Attached = true;
G_Attachement = new GlueAttachement(this, legGroundHit);
return true;
}
return false;
}
/// <summary> During being attached </summary>
void Gluing_UpdateAttachement()
{
if (G_Attachement.NoTransform == false && G_Attachement.AttachedTo == null)
{
// Reset attachement on attached to destroy
G_Attachement = new GlueAttachement();
G_AttachementHandler.OnLegRequireRepose();
G_Attached = false;
}
if (G_Attached == false) // Transition towards attachement point
{
_GluePosition = G_AttachementHandler.GetGluePosition();
Gluing_DragStretchApply();
if (Owner.AnimateFeet)
{
if (Owner.LimitFeetYaw > 0f)
//_GlueRotation = Quaternion.LerpUnclamped(A_LastApppliedAlignRot, G_AttachementHandler.GetGlueRotation(), 0.5f);
_GlueRotation = G_AttachementHandler.GetGlueRotation();
else
_GlueRotation = A_LastApppliedAlignRot;
}
}
else // Adjusting foot position in attachement space
{
_GlueLastAttachPosition = G_Attachement.GetRelevantAlignedHitPoint(this);
_GlueLastAttachPositionRootLocal = ToRootLocalSpace(_GlueLastAttachPosition);
Quaternion newAttachementRot = G_Attachement.GetRelevantAttachementRotation();
//if (qualityLOD == 0)
//{
// float angle = Quaternion.Angle(_GlueLastAttachRotation, newAttachementRot);
// if (angle > 1f)
// {
// newAttachementRot = Quaternion.Lerp(_GlueLastAttachRotation, newAttachementRot, Owner.DeltaTime * (4f + angle * 0.2f));
// }
//}
_GlueLastAttachRotation = newAttachementRot;
_GluePosition = G_AttachementHandler.GetGluePosition();
Gluing_DragStretchApply();
if (Owner.AnimateFeet)
{
if (Owner.LimitFeetYaw > 0f)
_GlueRotation = G_AttachementHandler.GetGlueRotation();
//_GlueRotation = Quaternion.LerpUnclamped(A_LastApppliedAlignRot, G_AttachementHandler.GetGlueRotation(), 0.5f);
else
{
_GlueRotation = A_LastApppliedAlignRot;
}
}
}
G_AttachementHandler.PostUpdate();
}
/// <summary> World Space </summary>
Vector3 G_GlueDragOffset = Vector3.zero;
void Gluing_DragStretchApply()
{
if (Owner.AllowGlueDrag > 0f) // Shifting too much stretched glue point
{
float stretchFactor = IKProcessor.GetStretchValue(_GluePosition - Owner._LastAppliedHipsStabilityOffset);
float baseStretchFact = Mathf.LerpUnclamped(1f, 0.825f, Owner.AllowGlueDrag);
float stretchHelper = baseStretchFact * Mathf.LerpUnclamped(1f, LegStretchLimit, Owner.AllowGlueDrag);
if (stretchHelper > baseStretchFact) stretchHelper = baseStretchFact;
Vector3 targetDragPos = _GluePosition;
if (stretchFactor > stretchHelper * 1.1f)
{
float diff = (stretchFactor - stretchHelper * 1.1f) * 2f * Mathf.Min(1f, Owner.AllowGlueDrag);
if (A_PreWasAligning)
targetDragPos = Vector3.Lerp(_GluePosition, ankleAlignedOnGroundHitWorldPos, diff);
else
targetDragPos = Vector3.Lerp(_GluePosition, A_PreIKPosForGluing, diff);
}
Vector3 offset = targetDragPos - _GluePosition;
G_GlueDragOffset = Vector3.Lerp(G_GlueDragOffset, offset, Owner.DeltaTime * 14f);
if( float.IsNaN( G_GlueDragOffset.x ) || float.IsNaN( G_GlueDragOffset.z ) ) G_GlueDragOffset = Vector3.zero;
}
}
/// <summary> Apply Glue / Leg Adjust Animation to the IK controls </summary>
void Gluing_ApplyCoords()
{
if (_glueTargetBlend < 0.0001f) // Gluing inactive
{
return;
}
float applyBlend = _glueTargetBlend * G_AttachementHandler.glueAnimationBlend;
if (applyBlend >= 1f)
{
_FinalIKPos = _GluePosition + G_GlueDragOffset;
if (Owner.AnimateFeet)
{
_FinalIKRot = _GlueRotation;
}
}
else
{
_FinalIKPos = Vector3.LerpUnclamped(A_PreIKPosForGluing, _GluePosition + G_GlueDragOffset, applyBlend);
if (Owner.AnimateFeet) //A_LastTargetAlignRot
{
_FinalIKRot = Quaternion.LerpUnclamped(_FinalIKRot, _GlueRotation, applyBlend);
}
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7c6cd7b528bdb434dae2ad86f2616cbe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,129 @@
using FIMSpace.AnimationTools;
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
public partial class Leg
{
LegHelper _h_boneStart;
LegHelper _h_boneMid;
LegHelper _h_boneEnd;
public class LegHelper
{
public Transform Bone;
public LegHelper Child;
public Vector3 InitPositionRootSpace;
public Vector3 LastKeyframeRootPos;
public LegHelper(Leg leg, Transform bone)
{
Bone = bone;
InitPositionRootSpace = leg.ToRootLocalSpace(bone.position);
}
public void Calibrate(Leg leg, Vector3 wPos)
{
LastKeyframeRootPos = leg.ToRootLocalSpace(wPos);
}
}
}
public Leg Setup_TryAutoLegSetup( Leg scheme, Transform toSetup, bool apply = true)
{
if (scheme == null) return null;
if (toSetup == null) return null;
// First check if most bottom bone has the same depth, conditions to replicate scheme properly
Transform mostBottomScheme = SkeletonRecognize.GetBottomMostChildTransform(scheme.BoneStart);
int bottomDepth = SkeletonRecognize.SkeletonInfo.GetDepth(mostBottomScheme, scheme.BoneStart);
Transform mostBottomToSet = SkeletonRecognize.GetBottomMostChildTransform(toSetup);
int bottomDepthToSet = SkeletonRecognize.SkeletonInfo.GetDepth(mostBottomToSet, toSetup);
if (mostBottomToSet == null) return null;
if (mostBottomScheme == null) return null;
if (bottomDepthToSet != bottomDepth) return null;
if (bottomDepth == 0) return null;
if (bottomDepthToSet == 0) return null;
Transform targetStart, targetMid, targetEnd;
targetStart = toSetup;
int referenceDepth = SkeletonRecognize.SkeletonInfo.GetDepth(mostBottomScheme, scheme.BoneEnd);
targetEnd = mostBottomToSet;
for (int i = 0; i < referenceDepth; i++) { if (targetEnd.parent == null) return null; targetEnd = targetEnd.parent; }
referenceDepth = SkeletonRecognize.SkeletonInfo.GetDepth(mostBottomScheme, scheme.BoneMid);
targetMid = mostBottomToSet;
for (int i = 0; i < referenceDepth; i++) { if (targetEnd.parent == null) return null; targetMid = targetMid.parent; }
#region Commented but can be helpful later
//referenceDepth = SkeletonRecognize.SkeletonInfo.GetDepth(scheme.BoneMid, scheme.BoneStart);
//targetStart = targetMid;
//for (int i = 0; i < referenceDepth; i++) { if (targetEnd.parent == null) return null; targetStart = targetStart.parent; }
#endregion
if (targetStart == targetMid) return null;
if (targetStart == targetEnd) return null;
if (targetMid == targetEnd) return null;
float referenceLength = scheme.LegLimbLength();
Leg leg = new Leg();
leg.BoneStart = targetStart;
leg.BoneMid = targetMid;
leg.BoneEnd = targetEnd;
leg.Owner = this;
if (leg.LegLimbLength() < referenceLength * 0.2f) return null;
bool can = true;
for (int l = 0; l < Legs.Count; l++)
{
if (Legs[l].BoneStart == leg.BoneStart) { can = false; break; }
if (Legs[l].BoneStart == leg.BoneMid) { can = false; break; }
if (Legs[l].BoneMid == leg.BoneStart) { can = false; break; }
if (Legs[l].BoneMid == leg.BoneMid) { can = false; break; }
}
if (!can) return null;
if (apply)
{
Legs.Add(leg);
leg.DefineLegSide(this);
leg.RefreshLegAnkleToHeelAndFeetAndAxes(BaseTransform);
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty(this);
#endif
}
return leg;
}
public void Setup_TryAutoLegsSetup(Leg scheme, Transform parentOfLegs)
{
if (parentOfLegs == null) return;
for (int i = 0; i < parentOfLegs.childCount; i++)
{
Setup_TryAutoLegSetup(scheme, parentOfLegs.GetChild(i), true);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a1315eea780ea4a4fbb7f8a8106b79dc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,259 @@
using FIMSpace.FTools;
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
public partial class Leg
{
Transform Root { get { return Owner.BaseTransform; } }
float ScaleRef { get { return Owner.ScaleReference; } }
float FloorLevel { get { return Owner._glueingFloorLevel; } }
float DeltaTime { get { return Owner.DeltaTime; } }
public LegHelper ThighH { get { return _h_boneStart; } }
FimpIK_Limb.IKBone ThighIK { get { return IKProcessor.StartIKBone; } }
public LegHelper LowerLegH { get { return _h_boneMid; } }
FimpIK_Limb.IKBone LowerLegIK { get { return IKProcessor.MiddleIKBone; } }
public LegHelper AnkleH { get { return _h_boneEnd; } }
public FimpIK_Limb.IKBone AnkleIK { get { return IKProcessor.EndIKBone; } }
/// <summary> Assigned during initialization </summary>
private Vector3 C_AnkleToHeelRootSpace = Vector3.one;
Vector3 C_LastHeelWorldPos;
Vector3 C_LastHeelRootSpacePos;
public Vector3 C_LastMidRefFootWorldPos { get; private set; }
public Vector3 C_LastMidRefFootRootSpacePos { get; private set; }
Vector3 C_LastFootEndWorldPos;
Vector3 C_LastFootEndRootSpacePos;
/// <summary> When Y > 0 that means foot is above ground </summary>
public Vector3 C_Local_MidFootPosVsGroundHit { get; private set; }
Vector3 C_Local_AnkleToHeelRotated;
//float C_AnkleHeightToGlueFloorHeight = 0f;
/// <summary> Below zero when foot in animation is below root origin zero position, greater than zero when foot is above root origin zero position </summary>
public float C_Local_FootElevateInAnimation { get; private set; }
public float C_AnkleToHeelWorldHeight { get; private set; }
public float C_AnimatedAnkleFlatHeight { get; private set; }
float _C_DynamicYScale = 1f;
void Controll_Init()
{
C_AnkleToHeelRootSpace = ToRootLocalSpace(Root.position + BoneEnd.TransformVector(AnkleToHeel));
ThighH.Calibrate(this, ThighH.Bone.position);
AnkleH.Calibrate(this, AnkleH.Bone.position);
}
Vector3 TransformVectorAnkleWithAlignedRotation(Vector3 offset)
{
return ankleAlignedOnGroundHitRotation * (Vector3.Scale( offset, BoneEnd.lossyScale) );
}
/// <summary>
/// On Begin LateUpdate
/// </summary>
void Controll_Calibrate()
{
_C_DynamicYScale = Owner.DynamicYScale;
ThighH.Calibrate(this, ThighH.Bone.position);
AnkleH.Calibrate(this, _SourceIKPosUnchangedY);
Vector3 footEnd = AnkleToHeel;
// Mid foot with aligned rotation use
Vector3 ankleToHeelShift = TransformVectorAnkleWithAlignedRotation(AnkleToHeel);
//if (Owner.AnimateFeet) footEnd = AnkleToFeetEnd;
//if (Owner.AnimateFeet)
//{
// footEnd = AnkleToFeetEnd;
// footEnd = TransformVectorAnkleWithAlignedRotation(footEnd);
// C_LastFootEndWorldPos = _SourceIKPosUnchangedY + footEnd + (footEnd - ankleToHeelShift) * Owner.FeetLengthAdjust;
//}
//else
// C_LastFootEndWorldPos = _SourceIKPosUnchangedY + TransformVectorAnkleWithAlignedRotation(footEnd);
C_LastFootEndWorldPos = _SourceIKPosUnchangedY + TransformVectorAnkleWithAlignedRotation(footEnd);
C_LastFootEndRootSpacePos = ToRootLocalSpace(C_LastFootEndWorldPos);
C_AnimatedAnkleFlatHeight = ToRootLocalSpaceDir(ankleToHeelShift).y;
//C_AnkleHeightToGlueFloorHeight = (FloorLevel * Owner.baseTransform.lossyScale.y) + C_AnimatedAnkleFlatHeight;
//_Editor_Label = "animH: " + C_AnimatedAnkleFlatHeight;
//if (PlaymodeIndex == 0) UnityEngine.Debug.Log("ankleToHeelShift " + ankleToHeelShift.x+","+ankleToHeelShift.y+","+ankleToHeelShift.z);
C_LastHeelWorldPos = _SourceIKPosUnchangedY + ankleToHeelShift;
C_LastHeelRootSpacePos = ToRootLocalSpace(C_LastHeelWorldPos);
if (Owner.AnimateFeet)
{
C_LastMidRefFootWorldPos = Vector3.LerpUnclamped(C_LastFootEndWorldPos, C_LastHeelWorldPos, FootMiddlePosition);
C_LastMidRefFootRootSpacePos = Vector3.LerpUnclamped(C_LastFootEndRootSpacePos, C_LastHeelRootSpacePos, FootMiddlePosition);
// Mid foot with aligned rotation use
//C_LastMidRefFootWorldPos = Vector3.LerpUnclamped(C_LastFootEndWorldPos, C_LastHeelWorldPos, FootMiddlePosition);
//ankleAlignedOnGroundHitRotation
}
else
{
C_LastMidRefFootRootSpacePos = C_LastHeelRootSpacePos;
C_LastMidRefFootWorldPos = C_LastHeelWorldPos;
}
//if (PlaymodeIndex == 0) UnityEngine.Debug.DrawRay(C_LastHeelWorldPos, Vector3.forward, Color.green, 0.11f);
//if (PlaymodeIndex == 0) UnityEngine.Debug.DrawRay(C_LastMidRefFootWorldPos, Vector3.forward, Color.yellow, 0.11f);
C_Local_MidFootPosVsGroundHit = C_LastMidRefFootRootSpacePos - groundHitRootSpacePos;
//UnityEngine.Debug.DrawRay(RootSpaceToWorld(C_LastMidRefFootRootSpacePos), Vector3.up, Color.green, .02f);
//UnityEngine.Debug.DrawRay(RootSpaceToWorld(groundHitRootSpacePos), Vector3.up, Color.yellow, .02f);
//UnityEngine.Debug.DrawLine(RootSpaceToWorld(groundHitRootSpacePos), RootSpaceToWorld(C_LastMidRefFootRootSpacePos), Color.yellow, .02f);
//if (C_Local_MidFootPosVsGroundHit.y < 0f) _Editor_Label = "groundalign";
//else _Editor_Label = "nope";
C_Local_FootElevateInAnimation = C_LastMidRefFootRootSpacePos.y - ParentHub._Hips_LastHipsOffset;
C_Local_AnkleToHeelRotated = ToRootLocalSpace(Root.position + BoneEnd.TransformVector(AnkleToHeel));
C_AnkleToHeelWorldHeight = BoneEnd.TransformVector(AnkleToHeel).magnitude;
}
Vector3 RootSpaceToWorldVec(Vector3 localVec)
{
return Owner.RootToWorldSpaceVec(localVec);
}
Vector3 RootSpaceToWorld(Vector3 rootLocal)
{
return Owner.RootToWorldSpace(rootLocal);
}
Vector3 ToRootLocalSpaceDir(Vector3 worldDir)
{
return Owner.ToRootLocalSpaceVec(worldDir);
}
Vector3 ToRootLocalSpace(Vector3 worldPos)
{
return Owner.ToRootLocalSpace(worldPos);
}
Vector3 ChangeLocalY(Vector3 worldPos, float targetLocalY)
{
worldPos = ToRootLocalSpace(worldPos);
worldPos.y = targetLocalY;
return RootSpaceToWorld(worldPos);
}
Vector3 ChangeLocalPosExceptY(Vector3 worldPos, Vector3 targetWorldPos)
{
worldPos = ToRootLocalSpace(worldPos);
Vector3 newLocal = ToRootLocalSpace(targetWorldPos);
worldPos.x = newLocal.x;
worldPos.z = newLocal.z;
return RootSpaceToWorld(worldPos);
}
void Control_StepEventCalcs()
{
StepEventRestore();
if (Owner.UseGluing == false) return;
if (_StepSent) return;
if (Owner._glueModeExecuted == EGlueMode.Idle)
{
//if (G_Transition.LastGlueMode != EGlueMode.Idle) return;
//if (G_Transition._legMoveFactor < 0.1f) return;
if (G_GlueInternalTransition >= 0.85f - Owner.EventExecuteSooner)
{
if (_ToConfirmStepEvent > 0.1f)
{
SendStepEvent(G_AttachementHandler.legMoveDistanceFactor, EStepType.IdleGluing);
}
else
{
float proMul = Mathf.InverseLerp(1f, 0f, LegAnimatingSettings.RaiseYAxisCurve.Evaluate(G_GlueInternalTransition));
_ToConfirmStepEvent += DeltaTime * (3f + 3f * proMul);
}
}
else
{
_ToConfirmStepEvent = 0f;
}
}
else // Movement Stage
{
//if (PlaymodeIndex == 1) UnityEngine.Debug.Log("goes here");
//if (Owner.MovingTime < 0.1f) return;
if (G_HandlerExecutingLegAnimationMode != EGlueMode.Moving) return;
if (Owner.SendOnMovingGlue == false) return;
if (G_CustomForceNOTAttach) return;
//if (Owner.SwingHelper > 0f)
//{
// Vector3 desiredLocal = ToRootLocalSpaceDir(Owner.DesiredMovementDirection);
// Vector3 legSwingLocal = ToRootLocalSpaceDir(_G_RefernceSwing);
// float swingDot = Vector3.Dot(desiredLocal.normalized, legSwingLocal.normalized);
// if (swingDot < (1f - Owner.SwingHelper * 1f) * 1f) { return; } // Dont allow attach when swinging foot in the same direction as desired direction
//}
float heightFactor = FloorLevel * Owner.BaseTransform.lossyScale.y + C_AnkleToHeelWorldHeight * 0.5f + A_LastAlignHeightCompareValue * (1.65f + Owner.EventExecuteSooner);
if (G_CustomForceAttach)
{
_ToConfirmStepEvent += DeltaTime * 5f;
heightFactor += ScaleRef * 0.1f;
}
//if (PlaymodeIndex == 0) UnityEngine.Debug.Log("ightDiff " + A_LastAlignHeightDiff + " vs " + heightFactor);
if (A_LastAlignHeightDiff <= heightFactor)
{
if (_ToConfirmStepEvent > 0.2f)
{
SendStepEvent(1f, EStepType.MovementGluing);
_ToConfirmStepEvent = 0f;
}
else
{
_ToConfirmStepEvent += DeltaTime;
if (A_LastAlignHeightDiff < heightFactor * 0.75f)
_ToConfirmStepEvent += DeltaTime * 1f;
if (A_LastAlignHeightDiff < heightFactor * 0.5f)
_ToConfirmStepEvent += DeltaTime * 1f;
}
}
else
_ToConfirmStepEvent = 0f;
}
}
internal void StepEventSentInCustomWay()
{
_StepSent = true;
_StepSentAt = Time.unscaledTime;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 61fa30767d669814cb571ee25eb95e79
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,256 @@
using FIMSpace.FTools;
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
public partial class Leg
{
public FimpIK_Limb IKProcessor { get; private set; }
public void IK_Initialize(bool generateNew = true)
{
if (generateNew) IKProcessor = new FimpIK_Limb();
if (BoneFeet) IKProcessor.SetLegWithFeet(BoneStart, BoneMid, BoneEnd, BoneFeet);
else IKProcessor.SetBones(BoneStart, BoneMid, BoneEnd);
IKProcessor.Init(Owner.BaseTransform);
IKProcessor.UseEndBoneMapping = false;
IKProcessor.IKWeight = 1f;
IKProcessor.IKPositionWeight = 1f;
IKProcessor.FootRotationWeight = 1f;
IKProcessor.ManualHintPositionWeight = 0f;
IKProcessor.FeetStretchSensitivity = 0.9f;
IKProcessor.FeetFadeQuicker = 1.1f;
IKProcessor.FeetStretchLimit = 0.8f;
IKProcessor.HumanoidAnimator = Owner.Mecanim;
IKProcessor.IsRight = (Side == ELegSide.Right);
_FinalIKPos = IKProcessor.EndIKBone.transform.position;
_PreviousFinalIKPos = _FinalIKPos;
_PreviousFinalIKPosForStability = _FinalIKPos;
_PreviousFinalIKPosRootLocal = ToRootLocalSpace( _FinalIKPos );
IKProcessor.IKTargetPosition = _FinalIKPos;
IKProcessor.IKTargetRotation = _FinalIKRot;
}
public void AssignCustomIKProcessor(FimpIK_Limb ik)
{
IKProcessor = ik;
IK_Initialize(false);
}
/// <summary> If not using IK multiplicator it's simply _SourceIKPos </summary>
Vector3 _SourceIKPosUnchangedY;
Vector3 _SourceIKPos;
public Vector3 _FinalIKPos;
Quaternion _SourceIKRot;
Quaternion _FinalIKRot;
//bool customOverwriteIKPositions = false;
bool customOverwritingIKPos = false;
Vector3 customOverwritePos = Vector3.zero;
public void OverrideTargetIKPosition(Vector3? targetIKPos)
{
if (targetIKPos == null)
{
customOverwritingIKPos = false;
}
else
{
customOverwritingIKPos = true;
customOverwritePos = targetIKPos.Value;
}
}
bool customOverwritingIKRot = false;
Quaternion customOverwriteRot = Quaternion.identity;
public void OverrideTargetIKRotation(Quaternion? targetIKRot)
{
if (targetIKRot == null)
{
if (customOverwritingIKRot == true) IKProcessor.FootRotationWeight = 1f;
customOverwritingIKRot = false;
}
else
{
customOverwritingIKRot = true;
customOverwriteRot = targetIKRot.Value;
}
}
public void OverrideFinalIKPos(Vector3 pos) { _FinalIKPos = pos; }
public void OverrideFinalAndSourceIKPos(Vector3 pos) { _FinalIKPos = pos; _SourceIKPos = pos; }
public Vector3 GetFinalIKPos() { return _FinalIKPos; }
public Vector3 GetSourceIKPos() { return _SourceIKPos; }
public Quaternion GetFinalIKRot() { return _FinalIKRot; }
public Quaternion GetSourceIKRot() { return _SourceIKRot; }
public void OverrideFinalIKRot(Quaternion rot) { _FinalIKRot = rot; }
public Vector3 _PreviousFinalIKPos { get; private set; }
public Vector3 _PreviousFinalIKPosRootLocal { get; private set; }
public Vector3 _PreviousFinalIKPosForStability { get; private set; }
public Quaternion _PreviousFinalIKRot { get; private set; }
public Vector3 _AnimatorStartBonePos { get; private set; }
public Vector3 _AnimatorMidBonePos { get; private set; }
public Vector3 _AnimatorEndBonePos { get; private set; }
bool _wasFixedCalibrateAnimationCaptured = false;
public Quaternion _AnimatorStartBoneLocRot { get; private set; }
public Quaternion _AnimatorMidBoneLocRot { get; private set; }
public Quaternion _AnimatorEndBoneLocRot { get; private set; }
bool _wasGrounded = true;
Vector3 _ungroundLocalIKCache;
/// <summary>
/// Should be called after hips adjustements
/// </summary>
public void IK_PreUpdate()
{
IKProcessor.CallPreCalibrate = Owner.Calibrate == ECalibrateMode.Calibrate;
#region Handling unground fade (return;)
if (Owner.IsGrounded == false)
{
if (_wasGrounded)
{
_ungroundLocalIKCache = ToRootLocalSpace(_PreviousFinalIKPos);
_wasGrounded = false;
}
_SourceIKPos = RootSpaceToWorld(_ungroundLocalIKCache);
_SourceIKPos = Vector3.Lerp(_SourceIKPos, IKProcessor.EndIKBone.transform.position, 1f - Owner.IsGroundedBlend);
_ungroundLocalIKCache = ToRootLocalSpace(_SourceIKPos);
_SourceIKRot = BoneEnd.rotation;// IKProcessor.EndIKBone.transform.rotation;
_SourceIKPosUnchangedY = _SourceIKPos;
_FinalIKPos = _SourceIKPos;
_FinalIKRot = _SourceIKRot;
return;
}
else
{
_wasGrounded = true;
}
#endregion
//IKProcessor.RefreshAnimatorCoords();
//_SourceIKPos = _AnimtorEndBonePos + Owner.Up * Owner._Hips_LastHipsOffset;
if (!_overwrittenSourceIKPos)
_SourceIKPos = IKProcessor.EndIKBone.transform.position;
else
_overwrittenSourceIKPos = false;
_SourceIKRot = BoneEnd.rotation;// IKProcessor.EndIKBone.transform.rotation;
_SourceIKPosUnchangedY = _SourceIKPos;
//if (ParentHub != Owner.HipsSetup) _SourceIKPosUnchangedY += Owner.Up * ParentHub._Hips_StepHeightAdjustOffset;
_FinalIKPos = _SourceIKPos;
_FinalIKRot = _SourceIKRot;
}
public void IK_PostUpdate()
{
if (customOverwritingIKPos) // Custom IK position follow implementation
{
_FinalIKPos = customOverwritePos;
if (customOverwritingIKRot)
{
IKProcessor.FootRotationWeight = 1f;
_FinalIKRot = customOverwriteRot;
}
else
{
IKProcessor.FootRotationWeight = 0f;
}
}
else
{
if (G_LegAnimation.LegAdjustementFootAngleOffset != 0f || FootPitchOffset != 0f)
{
_FinalIKRot = Quaternion.AngleAxis(G_LegAnimation.LegAdjustementFootAngleOffset + FootPitchOffset, _SourceIKRot * AnkleIK.right) * _FinalIKRot;
}
}
// NaN protection
if ( float.IsNaN( _FinalIKPos.x) || float.IsNaN( _FinalIKPos.y ) || float.IsNaN( _FinalIKPos.z ) )
{
Reset();
_FinalIKPos = RootSpaceToWorld( InitialPosInRootSpace );
Gluing_Init();
}
_PreviousFinalIKPosForStability = _FinalIKPos;
IKProcessor.IKTargetPosition = _FinalIKPos;
IKProcessor.IKTargetRotation = _FinalIKRot;
//if ( ApplyIKTo ) ApplyIKTo.position = _FinalIKPos;
if (IKProcessor.IKWeight > 0f)
{
if (!Owner.UseCustomIK) if (LegStretchLimit < 1.1f) IKProcessor.ApplyMaxStretchingPreprocessing(LegStretchLimit, 3f);
ExtraIKPostProcessingApply();
if (!Owner.UseCustomIK) IKProcessor.Update();
}
_PreviousFinalIKPos = IKProcessor.IKTargetPosition;
_PreviousFinalIKPosRootLocal = ToRootLocalSpace( _PreviousFinalIKPos );
if (Owner.AnimateFeet) _PreviousFinalIKRot = IKProcessor.IKTargetRotation;
//UnityEngine.Debug.DrawRay(IKProcessor.IKTargetPosition, IKProcessor.IKTargetRotation * Vector3.forward, Color.green, 0.01f);
}
public void IK_UpdateParamsBase()
{
IKProcessor.IKWeight = Owner._MainBlend * LegBlendWeight * InternalModuleBlendWeight;
BlendWeight = IKProcessor.IKWeight;
IKProcessor.InverseHint = InverseHint;
}
public void IK_UpdateParams()
{
IK_UpdateParamsBase();
IKProcessor.AutoHintMode = Owner.IKHintMode;
//IK_UseIKMultiplicator = Owner.IKOffsetsMultiply != Vector3.one;
IKProcessor.FeetStretchSensitivity = 0.7f + 0.6f * FeetSensitivity;
IKProcessor.FeetFadeQuicker = 0.95f + 0.35f * FeetSensitivity;
IKProcessor.FeetStretchLimit = 0.8f + 0.2f * FeetSensitivity;
IKProcessor.disableFeet = !UseFeet;
}
public void RandomizeIndividualSettings(float from, float to)
{
GlueThresholdMultiplier = UnityEngine.Random.Range(Mathf.Lerp(from, to, 0.4f), to);
LegMoveSpeedMultiplier = UnityEngine.Random.Range(from, to);
LegRaiseMultiplier = UnityEngine.Random.Range(from, to);
}
bool _overwrittenSourceIKPos = false;
public void OverrideAnimatorAnklePosition(Vector3 targetPos)
{
_overwrittenSourceIKPos = true;
_AnimatorEndBonePos = targetPos + (Owner._LastAppliedHipsFinalPosition - ParentHub.LastKeyframePosition);
_SourceIKPos = _AnimatorEndBonePos;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0faf314597b95914cbf608864566388c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,458 @@
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
public partial class Leg
{
public bool RaycastHitted { get; private set; }
public RaycastHit LastGroundHit { get { return legGroundHit; } }
public RaycastHit legGroundHit;
public RaycastHit lastGroundHitWithTarget;
public Vector3 groundHitRootSpacePos { get; private set; }
public Vector3 lastRaycastingOrigin { get; private set; }
public Vector3 lastRaycastingEndPoint { get; private set; }
Vector3 previousAnkleAlignedOnGroundHitWorldPos;
public Vector3 ankleAlignedOnGroundHitWorldPos { get; private set; }
Vector3 ankleAlignedOnGroundHitRootLocal;
Quaternion ankleAlignedOnGroundHitRotation;
RaycastHit replacementHit;
#region Custom Raycast
/// <summary> If source raycast hitted ground - before custom raycast override </summary>
public bool User_RaycastHittedSource { get; private set; }
bool _UsingEmptyRaycast = false;
bool _UsingCustomRaycast = false;
bool _disableSourceRaycast = false;
float _CustomRaycastBlendIn = 0f;
RaycastHit _CustomRaycastHit;
Vector3 _PreviousCustomRaycastingStartIKPos;
Vector3 _PreviousCustomRaycastingIKPos;
public void User_OverrideRaycastHit(Transform tr, bool disableSourceRaycast = true)
{
if (!_UsingCustomRaycast)
{
_CustomRaycastBlendIn = 0f;
_PreviousCustomRaycastingStartIKPos = C_LastHeelWorldPos;
}
_disableSourceRaycast = disableSourceRaycast;
_UsingCustomRaycast = true;
RaycastHit hit = new RaycastHit();
hit.point = tr.position;
hit.normal = tr.up;
_CustomRaycastOnBlendIn(hit);
}
public void User_OverrideRaycastHit(RaycastHit hit, bool disableSourceRaycast = true)
{
if (!_UsingCustomRaycast)
{
_CustomRaycastBlendIn = 0f;
_PreviousCustomRaycastingStartIKPos = C_LastHeelWorldPos;
}
_disableSourceRaycast = disableSourceRaycast;
_UsingCustomRaycast = true;
_CustomRaycastOnBlendIn(hit);
}
public void User_RestoreRaycasting()
{
if (_UsingCustomRaycast) _CustomRaycastBlendIn = 1f;
_UsingCustomRaycast = false;
_disableSourceRaycast = false;
}
void _CustomRaycastOnBlendIn(RaycastHit hit)
{
_CustomRaycastBlendIn += Owner.DeltaTime * 6f;
if (_CustomRaycastBlendIn > 1f) _CustomRaycastBlendIn = 1f;
hit.point = Vector3.LerpUnclamped(_PreviousCustomRaycastingStartIKPos, hit.point, _CustomRaycastBlendIn);
_CustomRaycastHit = hit;
_PreviousCustomRaycastingIKPos = hit.point;
}
void _CustomRaycastOnBlendOut()
{
if (_UsingCustomRaycast) return;
if (_CustomRaycastBlendIn <= 0f) return;
_CustomRaycastBlendIn -= Owner.DeltaTime * 8f;
if (_CustomRaycastBlendIn < 0f) _CustomRaycastBlendIn = 0f;
if (RaycastHitted == false)
{
legGroundHit = _CustomRaycastHit;
return;
}
RaycastHit hit = legGroundHit;
hit.point = Vector3.LerpUnclamped(hit.point, _PreviousCustomRaycastingIKPos, _CustomRaycastBlendIn);
hit.normal = Vector3.SlerpUnclamped(hit.normal, _CustomRaycastHit.normal, _CustomRaycastBlendIn);
legGroundHit = hit;
}
#endregion
public float raycastSlopeAngle { get; private set; }
void Raycasting_Init()
{
ankleAlignedOnGroundHitWorldPos = BoneEnd.position;
raycastSlopeAngle = 0f;
}
public void OverrideControlPositionsWithCurrentIKState()
{
AnkleH.LastKeyframeRootPos = ToRootLocalSpace(_FinalIKPos);
_AnimatorEndBonePos = _FinalIKPos;
}
public void OverrideSourceIKPos()
{
OverrideSourceIKPos(_FinalIKPos);
}
public void OverrideSourceIKPos(Vector3 newSrc)
{
_SourceIKPos = newSrc;
}
bool _noRaycast_skipFeetCalcs = false;
public void Raycasting_PreLateUpdate()
{
RaycastHitted = false;
_noRaycast_skipFeetCalcs = false;
if (!_disableSourceRaycast)
{
if (Owner.RaycastStyle == ERaycastStyle.NoRaycasting)
{
GenerateZeroFloorRaycastHit();
CustomRaycastValidate();
_noRaycast_skipFeetCalcs = true;
_UsingEmptyRaycast = true;
ankleAlignedOnGroundHitRotation = _SourceIKRot;
}
else
{
_UsingEmptyRaycast = false;
if (Owner.RaycastStyle == ERaycastStyle.StraightDown)
{
Raycast_StraightDown();
}
else if (Owner.RaycastStyle == ERaycastStyle.OriginToFoot)
{
Raycast_OriginToFoot();
}
else if (Owner.RaycastStyle == ERaycastStyle.OriginToFoot_DownOnNeed)
{
Raycast_OriginToFoot();
if (!RaycastHitted) Raycast_StraightDown();
}
else if (Owner.RaycastStyle == ERaycastStyle.AlongBones)
{
Raycast_AlongBones();
if (!RaycastHitted) { Raycast_StraightDown(); }
}
if (!RaycastHitted)
{
NoRaycastBehaviour();
}
}
User_RaycastHittedSource = RaycastHitted;
_CustomRaycastOnBlendOut();
}
if (_UsingCustomRaycast)
{
RaycastHitted = true;
legGroundHit = _CustomRaycastHit;
groundHitRootSpacePos = ToRootLocalSpace(legGroundHit.point);
_UsingEmptyRaycast = true;
_noRaycast_skipFeetCalcs = true;
_Raycasting_CalculateBasis();
ankleAlignedOnGroundHitRotation = GetAlignedOnGroundHitRot(_SourceIKRot, legGroundHit.normal);
}
if (_noRaycast_skipFeetCalcs)
{
return;
}
// Foot rotation on raycast hit
if (RaycastHitted)
{
lastGroundHitWithTarget = legGroundHit;
ankleAlignedOnGroundHitRotation = GetAlignedOnGroundHitRot(_SourceIKRot, legGroundHit.normal);
}
else
ankleAlignedOnGroundHitRotation = _SourceIKRot;
}
void NoRaycastBehaviour()
{
if (Owner.NoRaycastGroundBehaviour == ENoRaycastBehviour.Detach) return;
if (Owner.NoRaycastGroundBehaviour == ENoRaycastBehviour.ZeroFloorSteps)
{
_noRaycast_skipFeetCalcs = true;
_UsingEmptyRaycast = true;
GenerateZeroFloorRaycastHit();
ankleAlignedOnGroundHitRotation = _SourceIKRot;
}
else if (Owner.NoRaycastGroundBehaviour == ENoRaycastBehviour.KeepAttached)
{
if (IKProcessor.GetStretchValue(_PreviousFinalIKPos) > Owner.NoRaycast_KeepAttachedUntilStretch)
{
lastGroundHitWithTarget = new RaycastHit();
return;
}
if (lastGroundHitWithTarget.transform)
{
_noRaycast_skipFeetCalcs = true;
legGroundHit = lastGroundHitWithTarget;
RaycastHitted = true;
_Raycasting_CalculateBasis();
Vector3 fakehitRootSpace = ToRootLocalSpace(lastGroundHitWithTarget.point);
//ankleAlignedOnGroundHitRootLocal = fakehitRootSpace;
fakehitRootSpace.y = 0f;
groundHitRootSpacePos = fakehitRootSpace;
}
}
}
void GenerateZeroFloorRaycastHit()
{
RaycastHit hit = new RaycastHit();
Vector3 fakehitRootSpace = ToRootLocalSpace(_SourceIKPos);
ankleAlignedOnGroundHitRootLocal = fakehitRootSpace;
fakehitRootSpace.y = 0f;
Vector3 fakeHit = RootSpaceToWorld(fakehitRootSpace);
hit.point = fakeHit;
hit.normal = Owner.Up;
legGroundHit = hit;
RaycastHitted = true;
groundHitRootSpacePos = fakehitRootSpace;
}
void CustomRaycastValidate()
{
_Raycasting_CalculateBasis();
raycastSlopeAngle = 0f;
A_WasAligning = true;
A_WasAligningFrameBack = true;
A_LastTargetAlignRot = _SourceIKRot;
A_LastApppliedAlignRot = _SourceIKRot;
A_PreviousRelevantAnklePos = _SourceIKPos;
A_LastAlignHeightDiff = C_Local_MidFootPosVsGroundHit.y;
A_LastAlignHeightCompareValue = ScaleRef * 0.002f + ParentHub._Hips_StepHeightAdjustOffset;
}
Vector3 Raycast_RefreshOrigin()
{
Vector3 origin = ParentHub.LastRootLocalPos;
origin = RootSpaceToWorld(origin);
lastRaycastingOrigin = origin;
return origin;
}
void Raycast_OriginToFoot()
{
Vector3 origin = Raycast_RefreshOrigin();
Vector3 castEndPoint = RootSpaceToWorld(AnkleH.LastKeyframeRootPos) - Owner.Up * C_AnkleToHeelWorldHeight;
Vector3 direction = castEndPoint - origin;
float toGround = direction.magnitude * 1.05f;
direction.Normalize();
Vector3 rayGroundPos = origin + direction * toGround;
if (Physics.Linecast(origin, rayGroundPos, out legGroundHit, Owner.GroundMask, Owner.RaycastHitTrigger))
{
CaptureRaycastHitForLeg();
}
else
{
ankleAlignedOnGroundHitWorldPos = AnkleIK.srcPosition;
}
}
void Raycast_AlongBones()
{
Raycast_RefreshOrigin();
if (DoRaycasting(_AnimatorStartBonePos, _AnimatorMidBonePos))
{
CaptureRaycastHitForLeg();
}
else
{
Vector3 endPos = _AnimatorEndBonePos + (_AnimatorEndBonePos - _AnimatorMidBonePos) * 0.1f;
if (DoRaycasting(_AnimatorMidBonePos, endPos))
{
CaptureRaycastHitForLeg();
}
else
ankleAlignedOnGroundHitWorldPos = AnkleIK.srcPosition;
}
}
void Raycast_StraightDown()
{
Vector3 castStartPointLocal = AnkleH.LastKeyframeRootPos;
Vector3 origin = ParentHub.LastRootLocalPos;
float toGround;
if (Owner.RaycastStartHeight == ERaycastStartHeight.FirstBone)
{
origin = BoneStart.position;
toGround = IKProcessor.fullLength;
}
else
{
origin.x = castStartPointLocal.x;
//origin.x = Ankle.LastKeyframeRootPos.x;
origin.z = castStartPointLocal.z;
toGround = Owner.ScaleReference * (Owner.RaycastStartHeightMul / Root.lossyScale.y);
if (Owner.RaycastStartHeight == ERaycastStartHeight.StaticScaleReference)
origin.y = toGround;
origin = RootSpaceToWorld(origin);
}
lastRaycastingOrigin = origin;
Vector3 direction = -Owner.Up;
Vector3 rayGroundPos = origin + direction * toGround;
float extraRaycastingDistance = ScaleRef * Owner.CastDistance;
Vector3 rayEnd = rayGroundPos + direction * extraRaycastingDistance;
lastRaycastingEndPoint = rayEnd;
//UnityEngine.Debug.DrawLine(origin, rayEnd, Color.green, 0.11f);
if (DoRaycasting(origin, rayEnd))
{
CaptureRaycastHitForLeg();
}
else
{
ankleAlignedOnGroundHitWorldPos = AnkleIK.srcPosition;
}
}
internal bool DoRaycasting(Vector3 origin, Vector3 rayEnd)
{
bool hitted;
if (Owner.RaycastShape == ERaycastMode.Linecast)
{
hitted = Physics.Linecast(origin, rayEnd, out legGroundHit, Owner.GroundMask, Owner.RaycastHitTrigger);
}
else
{
float radius = Owner.ScaleReference * 0.065f * Owner.SpherecastResize;
Vector3 castDir = rayEnd - origin;
float castDistance = castDir.magnitude - radius;
hitted = Physics.SphereCast(origin, radius, castDir.normalized, out legGroundHit, castDistance - radius, Owner.GroundMask, Owner.RaycastHitTrigger);
if (hitted)
{
if (Owner.SpherecastRealign > 0f)
{
Vector3 ppos = ToRootLocalSpace(legGroundHit.point);
ppos.x = Mathf.LerpUnclamped(ppos.x, AnkleH.LastKeyframeRootPos.x, Owner.SpherecastRealign);
ppos.z = Mathf.LerpUnclamped(ppos.z, AnkleH.LastKeyframeRootPos.z, Owner.SpherecastRealign);
legGroundHit.point = RootSpaceToWorld(ppos);
}
}
}
return hitted;
}
void CaptureRaycastHitForLeg()
{
RaycastHitted = true;
groundHitRootSpacePos = ToRootLocalSpace(legGroundHit.point);
#region Prevent sudden angle changes - smooth on big angle changes
raycastSlopeAngle = Vector3.Angle(Owner.Up, legGroundHit.normal);
if (raycastSlopeAngle > 45f)
{
var hit = legGroundHit;
hit.normal = Vector3.Slerp(legGroundHit.normal, Owner.Up, Mathf.InverseLerp(45f, 90f, raycastSlopeAngle) * 0.5f);
legGroundHit = hit;
}
#endregion
_Raycasting_CalculateBasis();
}
void _Raycasting_CalculateBasis()
{
previousAnkleAlignedOnGroundHitWorldPos = ankleAlignedOnGroundHitWorldPos;
ankleAlignedOnGroundHitWorldPos = GetAlignedOnGroundHitPos(groundHitRootSpacePos, legGroundHit.point, legGroundHit.normal);
ankleAlignedOnGroundHitRootLocal = ToRootLocalSpace(ankleAlignedOnGroundHitWorldPos);
}
Vector3 GetAlignedOnGroundHitPos(Vector3 rootSpaceHitPos, Vector3 worldHit, Vector3 normal)
{
Vector3 andjustedLocalAnklePos = rootSpaceHitPos;
andjustedLocalAnklePos.y = ToRootLocalSpace(worldHit + normal * C_AnkleToHeelWorldHeight).y;
return RootSpaceToWorld(andjustedLocalAnklePos);
}
Quaternion GetAlignedOnGroundHitRot(Quaternion sourceRotation, Vector3 normal)
{
Quaternion alignedRot = Quaternion.FromToRotation(sourceRotation * AnkleIK.up, normal);
alignedRot *= sourceRotation;
return alignedRot;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 76c6873923e5b53478537f2b2bd4b44d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,20 @@
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
public partial class Leg
{
public Vector3 InitialPosInRootSpace { get; private set; }
void Stability_Init()
{
Vector3 initRootSpacePos = ToRootLocalSpace(BoneEnd.position);
//initRootSpacePos.y += C_AnkleToHeelRootSpace.y;
InitialPosInRootSpace = initRootSpacePos;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f125e04b9d5ecc04ca8b6f50d89d1f6f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,468 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
public List<Leg> Legs = new List<Leg>();
public enum ELegSide { Undefined, Left, Right }
[System.Serializable]
public partial class Leg
{
public LegsAnimator Owner;
public int PlaymodeIndex { get; private set; }
[FPD_Suffix(0f, 1f)]
public float LegBlendWeight = 1f;
#region Blend variables
/// <summary> Multiplier for modules </summary>
internal float InternalModuleBlendWeight = 1f;
/// <summary> (precomputed on preCalibrate) LegBlendWeight * InternalModuleBlendWeight </summary>
public float BlendWeight { get; private set; }
/// <summary> (precomputed on preCalibrate) Inidividual leg blend + legs animator blend </summary>
float finalBoneBlend = 1f;
#endregion
#region Leg Individual Settings
[Tooltip("Make idle glue animation motion faster for this single leg")]
public float LegMoveSpeedMultiplier = 1f;
public float LegRaiseMultiplier = 1f;
[Space(3)]
public float GlueThresholdMultiplier = 1f;
public Vector2 GluePointOffset = Vector2.zero;
[Space(3)]
[Range(0f, 1f)] public float LegStretchMultiplier = 1f;
[Tooltip("Motion preset for the leg to be animated with different character than the other legs ('Idle Glue Motion' settings)")]
public LegMotionSettingsPreset CustomLegAnimating;
[Range(-40f, 40f)]
public float FootPitchOffset = 0f;
#endregion
public Transform BoneStart;
public Transform BoneMid;
public Transform BoneEnd;
public ELegSide Side = ELegSide.Undefined;
public int OppositeLegIndex = -1;
public ERaycastPrecision RaycastPrecision = ERaycastPrecision.Linecast;
[Tooltip("(Experimental) If you want to animate in additional feet bone which in some cases can add nice animation feeling")]
public bool UseFeet = false;
public Transform BoneFeet;
[Range(0f, 1f)]
[Tooltip("Defining how quick heel should get up if leg gets stretched (change max stretching param under IK tab to be lower value that 1.1)")]
public float FeetSensitivity = 0.5f;
/// <summary> To avoid using for() loops but while() for better performance (Only Playmode) </summary>
[field:NonSerialized] public Leg NextLeg { get; private set; }
public void SetNextLeg(Leg next) { NextLeg = next; }
[field: NonSerialized] public HipsReference ParentHub { get; private set; }
// Unity throws serialization depth limit warning when it's using {get; private set;} ¯\_(ツ)_/¯
// Finally! [field:NonSerialized] seems to fix it!!!
//public HipsReference ParentHub { get; private set; }
bool hasOppositeleg = false;
private LegStepAnimatingParameters targetLegAnimating;
public LegStepAnimatingParameters LegAnimatingSettings { get { return targetLegAnimating; } }
public float LegStretchLimit { get; private set; } = 1f;
public void InitLegBasics(LegsAnimator creator, int index, Leg nextLeg)
{
if (creator != null) Owner = creator;
PlaymodeIndex = index;
SetNextLeg(nextLeg);
LegStretchLimit = 1f;
BlendWeight = 1f;
InternalModuleBlendWeight = 1f;
EnsureAxesNormalization();
#region Initialize Bone Helpers
_h_boneStart = new LegHelper(this, BoneStart);
_h_boneMid = new LegHelper(this, BoneMid);
_h_boneEnd = new LegHelper(this, BoneEnd);
_h_boneStart.Child = _h_boneMid;
_h_boneMid.Child = _h_boneEnd;
#endregion
Gluing_Init();
Reset();
Controll_Init();
Raycasting_Init();
Stability_Init();
AlignStep_Init();
RefreshHasOppositeLeg();
targetLegAnimating = CustomLegAnimating ? CustomLegAnimating.Settings : creator.LegAnimatingSettings;
ankleAlignedOnGroundHitWorldPos = _FinalIKPos; //
}
public void RefreshHasOppositeLeg()
{
hasOppositeleg = false;
if (GetOppositeLeg() != null) hasOppositeleg = true;
}
public void Leg_UpdateParams()
{
targetLegAnimating = CustomLegAnimating ? CustomLegAnimating.Settings : Owner.LegAnimatingSettings;
IK_UpdateParams();
}
internal void AssignParentHub(HipsReference hipsReference)
{
ParentHub = hipsReference;
}
internal void Reset()
{
_SourceIKPos = BoneEnd.position;
_SourceIKRot = BoneEnd.rotation;
_FinalIKPos = _SourceIKPos;
_FinalIKRot = _SourceIKRot;
_PreviousFinalIKPos = _FinalIKPos;
_PreviousFinalIKRot = _FinalIKRot;
legGroundHit = new RaycastHit();
legGroundHit.point = _FinalIKPos;
legGroundHit.normal = Owner.Up;
_PreviousFinalIKPosForStability = _SourceIKPos;
ankleAlignedOnGroundHitRotation = _SourceIKRot;
A_LastApppliedAlignRot = _SourceIKRot;
A_LastTargetAlignRot = _SourceIKRot;
groundHitRootSpacePos = ToRootLocalSpace(_SourceIKPos);
_SourceIKPosUnchangedY = groundHitRootSpacePos;
RaycastHit ghostHit = new RaycastHit();
ghostHit.point = _FinalIKPos;
ghostHit.normal = Owner.Up;
legGroundHit = ghostHit;
Glue_Reset(true);
//Gluing_Init();
//Raycasting_Init();
//AlignStep_Init();
//PreCalibrate();
}
#region Update Executing
public void PreCalibrate()
{
#region Editor ifdef - reset label
#if UNITY_EDITOR
_Editor_Label = "";
#endif
#endregion
BlendWeight = BlendWeight * InternalModuleBlendWeight;
finalBoneBlend = BlendWeight * Owner._MainBlend;
if (finalBoneBlend < 0.0001f)
{
if (_G_WasDisabled == false)
{
G_Attached = false;
G_AttachementHandler.Reset(false);
G_Attachement = new GlueAttachement();
_G_WasDisabled = true;
legGroundHit = new RaycastHit();
RaycastHitted = false;
}
return;
}
if( Owner.Calibrate == ECalibrateMode.Calibrate )
{
IKProcessor.PreCalibrate();
}
else if( Owner.Calibrate == ECalibrateMode.FixedCalibrate )
{
if( !_wasFixedCalibrateAnimationCaptured ) IKProcessor.PreCalibrate();
else
{
BoneStart.localRotation = _AnimatorStartBoneLocRot;
BoneMid.localRotation = _AnimatorMidBoneLocRot;
BoneEnd.localRotation = _AnimatorEndBoneLocRot;
}
}
//G_CustomForceNOTDetach = false;
//G_CustomForceDetach = false;
//G_CustomForceNOTAttach = false;
}
public void CheckAnimatorPose()
{
_AnimatorStartBonePos = BoneStart.position;
_AnimatorMidBonePos = BoneMid.position;
_AnimatorEndBonePos = BoneEnd.position;
if( Owner.Calibrate == ECalibrateMode.FixedCalibrate )
{
_wasFixedCalibrateAnimationCaptured = true;
_AnimatorStartBoneLocRot = BoneStart.localRotation;
_AnimatorMidBoneLocRot = BoneMid.localRotation;
_AnimatorEndBoneLocRot = BoneEnd.localRotation;
}
}
public void BeginLateUpdate()
{
if (finalBoneBlend < 0.0001f) return;
//G_StepHeatmapForceDetach = false;
//G_StepHeatmapForceNOTDetach = false;
G_CustomForceAttach = false;
G_CustomForceNOTDetach = false;
G_CustomForceDetach = false;
G_CustomForceNOTAttach = false;
IK_PreUpdate();
LegStretchLimit = Owner.LimitLegStretch * LegStretchMultiplier;
}
public void PreLateUpdate()
{
if (customOverwritingIKPos) return;
if (_G_WasDisabled && finalBoneBlend < 0.0001f) return;
Owner.Modules_LegBeforeRaycastingUpdate(this);
Raycasting_PreLateUpdate();
Controll_Calibrate();
}
public void LateUpdate()
{
if (finalBoneBlend < 0.0001f) return;
if (customOverwritingIKPos) return;
Owner.Modules_Leg_LateUpdate(this);
AlignStep_CheckAlignStatePre();
AlignStep_ValidateFootRotation();
//if (Owner._usingStepHeatmap) Owner._stepHeatmap.UpdatePreGlue(PlaymodeIndex);
Gluing_Update();
Gluing_ApplyCoords();
AlignStep_OnGroundAlign();
AlignStep_SmoothSuddenSteps();
AlignStep_LegElevation();
AlignStep_Complete();
Control_StepEventCalcs();
ExtraProcessingApply();
}
public void LateUpdate_Apply()
{
IK_PostUpdate();
}
public void FixedUpdate()
{
}
#endregion
#region Calculation Helpers
[Tooltip("Apply IK hint inversion, in case leg is bending in wrong direction.")]
public bool InverseHint = false;
/// <summary> Bone End's Local Space</summary>
public Vector3 AnkleToHeel = Vector3.zero;
/// <summary> Bone End's Local Space</summary>
public Vector3 AnkleToFeetEnd = Vector3.zero;
public Vector3 AnkleRight = Vector3.right;
public Vector3 AnkleUp = Vector3.up;
public Vector3 AnkleForward = Vector3.forward;
[Range(0f, 1.001f)]
public float FootMiddlePosition = 0.5f;
[Space(5)]
[FPD_Suffix(-45f, 45f, FPD_SuffixAttribute.SuffixMode.FromMinToMax, "°")] public float AnkleYawCorrection = 0f;
#endregion
#region Utilities
/// <summary> Current frame leg limb world length in units </summary>
public float LegLimbLength()
{
if (BoneStart == null || BoneMid == null || BoneEnd == null) return Owner.HipsToGroundDistance();
float len = 0f;
len += Vector3.Distance(BoneStart.position, BoneMid.position);
len += Vector3.Distance(BoneEnd.position, BoneMid.position);
return len;
}
public bool HasAllBonesSet()
{
if (BoneStart == null) return false;
if (BoneMid == null) return false;
if (BoneEnd == null) return false;
return true;
}
public float R(float toRound, int digits = 2)
{
return (float)System.Math.Round(toRound, digits);
}
#endregion
bool _StepSent = true;
float _StepSentAt = -100f;
float _RaiseSentAt = -100f;
bool _OppositeLegStepped = true;
float _ToConfirmStepEvent = 0f;
void SendStepEvent(float factor = 1f, EStepType type = EStepType.IdleGluing)
{
if (_StepSent) return;
//if (Time.unscaledTime - _StepSentAt < 0.05f) return;
if (Owner.GroundedTime < 0.1f) type = EStepType.OnLanding;
else if (Owner.IsMoving == false) if (Owner.StoppedTime < 0.15f) type = EStepType.OnStopping;
Owner.Events_OnStep(this, factor, type);
_StepSent = true;
_StepSentAt = Time.unscaledTime;
if (hasOppositeleg)
{
_OppositeLegStepped = true;
GetOppositeLeg()._OppositeLegStepped = !Owner.IsMoving;
}
}
void SendRaiseEvent( float distanceToNew = 1f)
{
if (Time.unscaledTime - _RaiseSentAt < 0.05f) return;
_RaiseSentAt = Time.unscaledTime;
EStepType type = EStepType.IdleGluing;
if( Owner.IsMoving == false ) if( Owner.StoppedTime < 0.15f ) type = EStepType.OnStopping;
Owner.Events_OnRaise( this, distanceToNew, type );
}
void StepEventRestore()
{
if (!Owner.UseEvents) return;
if (!_StepSent) return;
if (Time.unscaledTime - _StepSentAt < 0.1f) return;
if (Owner.GroundedTime < 0.1f) return;
//if (!Owner.StepEventOnLanding) if (Owner.IsGroundedBlend < 0.9f) return;
if (Owner.UseGluing)
{
if (G_AttachementHandler.glueAnimationBlend > 0.5f && G_GlueInternalTransition > 0.25f) return;
if (Owner._glueModeExecuted == EGlueMode.Idle)
{
if (G_DuringAttaching == false) return;
if (Owner.GlueMode == EGlueMode.Automatic)
{
if (Owner.IsMoving) return;
if (Owner.Helper_WasMoving) return;
}
if (Owner.SendOnStopping == false && Owner.StoppedTime < 0.155f) return;
if (G_AttachementHandler.lasGlueModeOnAttaching != EGlueMode.Idle) return;
if (G_AttachementHandler.legMoveDistanceFactor < 0.05f) return;
}
else
{
if (Owner.GlueMode == EGlueMode.Automatic) if (!Owner.IsMoving) return;
if (Owner.MovingTime < 0.06f) return;
if (A_PreWasAligning) return;
if (A_AligningHelperBlend > .5f - Owner.EventExecuteSooner) return;
if (hasOppositeleg)
{
if (GetOppositeLeg()._OppositeLegStepped == false) return;
}
float heightFactor = -ScaleRef * 0.2f + FloorLevel * Owner.BaseTransform.lossyScale.y + C_AnkleToHeelWorldHeight * 0.75f + A_LastAlignHeightCompareValue * (3f + Owner.EventExecuteSooner);
if (A_LastAlignHeightDiff < heightFactor)
{
return;
}
}
}
else // Not gluing - aligning based event triggering
{
if (A_PreWasAligning) return;
if (A_AligningHelperBlend > 0.05f) { return; }
if (Owner.IsMovingBlend < 0.05f) { _StepSent = true; return; }
if (Owner.Helper_WasMoving == false) { _StepSent = true; return; }
if (Owner.IsMoving == false) { _StepSent = true; return; }
}
_StepSent = false;
}
}
public void Legs_AddNewLeg()
{
Leg leg = new Leg();
leg.Owner = this;
Legs.Add(leg);
}
public void Legs_RefreshLegsOwner()
{
for (int i = 0; i < Legs.Count; i++)
{
Legs[i].Owner = this;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ddc334030cb970247840b2908504cce8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 393dbd88a54c9ee4484c6d8f99fb197b, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9fa157741285d7f44861b154a647e4ca
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,105 @@
using System.Collections.Generic;
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
public partial class Leg
{
public List<Leg> Legs { get { return Owner.Legs; } }
public void DefineLegSide(LegsAnimator get, Leg knownOppositeLeg = null)
{
if (knownOppositeLeg != null)
{
if (knownOppositeLeg.Side != ELegSide.Undefined)
{
if (knownOppositeLeg.Side == ELegSide.Left) Side = ELegSide.Right;
else Side = ELegSide.Left;
return;
}
}
if (BoneStart != null)
{
if (get.Util_OnLeftSide(BoneStart)) Side = ELegSide.Left; else Side = ELegSide.Right;
}
}
public void AssignOppositeLegIndex(int oppositeIndex)
{
if (oppositeIndex == OppositeLegIndex) return;
if (Owner)
if (Owner.Legs.ContainsIndex(oppositeIndex))
{
Owner.Legs[oppositeIndex].OppositeLegIndex = Owner.Leg_GetIndex(this);
}
OppositeLegIndex = oppositeIndex;
}
public Leg GetOppositeLegReference(LegsAnimator legs)
{
if (OppositeLegIndex < 0) return null;
if (legs.Legs.ContainsIndex(OppositeLegIndex) == false) return null;
return legs.Legs[OppositeLegIndex];
}
public void RefreshLegAnkleToHeelAndFeetAndAxes(Transform baseT)
{
RefreshLegAnkleToHeelAndFeet(baseT);
RefreshLegAnkleAxes(baseT);
}
public void RefreshLegAnkleToHeelAndFeet(Transform baseT)
{
if (BoneEnd == null) return;
Vector3 wGroundPos = BoneEnd.position;
wGroundPos.y = baseT.position.y;
AnkleToHeel = BoneEnd.InverseTransformPoint(wGroundPos);
AnkleToFeetEnd = BoneEnd.InverseTransformPoint(wGroundPos + baseT.forward * ScaleRef * 0.15f);
}
public void RefreshLegAnkleAxes(Transform baseT)
{
if (!BoneEnd) return;
Quaternion baseAdjustRotation = baseT.rotation * Quaternion.Euler(0f, AnkleYawCorrection, 0f);
AnkleForward = BoneEnd.InverseTransformDirection(baseAdjustRotation * Vector3.forward);
AnkleUp = BoneEnd.InverseTransformDirection(baseAdjustRotation * Vector3.up);
AnkleRight = BoneEnd.InverseTransformDirection(baseAdjustRotation * Vector3.right);
}
void EnsureAxesNormalization()
{
AnkleRight.Normalize();
AnkleUp.Normalize();
AnkleForward.Normalize();
}
}
public int Leg_GetIndex(Leg leg)
{
for (int i = 0; i < Legs.Count; i++)
{
if (leg == Legs[i]) return i;
}
return -1;
}
public Leg Leg_GetLeg(int index)
{
if (index < 0) return null;
if (index >= Legs.Count) return null;
return Legs[index];
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b10b7b8a1ff093c4895b924c8b4569c5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,156 @@
using System;
using System.Globalization;
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
public LegStepAnimatingParameters BaseLegAnimating = new LegStepAnimatingParameters();
/// <summary> For future leg motion presets </summary>
public LegStepAnimatingParameters LegAnimatingSettings { get { return BaseLegAnimating; } }
/// <summary>
/// Step Animating is used by leg movement during gluing process
/// </summary>
[System.Serializable]
public class LegStepAnimatingParameters
{
[Tooltip("Average duration of the automatic leg animation")]
[Range(0.1f, 1f)] public float StepMoveDuration = .375f;
[Tooltip("Curve of ik point going towards desired position (just XZ movement, to Y - no leg rise curve)")]
[FPD_FixedCurveWindow(0f, 0f, 1f, 1.25f, .4f, .5f, 1f)]
public AnimationCurve MoveToGoalCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f);
[Tooltip("Making foot motion move towards target not in full linear straight towards target motion but adding a bit curve back (positive value) or forward (negative values) making movement a bit more natural")]
[FPD_FixedCurveWindow(0f, -1f, 1f, 1f, .4f, .6f, .9f)] public AnimationCurve SpherizeTrack = AnimationCurve.EaseInOut(0f, 0f, 1f, 0f);
[Range(0f,2f)] public float SpherizePower = 0.3f;
[Tooltip("Minimum leg raise height. If distance of target step animation is small, then foot raise is smaller - down to this minimum raise value.")]
[Range(0f, 1f)] public float MinFootRaise = .1f;
[Tooltip("Maximum leg raise height. If distance of target step animation is very big, then foot raise is bigger - up to this maximum raise value.")]
[Range(0f, 1f)] public float MaxFootRaise = .4f;
[Tooltip("Raise height step animation curve evaluated on step animation duration.")]
[FPD_FixedCurveWindow(0f, 0f, 1f, 1f, .5f, 1f, .5f)]
public AnimationCurve RaiseYAxisCurve;
[Space(3)]
[Tooltip("Allowing to speed up leg adjusting animation when leg is getting stretched, when opposite leg is requesting adjustement or when main character is rotating in place quickly")]
[Range(0f, 1f)] public float AllowSpeedups = 0.4f;
[Tooltip("You can allow to use opposite leg before idle glue leg adjustement finishes")]
[Range(0.1f,1f)] public float AllowDetachBefore = 1f;
// Expert Curves
[Tooltip("Extra hips push power animation curve evaluated on step animation duration.")]
[FPD_FixedCurveWindow(0f, 0f, 1f, 1f, 1f, .6f, .6f)]
public AnimationCurve PushHipsOnMoveCurve;
[Tooltip("Extra foot ankle rotation animation curve evaluated on step animation duration.")]
[FPD_FixedCurveWindow(0f, -1f, 1f, 1f)]
public AnimationCurve FootRotationCurve;
[Tooltip( "When steps are too small, then leg will not apply hips motion to the system" )]
[Range( 0f, 0.2f )] public float DoStepAnimationOnDistanceFactor = 0.055f;
#region Curves Definition
public void RefreshDefaultCurves()
{
//LogCurve("FootRotationCurve", FootRotationCurve); return;
Curves_RefreshMoveToGoalCurve();
Curves_RefreshRaiseYAxisCurve();
Curves_RefreshSpherizeTrack();
Curves_RefreshFootRotationCurve();
Curves_RefreshPushHipsOnMoveCurve();
}
public void Curves_RefreshRaiseYAxisCurve()
{
RaiseYAxisCurve = new AnimationCurve();
RaiseYAxisCurve.AddKey(new Keyframe(0f, 0f, 0.8504464f, 0.8504464f, 0f, 0.6517575f));
RaiseYAxisCurve.AddKey(new Keyframe(0.2731183f, 0.45f, 0.9770691f, 0.9770691f, 0.3333333f, 0.3387407f));
RaiseYAxisCurve.AddKey(new Keyframe(0.505118f, 0.5f, -0.2710344f, -0.2710344f, 0.3333333f, 0.3333333f));
RaiseYAxisCurve.AddKey(new Keyframe(0.9110107f, 0f, -0.1500788f, -0.1500788f, 0.5409704f, 0f));
}
public void Curves_RefreshRaiseYAxisCurveSpiderPreset()
{
RaiseYAxisCurve = new AnimationCurve();
RaiseYAxisCurve.AddKey(new Keyframe(0f, 0f, 0.8504464f, 0.8504464f, 0f, 0.6517575f));
RaiseYAxisCurve.AddKey(new Keyframe(0.2731183f, 0.45f, 0.9770691f, 0.9770691f, 0.3333333f, 0.3387407f));
RaiseYAxisCurve.AddKey(new Keyframe(0.5943514f, 0.7946472f, -0.2710344f, -0.2710344f, 0.3333333f, 0.3333333f));
RaiseYAxisCurve.AddKey(new Keyframe(1f, 0f, -0.1500788f, -0.1500788f, 0.5409704f, 0f));
}
public void Curves_RefreshMoveToGoalCurve()
{
MoveToGoalCurve = new AnimationCurve();
MoveToGoalCurve.AddKey(new Keyframe(0, 0, 0, 0, 0, 0.1842105f));
MoveToGoalCurve.AddKey(new Keyframe(0.4885197f, 0.8972011f, 1.38764f, 1.38764f, 0.3333333f, 0.3333333f));
MoveToGoalCurve.AddKey(new Keyframe(1, 1, 0, 0, 0, 0));
}
public void Curves_RefreshFootRotationCurve()
{
FootRotationCurve = new AnimationCurve();
FootRotationCurve.AddKey(new Keyframe(0f, 0f, 0.5764588f, 0.5764588f, 0f, 0.4956417f));
FootRotationCurve.AddKey(new Keyframe(0.4378169f, 0.2035736f, -0.2411275f, -0.2411275f, 0.3333333f, 0.4033037f));
FootRotationCurve.AddKey(new Keyframe(0.7841034f, -0.1339308f, 0.3331003f, 0.3331003f, 0.3333333f, 0.3333333f));
FootRotationCurve.AddKey(new Keyframe(1f, 0f, 0.3498169f, 0.3498169f, 0.5534658f, 0f));
}
public void Curves_RefreshPushHipsOnMoveCurve()
{
PushHipsOnMoveCurve = new AnimationCurve();
PushHipsOnMoveCurve.AddKey(new Keyframe(0f, 0f, 5.630541f, 5.630541f, 0f, 0.198735f));
PushHipsOnMoveCurve.AddKey(new Keyframe(0.383f, 0.3733972f, -0.1664574f, -0.1664574f, 0.333f, 0.2940554f));
PushHipsOnMoveCurve.AddKey(new Keyframe(0.7075226f, 0.1460427f, -1.565806f, -1.565806f, 0.3605607f, 0.3446763f));
PushHipsOnMoveCurve.AddKey(new Keyframe(1f, 0f, 0f, 0f, 0.09374858f, 0f));
}
public void Curves_RefreshPushHipsOnMoveCurveSpiderPreset()
{
PushHipsOnMoveCurve = new AnimationCurve();
PushHipsOnMoveCurve.AddKey(new Keyframe(0f, 0f, 5.630541f, 5.630541f, 0f, 0.198735f));
PushHipsOnMoveCurve.AddKey(new Keyframe(0.320017f, 0.654645f, -0.1664574f, -0.1664574f, 0.333f, 0.2940554f));
PushHipsOnMoveCurve.AddKey(new Keyframe(0.6681702f, 0.2174691f, -1.565806f, -1.565806f, 0.3605607f, 0.3446763f));
PushHipsOnMoveCurve.AddKey(new Keyframe(1f, 0f, 0f, 0f, 0.09374858f, 0f));
}
public void Curves_RefreshSpherizeTrack()
{
SpherizeTrack = new AnimationCurve();
SpherizeTrack.AddKey(new Keyframe(0f, 0f, 0.6958197f, 0.6958197f, 0f, 0.460011f));
SpherizeTrack.AddKey(new Keyframe(0.4f, 0.3f, -0.04204308f, -0.04204308f, 0.333f, 0.3410656f));
SpherizeTrack.AddKey(new Keyframe(0.85f, 0f, -0.2721428f, -0.2721428f, 0.3953607f, 0f));
}
public void LogCurve(string name, AnimationCurve c)
{
string log = "";
IFormatProvider prov = CultureInfo.InvariantCulture;
for (int i = 0; i < c.keys.Length; i++)
{
var key = c.keys[i];
log += "\n"+ name+".AddKey(new Keyframe(" + key.time.ToString(prov) + "f, " + key.value.ToString(prov) + "f, " + key.inTangent.ToString(prov) + "f, " + key.outTangent.ToString(prov) + "f, " + key.inWeight.ToString(prov) + "f, " + key.outWeight.ToString(prov) + "f));";
}
Debug.Log(log);
}
#endregion
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9b3b003f7cccdd04e8d580c9c9a9895a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,70 @@
using System;
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
[Tooltip("Using algorithm responsive for attaching feet to the ground when detected grounded foot in the played animation.")]
public bool UseGluing = true;
[Tooltip("You can smoothly change Glue Blend down to transition into sliding if your character is walking on ice or sliding on steep ground.")]
[FPD_Suffix(0f,1f)] public float MainGlueBlend = 1f;
public float GlueBlend
{
get { return UseGluing ? (_MainBlend * MainGlueBlend) : 0f; }
}
[Space(3)]
[Tooltip("If distance from the last attach point exceeds this distance (check scene gizmos) the leg will be detached.")]
[Range(0f, 1f)] public float GlueRangeThreshold = 0.375f;
[Tooltip("How quickly leg attachement transition should be proceeded.")]
[Range(0f, 1f)] public float GlueFadeInSpeed = 0.85f;
[Tooltip("If foot animation in original played clip is not reaching floor soon enough, increase it to attach for position slightly below current foot positioning.")]
[Range(0f, 1f)] public float AllowGlueBelowFoot = 0.2f;
[Tooltip("How quickly leg detachement transition should be proceeded.")]
[Range(0f, 1f)] public float GlueFadeOutSpeed = 0.5f;
[Space(5)]
[Tooltip("If leg rotation exceeds this angle during being attach, the leg will be detached.")]
[FPD_Suffix(0f, 90f, FPD_SuffixAttribute.SuffixMode.FromMinToMaxRounded, "°")] public float UnglueOn = 30f;
[Space(1)]
[Tooltip("When leg glue target position is stretching leg too much it will shift leg target towards source animation leg position.")]
[Range(0f, 1f)] public float AllowGlueDrag = 0.7f;
[NonSerialized] public float DontGlueAttachIfTooNearOppositeLeg = 0f;
//[Space(1)]
//[Tooltip("When main transform is rotating then glue transition will speed up to catch up steps better")]
//[Range(0f, 1f)] public float SpeedupOnRotation = 0.0f;
//[Space(6)]
//public bool AllowReRaycastDuringTransition = false;
//[Space(3)]
//[Range(0.9f, 1.2f)] public float UnglueIfStretch = 1.05f;
public enum EGlueMode
{
[Tooltip("Idle Mode is applying leg animation with extra motion and is checking some extra conditions like opposite leg grounded state etc.")]
Idle,
[Tooltip("Moving Mode is dedicated to be applied during playing animations with dynamic legs, it's checking less conditions than Idle Mode and is snapping glue points in a more straight forward slide animation.")]
Moving,
[Tooltip("Automatic mode is syncing with IsMoving/IsIdling LegsAnimator flags.")]
Automatic
}
[Tooltip("Enter on the value field on the right to see tooltip.")]
public EGlueMode GlueMode = EGlueMode.Automatic;
/// <summary> Change only when overriding automatic value! </summary>
public EGlueMode _glueModeExecuted { get; set; }
[Tooltip("Making Gluing animations only local space, which can be helpful when character is standing on the moving platform.")]
public bool OnlyLocalAnimation = false;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 83ba814eb76b50347bf42b4f03ba4ece
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,40 @@
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
[Tooltip("Smoothing leg align motion when sudden uneven terrain step occurs")]
[Range(0f, 1f)] public float SmoothSuddenSteps = 0.85f;
[Space(3)]
[Tooltip("Making leg rise a bit over ground when character leg overlaps collision (it's mostly visible on steep slopes)")]
[Range(0f, 2f)] public float LegElevateBlend = 0.7f;
[Range(0f, 1f)] public float LegElevateHeightLimit = 0.6f;
[Space(6)]
[Tooltip("Overall foot rotation blend on the slope step align.")]
[FPD_Suffix(0f,1f)] public float FootRotationBlend = 1f;
//[Tooltip("Allowing to align foot on ground hit more below foot rather than only overlapping ground hit height")]
//[Range(0f,1f)] public float FootDeeperRange = 0f;
//[FPD_Suffix(0f, 90f, FPD_SuffixAttribute.SuffixMode.FromMinToMaxRounded, "°")] public float FootAngleLimit = 0.75f;
//[Space(2)]
//[Range(0f, 1f)] public float FootRollBlend = 0.5f;
[Space(4)]
[Tooltip("How quickly foot should align it's rotation to the slopes")]
[Range(0f, 1f)] public float FootAlignRapidity = 0.75f;
[Tooltip("If it's human leg limb with foot, then turn it on for the foot bone animation and alignments. But if it's something like spider leg, then disable it")]
public bool AnimateFeet = true;
[Tooltip("If feet rotation is above this value, feet rotation will be limited to avoid weird foot rotation pose")]
[FPD_Suffix(0f, 90f, FPD_SuffixAttribute.SuffixMode.FromMinToMax, "°")] public float LimitFeetYaw = 30f;
[Tooltip("Local space ANKLE-step height detection level. It's detail parameter to adjust feet aligning sooner/later when foot is near to ground.")]
[Range(-0.05f, 0.15f)] public float AnimationFloorLevel = 0.001f;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 68c9a24e70fdb154fa7b8dd5547f2089
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9a74b8f5b20ea42489e39b1513b4926c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 108661734cf600e4aab82078dfcc5e06
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,344 @@
#if UNITY_EDITOR
using FIMSpace.FEditor;
using UnityEditor;
#endif
using System.Collections.Generic;
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
//[CreateAssetMenu(fileName = "LAM_Change Gluing On Animator State", menuName = "FImpossible Creations/Legs Animator/LAM_FadeLegOnAnimator", order = 1)]
public abstract class LAM_FadeOnAnimatorStatusBase : LegsAnimatorControlModuleBase
{
LegsAnimator.Variable _fadeSpeedV;
LegsAnimator.Variable _layerV;
float enabledMultiplier = 1f;
float sd_eneMul = 0f;
List<int> stateHashes;
List<int> tagHashes;
enum ELayerSelectMode { ByIndex, Auto }
LegsAnimator.Variable _layerMode;
LegsAnimator.Variable _layerSkip;
List<int> layersToCheck = null;
int lastAutoWeightIndex = 0;
#region Auto Layers Check Init
bool InitLayerCheck(LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
if (helper.Parent.Mecanim == null) return false;
if (_layerMode.GetInt() == 0) return false;
if (_layerMode == null || _layerSkip == null) return false;
layersToCheck = new List<int>();
string[] args = _layerSkip.GetString().Split(',');
for (int i = 0; i < helper.Parent.Mecanim.layerCount; i++) layersToCheck.Add(i);
for (int a = 0; a < args.Length; a++)
{
int parsed;
if (int.TryParse(args[a], out parsed))
{
layersToCheck.Remove(parsed);
}
else
{
int layerNameIndex = -1;
for (int i = 0; i < helper.Parent.Mecanim.layerCount; i++)
{
if (helper.Parent.Mecanim.GetLayerName(i) == args[a])
{
layerNameIndex = i;
break;
}
}
if (layerNameIndex != -1) layersToCheck.Remove(layerNameIndex);
}
}
return true;
}
#endregion
public override void OnInit(LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
if (LA.Mecanim == null)
{
Debug.Log("[Legs Animator] Fade On Animation Module: Not found animator reference in legs animator Extra/Control!");
helper.Enabled = false;
return;
}
_layerV = helper.RequestVariable("Animation Layer", 0);
_fadeSpeedV = helper.RequestVariable("Fade Speed", 0.75f);
var tagsV = helper.RequestVariable("Animation State Tag", "");
var statesV = helper.RequestVariable("Animation State Name", "");
// Prepare target animation hashes for quick checking animator state
string animStates = statesV.GetString();
//animStates = animStates.Replace(" ", "");
var statesSeparated = animStates.Split(',');
#region Prepare mecanim hashes
stateHashes = new List<int>();
if (statesSeparated.Length > 0)
{
for (int i = 0; i < statesSeparated.Length; i++)
{
if (string.IsNullOrWhiteSpace(statesSeparated[i])) continue;
stateHashes.Add(Animator.StringToHash(statesSeparated[i]));
}
}
string tagNames = tagsV.GetString();
//tagNames = tagNames.Replace(" ", "");
var tagsSeparated = tagNames.Split(',');
tagHashes = new List<int>();
if (tagsSeparated.Length > 0)
{
for (int i = 0; i < tagsSeparated.Length; i++)
{
if (string.IsNullOrWhiteSpace(tagsSeparated[i])) continue;
tagHashes.Add(Animator.StringToHash(tagsSeparated[i]));
}
}
if (stateHashes.Count == 0 && tagHashes.Count == 0)
{
helper.Enabled = false;
Debug.Log("[Legs Animator] Fade On Animation Module: No assigned animation state names/tags to control module on!");
return;
}
#endregion
if (_layerV.GetInt() < 0) _layerV.SetValue(0); if (_layerV.GetInt() > LA.Mecanim.layerCount - 1) _layerV.SetValue(LA.Mecanim.layerCount - 1);
// Auto Layers Check
_layerMode = helper.RequestVariable("Mode", 0);
_layerSkip = helper.RequestVariable("Skip", "");
if (_layerMode.GetInt() == 1)
{
if (InitLayerCheck(helper) == false) _layerMode.SetValue(0);
}
}
public override void OnAfterAnimatorCaptureUpdate(LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
Animator anim = LA.Mecanim;
if (anim == null) return;
int layer = _layerV.GetInt();
if (_layerMode.GetInt() == 1)
{
#region Auto Layer Check
float mostWeight = 0f;
int mostWeightI = -1;
for (int i = layersToCheck.Count - 1; i >= 0; i--) // Reverse for to stop checking on 100% weight top layer
{
int idx = layersToCheck[i];
float weight = helper.Parent.Mecanim.GetLayerWeight(idx);
if (weight > 0.95f) // Dont check if layer has
{
mostWeightI = idx;
break;
}
else
{
if (weight > mostWeight)
{
mostWeight = weight;
mostWeightI = idx;
}
}
}
layer = mostWeightI;
lastAutoWeightIndex = layer;
#endregion
}
AnimatorStateInfo animatorInfo = anim.IsInTransition(layer) ? anim.GetNextAnimatorStateInfo(layer) : anim.GetCurrentAnimatorStateInfo(layer);
bool fadeOut = false;
for (int n = 0; n < stateHashes.Count; n++)
{
if (animatorInfo.shortNameHash == stateHashes[n]) { fadeOut = true; break; }
}
if (!fadeOut)
{
for (int t = 0; t < tagHashes.Count; t++)
{
if (animatorInfo.tagHash == tagHashes[t]) { fadeOut = true; break; }
}
}
float fadeDur = 0.3f - _fadeSpeedV.GetFloat() * 0.299f;
if (fadeOut)
{
enabledMultiplier = Mathf.SmoothDamp(enabledMultiplier, -0.001f, ref sd_eneMul, fadeDur * 0.9f, 100000f, LA.DeltaTime);
}
else
{
enabledMultiplier = Mathf.SmoothDamp(enabledMultiplier, 1.01f, ref sd_eneMul, fadeDur, 100000f, LA.DeltaTime);
}
enabledMultiplier = Mathf.Clamp01((float)enabledMultiplier);
OnFadeAction(helper, enabledMultiplier);
}
protected abstract void OnFadeAction(LegsAnimator.LegsAnimatorCustomModuleHelper helper, float fadeValue);
#region Editor Code
#if UNITY_EDITOR
public override void Editor_InspectorGUI(LegsAnimator legsAnimator, LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
if (legsAnimator.Mecanim == null)
{
EditorGUILayout.HelpBox("Unity Animator Reference (Mecanim) is required by this module. Go to Extra/Control category and assign Mecanim reference there!", UnityEditor.MessageType.Warning);
if (GUILayout.Button("Go to Extra/Control")) { legsAnimator._EditorCategory = LegsAnimator.EEditorCategory.Extra; legsAnimator._EditorExtraCategory = LegsAnimator.EEditorExtraCategory.Control; }
}
GUI_HeaderBody(legsAnimator, helper);
Animator anim = legsAnimator.Mecanim;
bool drawLayer = true;
if (anim)
{
if (anim.layerCount <= 1) drawLayer = false;
}
if (drawLayer)
{
EditorGUILayout.BeginHorizontal();
EditorGUIUtility.labelWidth = 34;
var layerMode = helper.RequestVariable("Mode", 0);
if (Initialized) GUI.enabled = false;
ELayerSelectMode selMode = (ELayerSelectMode)layerMode.GetInt();
selMode = (ELayerSelectMode)EditorGUILayout.EnumPopup(new GUIContent("", "If layer to read animator state/tag from should be selected by index, or by top layer with biggest weight fade"), selMode, GUILayout.MaxWidth(74));
layerMode.SetValue((int)selMode);
GUI.enabled = true;
EditorGUIUtility.labelWidth = 40;
if (selMode == ELayerSelectMode.ByIndex)
{
GUILayout.Space(6);
var layerInd = helper.RequestVariable("Animation Layer", 0);
EditorGUIUtility.labelWidth = 42;
int indx = EditorGUILayout.IntField(new GUIContent("Index:", "Index to read animator state/tag from"), layerInd.GetInt());
if (indx < 0) indx = 0;
if (anim) if (indx > anim.layerCount - 1) indx = anim.layerCount - 1;
layerInd.SetValue(indx);
}
else
{
GUILayout.Space(6);
var skipVar = helper.RequestVariable("Skip", "");
EditorGUIUtility.labelWidth = 35;
string skip = skipVar.GetString();
if (Initialized) GUI.enabled = false;
skip = EditorGUILayout.TextField(new GUIContent("Skip:", "Write here indexes of upper body layers to skip checking them. You can also write here layer names. To skip multiple layers, use command ',' like: 3,4,6"), skip);
skipVar.SetValue(skip);
GUI.enabled = true;
}
EditorGUILayout.EndHorizontal();
EditorGUIUtility.labelWidth = 0;
if (selMode == ELayerSelectMode.Auto) EditorGUILayout.HelpBox("Automatic Layer: " + lastAutoWeightIndex, UnityEditor.MessageType.None);
}
GUI_MiddleBody(legsAnimator, helper);
GUILayout.Space(6);
var fadeSpd = helper.RequestVariable("Fade Speed", 0.75f);
fadeSpd.SetMinMaxSlider(0f, 1f);
fadeSpd.Editor_DisplayVariableGUI();
GUILayout.Space(4);
FGUI_Inspector.DrawUILineCommon(8);
GUI.enabled = !legsAnimator.LegsInitialized;
GUI_OnActionTypeLabel();
var hipsVar = helper.RequestVariable("Animation State Tag", "");
hipsVar.Editor_DisplayVariableGUI();
GUILayout.Space(3);
var extraMultiplier = helper.RequestVariable("Animation State Name", "");
extraMultiplier.Editor_DisplayVariableGUI();
EditorGUILayout.LabelField("Use commas ',' to take into account multiple clips/tags", EditorStyles.centeredGreyMiniLabel);
GUILayout.Space(3);
GUI.enabled = true;
GUI_FooterBody(legsAnimator, helper);
}
protected virtual void GUI_HeaderBody(LegsAnimator legsAnimator, LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
}
protected virtual void GUI_MiddleBody(LegsAnimator legsAnimator, LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
}
protected virtual void GUI_OnActionTypeLabel()
{
EditorGUILayout.LabelField("Do Fade On:", EditorStyles.centeredGreyMiniLabel);
}
protected virtual void GUI_FooterBody(LegsAnimator legsAnimator, LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
if (legsAnimator.LegsInitialized)
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
GUI.enabled = false;
EditorGUILayout.Slider("Current Weight: ", enabledMultiplier, 0f, 1f);
GUI.enabled = true;
EditorGUILayout.EndVertical();
}
}
#endif
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6b9899f5db06bb44eae64edd146badd2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: a5d56514a4a6be14bb151cf321fe54ef, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,162 @@
using System;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public partial class LegsAnimator
{
[System.Serializable]
public class Variable
{
public string VariableName = "Variable";
[SerializeField] private string Tooltip = "";
#region Tooltip refresh clean code helper
bool _tooltipWasSet = false;
public bool TooltipAssigned { get { return _tooltipWasSet; } }
public void AssignTooltip(string tooltip)
{
if (_tooltipWasSet) return;
Tooltip = tooltip;
_tooltipWasSet = true;
}
#endregion
/// <summary> For Number type, .w == 1 -> Int 0 -> Float </summary>
[SerializeField] private Vector4 _value = Vector4.zero;
[SerializeField] private string _string = "";
[SerializeField] private AnimationCurve _curve = null;
[SerializeField] private UnityEngine.Object _uObject = null;
[SerializeField] private object _object = null;
public Variable(string name, object value)
{
VariableName = name;
SetValue(value);
}
[NonSerialized] private int nameHash = 0;
public int GetNameHash
{
get
{
if (nameHash == 0) nameHash = VariableName.GetHashCode();
return nameHash;
}
}
public enum EVariableType
{
Number, Bool, Vector2, Vector3, String, Curve, UnityObject, CustomObject
}
public EVariableType VariableType = EVariableType.Number;
public void SetValue(object o)
{
if (o is int) { _value = new Vector4((int)o, 0, 0, 1); VariableType = EVariableType.Number; }
else if (o is float) { _value = new Vector4((float)o, 0, 0, 0); VariableType = EVariableType.Number; }
else if (o is bool) { bool v = (bool)o; if (v) _value.x = 1; else _value.x = 0; VariableType = EVariableType.Bool; }
else if (o is Vector2) { Vector2 v = (Vector2)o; _value = v; VariableType = EVariableType.Vector2; }
else if (o is Vector3) { Vector3 v = (Vector3)o; _value = v; VariableType = EVariableType.Vector3; }
else if (o is string) { _string = o as string; VariableType = EVariableType.String; }
else if (o is AnimationCurve) { _curve = o as AnimationCurve; VariableType = EVariableType.Curve; }
else if (o is UnityEngine.Object) { _uObject = o as UnityEngine.Object; VariableType = EVariableType.UnityObject; }
else { _object = o; VariableType = EVariableType.CustomObject; }
}
public int GetInt() { return (int)_value.x; }
public float GetFloat() { return _value.x; }
public bool GetBool() { return _value.x == 1; }
public Vector2 GetVector2() { return new Vector2(_value.x, _value.y); }
public Vector3 GetVector3() { return new Vector3(_value.x, _value.y, _value.z); }
public string GetString() { return _string; }
public AnimationCurve GetCurve() { return _curve; }
public UnityEngine.Object GetUnityObject() { return _uObject; }
public object GetObject() { return _object; }
public void SetMinMaxSlider(float min, float max)
{ _rangeHelper = new Vector4(min, max, 0, 0); }
public void SetCurveFixedRange(float startTime, float startValue, float endTime, float endValue)
{ _rangeHelper = new Vector4(startTime, startValue, endTime, endValue); }
[SerializeField] private Vector4 _rangeHelper = Vector4.zero;
/// <summary> Returns true if gui changed </summary>
public bool Editor_DisplayVariableGUI()
{
#if UNITY_EDITOR
EditorGUI.BeginChangeCheck();
GUIContent nameG = new GUIContent(VariableName, Tooltip);
if (VariableType == EVariableType.Number)
{
if (_value.w == 1) // Int
{
if (_rangeHelper.x != _rangeHelper.y && _rangeHelper.y != 0)
_value.x = EditorGUILayout.IntSlider(nameG, (int)_value.x, (int)_rangeHelper.x, (int)_rangeHelper.y);
else
_value.x = EditorGUILayout.IntField(nameG, (int)_value.x);
}
else // Float
{
if (_rangeHelper.x != _rangeHelper.y && _rangeHelper.y != 0)
_value.x = EditorGUILayout.Slider(nameG, _value.x, _rangeHelper.x, _rangeHelper.y);
else
_value.x = EditorGUILayout.FloatField(nameG, _value.x);
}
}
else if (VariableType == EVariableType.Bool)
{
bool v = _value.x == 1;
v = EditorGUILayout.Toggle(nameG, v);
SetValue(v);
}
else if (VariableType == EVariableType.Vector2)
{
_value = EditorGUILayout.Vector2Field(nameG, _value);
}
else if (VariableType == EVariableType.Vector3)
{
_value = EditorGUILayout.Vector3Field(nameG, _value);
}
else if (VariableType == EVariableType.String)
{
_string = EditorGUILayout.TextField(nameG, _string);
}
else if (VariableType == EVariableType.Curve)
{
_curve = EditorGUILayout.CurveField(nameG, _curve);
}
else if (VariableType == EVariableType.UnityObject)
{
_uObject = EditorGUILayout.ObjectField(nameG, _uObject, typeof(UnityEngine.Object), true);
}
else if (VariableType == EVariableType.CustomObject)
{
if (_object == null)
EditorGUILayout.LabelField("Containing Null");
else
EditorGUILayout.LabelField("Containing custom, not serialized object");
}
return EditorGUI.EndChangeCheck();
#else
return false;
#endif
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5f0cf00fef4c81b40ac504119c12a35c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: a5d56514a4a6be14bb151cf321fe54ef, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,152 @@
#if UNITY_EDITOR
using FIMSpace.FEditor;
using UnityEditor;
#endif
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
public abstract class LegsAnimatorControlModuleBase : ScriptableObject
{
protected Transform Transform { get { return Owner.BaseTransform; } }
protected LegsAnimator LA { get { return Owner; } }
protected LegsAnimator LegsAnim { get { return Owner; } }
protected LegsAnimator Owner { get; private set; }
protected bool Initialized { get; private set; } = false;
/// <summary> Editor helper to display extra field in the inspector view </summary>
public virtual bool AskForSpineBone { get { return false; } }
/// <summary> Editor helper to display extra field in the inspector view </summary>
public virtual bool AskForChestBone { get { return false; } }
/// <summary> If module supports it, use this value to fade off module influence </summary>
public float ModuleBlend { get; set; }
/// <summary> Module Blend * Legs Animator Blend </summary>
public float EffectBlend { get { return ModuleBlend * LA._MainBlend; } }
public void Base_Init(LegsAnimator legs, LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
ModuleBlend = 1f;
Owner = legs;
OnInit(helper);
Initialized = true;
}
/// <summary> [Base method does nothing] Called when Legs Animator starts to work for a first time </summary>
public virtual void OnInit(LegsAnimator.LegsAnimatorCustomModuleHelper helper) { }
/// <summary> [Base method does nothing] Calle after enabling back legs animator </summary>
public virtual void OnReInitialize(LegsAnimator.LegsAnimatorCustomModuleHelper helper) { }
/// <summary> [Base method does nothing] Called after main calibration, before leg animator algorithms </summary>
public virtual void OnUpdate(LegsAnimator.LegsAnimatorCustomModuleHelper helper) { }
/// <summary> [Base method does nothing] Called before capturing animator pose </summary>
public virtual void OnAfterAnimatorCaptureUpdate(LegsAnimator.LegsAnimatorCustomModuleHelper helper) { }
/// <summary> [Base method does nothing] Called before main calculations, before hips calculations </summary>
public virtual void OnPreLateUpdate(LegsAnimator.LegsAnimatorCustomModuleHelper helper) { }
/// <summary> [Base method does nothing] Called after main calculations, after hips calculations, just before applying IK </summary>
public virtual void OnLateUpdatePreApply(LegsAnimator.LegsAnimatorCustomModuleHelper helper) { }
/// <summary> [Base method does nothing] Called after applying IK </summary>
public virtual void OnPostLateUpdate(LegsAnimator.LegsAnimatorCustomModuleHelper helper) { }
/// <summary> [Base method does nothing] Special call, to update some of the IK settings only when big changes are happening. (called every change in the inspector window but needs to be called manually if editing IK settings through code) </summary>
public virtual void OnValidateAfterManualChanges(LegsAnimator.LegsAnimatorCustomModuleHelper helper) { }
/// <summary> [Base method does nothing] Updated per leg with leg access after PreLateUpdate </summary>
public virtual void Leg_LatePreRaycastingUpdate(LegsAnimator.LegsAnimatorCustomModuleHelper helper, LegsAnimator.Leg leg) { }
/// <summary> [Base method does nothing] Updated on each limbs hub (hips) during stability calculation </summary>
//public virtual void OnHipsStabilizingLegInfluence(LegsAnimator.LegsAnimatorCustomModuleHelper helper, LegsAnimator.Leg leg, ref Vector3 stabilityOffsetLocal) { }
/// <summary> [Base method does nothing] Updated per leg with leg access after Raycasting </summary>
public virtual void Leg_LateUpdate(LegsAnimator.LegsAnimatorCustomModuleHelper helper, LegsAnimator.Leg leg) { }
#region Commented but can be helpful later
///// <summary> [Base method does nothing] </summary>
//public virtual void Leg_OnLeg_Raycasting(LegsAnimator.Leg leg)
//{
//}
///// <summary> [Base method does nothing] </summary>
//public virtual void Leg_OnIK_Apply(LegsAnimator.Leg leg)
//{
//}
///// <summary> [Base method returns true] </summary>
//public virtual bool Leg_OnGlue_Condition_AllowAttach(LegsAnimator leg)
//{
// return true;
//}
///// <summary> [Base method does nothing] </summary>
//public virtual void Leg_OnGlue_Apply(LegsAnimator.Leg leg)
//{
//}
///// <summary> [Base method does nothing] </summary>
//public virtual void Leg_OnHips_Apply(LegsAnimator.Leg leg)
//{
//}
#endregion
#region Editor Code
#if UNITY_EDITOR
[System.NonSerialized] public SerializedObject BaseSerializedObject = null;
[System.NonSerialized] public bool Editor_Foldout = false;
[System.NonSerialized] public bool Editor_PlaymodeFoldout = false;
/// <summary> [Base method does nothing] </summary>
public virtual void Editor_OnSceneGUI(LegsAnimator legsAnimator, LegsAnimator.LegsAnimatorCustomModuleHelper helper) { }
/// <summary> [Base method does nothing] </summary>
public virtual void Editor_InspectorGUI(LegsAnimator legsAnimator, LegsAnimator.LegsAnimatorCustomModuleHelper helper) { }
#endif
#endregion
#region Editor Class
#if UNITY_EDITOR
[UnityEditor.CanEditMultipleObjects]
[UnityEditor.CustomEditor(typeof(LegsAnimatorControlModuleBase))]
public class LegsAnimatorControlModuleBaseEditor : UnityEditor.Editor
{
public LegsAnimatorControlModuleBase Get { get { if (_get == null) _get = (LegsAnimatorControlModuleBase)target; return _get; } }
private LegsAnimatorControlModuleBase _get;
public override void OnInspectorGUI()
{
if (LegsAnimator._Editor_LastSelectedLA != null)
{
if (GUILayout.Button(" < Go Back To '" + LegsAnimator._Editor_LastSelectedLA.name + "'", GUILayout.Height(24)))
{
Selection.activeGameObject = LegsAnimator._Editor_LastSelectedLA.gameObject;
}
FGUI_Inspector.DrawUILineCommon(5);
}
DrawDefaultInspector();
}
}
#endif
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4f120c4a03683c64ab9643feb165e1d2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: a5d56514a4a6be14bb151cf321fe54ef, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,159 @@
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using static FIMSpace.FProceduralAnimation.LegsAnimator;
namespace FIMSpace.FProceduralAnimation
{
/// <summary>
/// Examples class for customized controll over the Legs Animator IK Redirecting features
/// </summary>
//[CreateAssetMenu(fileName = "LAM_AnimCurveGlueCondition", menuName = "FImpossible Creations/Legs Animator/Control Module - Animation Curves Glue Condition", order = 5)]
public class LAM_AnimationCurvesGlueCondition : LegsAnimatorControlModuleBase
{
LegsAnimatorCustomModuleHelper _useHelper = null;
Variable FloorValueBelowVar { get { return _useHelper.RequestVariable("Floor Value Below", 0.01f); } }
Variable _play_FloorValueBelow = null;
Variable IgnoreMidConditionsVar { get { return _useHelper.RequestVariable("Ignore Mid Conditions", false); } }
Variable _play_IgnoreMidConditions = null;
Variable AllowHeightGlueOnLevelVar { get { return _useHelper.RequestVariable("Allow Height Glue On Level", -1f); } }
Variable _play_AllowHeightGlueOnLevels = null;
private List<int> animatorHashes = null;
private bool initialized = false;
public override void OnInit(LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
if (LA.Mecanim == null) return;
if (helper.customStringList == null) return;
_useHelper = helper;
_play_FloorValueBelow = FloorValueBelowVar;
_play_IgnoreMidConditions = IgnoreMidConditionsVar;
_play_AllowHeightGlueOnLevels = AllowHeightGlueOnLevelVar;
animatorHashes = new List<int>();
for (int l = 0; l < LA.Legs.Count; l++)
{
if (l >= helper.customStringList.Count) break;
animatorHashes.Add(Animator.StringToHash(helper.customStringList[l]));
}
initialized = true;
}
public override void Leg_LateUpdate(LegsAnimator.LegsAnimatorCustomModuleHelper helper, LegsAnimator.Leg leg)
{
if (initialized == false) return;
if (leg.G_CustomForceAttach) return; // If other module forces attach, skip calculations
float value = LA.Mecanim.GetFloat(animatorHashes[leg.PlaymodeIndex]);
if (value <= _play_AllowHeightGlueOnLevels.GetFloat()) // If allowing to glue on foot overlaps
{
if (leg.A_PreWasAligning)
{
value = _play_FloorValueBelow.GetFloat() - 0.01f;
}
}
if (value <= _play_FloorValueBelow.GetFloat()) // Value below ground level - GROUNDED
{
leg.G_CustomForceAttach = LA.GroundedTime > 0.2f;
//leg.G_CustomForceNOTAttach = false;
if (_play_IgnoreMidConditions.GetBool())
{
leg.G_CustomForceNOTDetach = true;
//leg.G_CustomForceDetach = false;
}
}
else // Value above grounded level - Foot UNGROUNDED
{
//leg.G_CustomForceAttach = false;
leg.G_CustomForceNOTAttach = true;
if (_play_IgnoreMidConditions.GetBool())
{
//leg.G_CustomForceNOTDetach = false;
leg.G_CustomForceDetach = true;
}
}
}
#region Editor Code
#if UNITY_EDITOR
public override void Editor_InspectorGUI(LegsAnimator legsAnimator, LegsAnimatorCustomModuleHelper helper)
{
_useHelper = helper;
if (legsAnimator.Mecanim == null)
{
UnityEditor.EditorGUILayout.HelpBox("No Animator found to handle animation curves!", UnityEditor.MessageType.Warning);
return;
}
EditorGUILayout.HelpBox("Using animation curve parameters to controll gluing timing. It requires extra curves inside animation clips but can provide better controll for gluing feature.", UnityEditor.MessageType.Info);
GUILayout.Space(5);
var floorValV = FloorValueBelowVar;
if (!floorValV.TooltipAssigned) floorValV.AssignTooltip( "Gluing condition basing on animation curves");
floorValV.Editor_DisplayVariableGUI();
var ignMidV = IgnoreMidConditionsVar;
if (!ignMidV.TooltipAssigned) ignMidV.AssignTooltip( "When enabled algorithm will not check detaching conditions on foot rotation angles");
ignMidV.Editor_DisplayVariableGUI();
//var allowHV = AllowHeightGlueOnLevelVar;
//if (allowHV.Tooltip == "") allowHV.Tooltip = "If at some value you want to allow glue if character is walking on steep terrain. If parameter is below choosed value it will be allowed";
//allowHV.Editor_DisplayVariableGUI();
GUILayout.Space(5);
if (helper.customStringList == null) helper.customStringList = new List<string>();
var list = helper.customStringList;
int targetCount = legsAnimator.Legs.Count;
if (list.Count < targetCount)
while (list.Count < targetCount) list.Add("");
else
while (list.Count > targetCount) list.RemoveAt(list.Count - 1);
GUILayout.Space(5);
EditorGUILayout.LabelField("Mecanim parameters per leg", EditorStyles.helpBox);
GUILayout.Space(3);
for (int i = 0; i < list.Count; i++)
{
list[i] = EditorGUILayout.TextField(new GUIContent("Leg [" + i + "] Curve Parameter:", "Legs name = " + legsAnimator.Legs[i].BoneStart.name + "\nAnimator parameter to read curve value for triggering gluing with more control"), list[i]);
}
GUILayout.Space(5);
if (initialized == false) return;
for (int l = 0; l < animatorHashes.Count; l++)
{
UnityEditor.EditorGUILayout.BeginHorizontal();
float val = LA.Mecanim.GetFloat(animatorHashes[l]);
UnityEditor.EditorGUILayout.LabelField(" [" + l + "] " + val, GUILayout.Width(106));
UnityEditor.EditorGUILayout.LabelField(LA.Legs[l].Side.ToString(), GUILayout.Width(54));
UnityEditor.EditorGUILayout.LabelField(" " + (val < _play_FloorValueBelow.GetFloat() ? "FOOT GROUNDED" : "FOOT MOVING"));
UnityEditor.EditorGUILayout.EndHorizontal();
}
}
#endif
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 507b876a190fd9b4ea6ec33d19af8342
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: a5d56514a4a6be14bb151cf321fe54ef, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,186 @@
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
//[CreateAssetMenu(fileName = "LAM_Auto Ground Align Body", menuName = "FImpossible Creations/Legs Animator/Control Module - Auto Ground Align Body", order = 1)]
public class LAM_AutoGroundAlignBodyMatrix : LegsAnimatorControlModuleBase
{
Vector3 averageNormal;
Vector3 animatedAverageNormal;
Quaternion lastOrientation;
LegsAnimator.Variable _blendV;
LegsAnimator.Variable _rotateV;
LegsAnimator.Variable _alignSpdV;
LegsAnimator.Variable _alignDownV;
LegsAnimator.Variable _AxisBlendV;
float _blend = 1f;
public override bool AskForSpineBone { get { return true; } }
public override void OnInit(LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
animatedAverageNormal = LA.Up;
lastOrientation = LA.BaseTransform.rotation;
_blendV = helper.RequestVariable("Matrix Blend", 1f);
_rotateV = helper.RequestVariable("Rotate Hips", 1f);
_alignSpdV = helper.RequestVariable("Aligning Speed", .7f);
_alignDownV = helper.RequestVariable("Spine Restore", .5f);
_AxisBlendV = helper.RequestVariable("Rotation Axis Blend", Vector3.one);
}
public override void Leg_LatePreRaycastingUpdate(LegsAnimator.LegsAnimatorCustomModuleHelper helper, LegsAnimator.Leg leg)
{
_blend = EffectBlend;
if (!leg.RaycastHitted) return;
// ! Let's still align body to predicted hits ! if (!leg.A_PreWasAligning) return;
averageNormal += leg.LastGroundHit.normal;
}
public override void OnUpdate(LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
Quaternion rot = lastOrientation;
float blend = _blend * _blendV.GetFloat();
if (blend < 1f) rot = Quaternion.SlerpUnclamped(Quaternion.identity, lastOrientation, blend);
Matrix4x4 mx = Matrix4x4.TRS(LA.BaseTransform.position, rot * LA.BaseTransform.rotation , LA.BaseTransform.lossyScale);
LA.User_OverwriteCastMatrix(mx);
}
public override void OnAfterAnimatorCaptureUpdate(LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
float blend = _blend * _rotateV.GetFloat();
if (blend < 0.001f) return;
Vector3 eulers = lastOrientation.eulerAngles;
eulers.x = LAM_DirectionalMovement.FormatAngleToPM180(eulers.x);
eulers.y = LAM_DirectionalMovement.FormatAngleToPM180(eulers.y);
eulers.z = LAM_DirectionalMovement.FormatAngleToPM180(eulers.z);
//LA._Hips_Modules_ExtraRotOffset += FEngineering.QToLocal(LA.BaseTransform.rotation, lastOrientation).eulerAngles;
Quaternion rotOffset = Quaternion.identity;
//rotOffset *= Quaternion.AngleAxis(eulers.x * blend, LA.BaseTransform.right);
//rotOffset *= Quaternion.AngleAxis(eulers.y * blend, LA.BaseTransform.up);
//rotOffset *= Quaternion.AngleAxis(eulers.z * blend, LA.BaseTransform.forward);
rotOffset *= Quaternion.AngleAxis(eulers.x * blend, Vector3.right);
rotOffset *= Quaternion.AngleAxis(eulers.y * blend, Vector3.up);
rotOffset *= Quaternion.AngleAxis(eulers.z * blend, Vector3.forward);
LA._LastHipsRotationOffsetOutsideInfo *= rotOffset;
if (LA.SpineBone)
{
Quaternion preSpineRot = LA.SpineBone.rotation;
//LA.HipsSetup.UniRotate.RotateBy(eulers, blend);
LA.HipsSetup.bone.rotation = rotOffset * LA.HipsSetup.bone.rotation;
//LA.SpineBone.rotation = Quaternion.Slerp(rotOffset * LA.SpineBone.rotation, LA.SpineBone.rotation, _alignDownV.GetFloat());
LA.SpineBone.rotation = Quaternion.Slerp(LA.SpineBone.rotation, preSpineRot, _alignDownV.GetFloat());
}
else
{
LA.HipsSetup.bone.rotation = rotOffset * LA.HipsSetup.bone.rotation;
}
}
public override void OnLateUpdatePreApply(LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
if (averageNormal == Vector3.zero) averageNormal = LA.Up;
else
averageNormal.Normalize();
if (_alignSpdV.GetFloat() < 0.999f)
{
float speedMul = Mathf.LerpUnclamped(5f, 20f, _alignSpdV.GetFloat());
animatedAverageNormal = Vector3.Slerp(animatedAverageNormal, averageNormal, LA.DeltaTime * speedMul);
}
else animatedAverageNormal = averageNormal;
lastOrientation = Quaternion.FromToRotation(Vector3.up, animatedAverageNormal);
Vector3 axisMul = _AxisBlendV.GetVector3();
if (axisMul != Vector3.one)
{
Vector3 eulers = lastOrientation.eulerAngles;
axisMul = helper.Parent.BaseTransform.TransformDirection(axisMul);
lastOrientation = Quaternion.Euler(eulers.x * axisMul.x, eulers.y * axisMul.y, eulers.z * axisMul.z);
}
averageNormal = Vector3.zero;
}
#region Editor Code
#if UNITY_EDITOR
public override void Editor_InspectorGUI(LegsAnimator legsAnimator, LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
EditorGUILayout.HelpBox("Adjusting body raycasting matrix on the raycasted slope + offers hips rotation. Mostly useful for spider creatures but it can also help humanoids slopes movement.", UnityEditor.MessageType.Info);
var mxVar = helper.RequestVariable("Matrix Blend", 1f);
mxVar.SetMinMaxSlider(0f, 1f);
mxVar.Editor_DisplayVariableGUI();
var alignSpeedVar = helper.RequestVariable("Aligning Speed", .7f);
alignSpeedVar.SetMinMaxSlider(0f, 1f);
alignSpeedVar.Editor_DisplayVariableGUI();
GUILayout.Space(4);
EditorGUILayout.LabelField("Optional hips rotation", EditorStyles.centeredGreyMiniLabel);
var rotateVar = helper.RequestVariable("Rotate Hips", 0f);
rotateVar.SetMinMaxSlider(0f, 1f);
rotateVar.Editor_DisplayVariableGUI();
if (legsAnimator.SpineBone != null)
{
var downVar = helper.RequestVariable("Spine Restore", .5f);
downVar.SetMinMaxSlider(0f, 1f);
downVar.Editor_DisplayVariableGUI();
}
GUILayout.Space(5);
var axisV = helper.RequestVariable("Rotation Axis Blend", Vector3.one);
axisV.AssignTooltip("Giving possibility for locking alignment rotation to target axes.\nX = Forward/Back lean Z = Sides Lean,\nset axis value 0 to disable adjustement leaning for the axis.");
axisV.Editor_DisplayVariableGUI();
}
public override void Editor_OnSceneGUI(LegsAnimator legsAnimator, LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
for (int l = 0; l < legsAnimator.Legs.Count; l++)
{
legsAnimator.Legs[l]._Editor_Raycasting_DrawControls();
}
}
#endif
#endregion
#region Inspector Editor Class Ineritance
#if UNITY_EDITOR
[UnityEditor.CanEditMultipleObjects]
[UnityEditor.CustomEditor(typeof(LAM_AutoGroundAlignBodyMatrix))]
public class LAM_AutoGroundAlignBodyMatrixEditor : LegsAnimatorControlModuleBaseEditor
{
}
#endif
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5303e47ec9952914e872859b97520662
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: a5d56514a4a6be14bb151cf321fe54ef, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,148 @@
using System;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using static FIMSpace.FProceduralAnimation.LegsAnimator;
namespace FIMSpace.FProceduralAnimation
{
//[CreateAssetMenu(fileName = "LAM_BasicPoseAdjust", menuName = "FImpossible Creations/Legs Animator/LAM_BasicPoseAdjust", order = 2)]
public class LAM_BasicPoseAdjust : LegsAnimatorControlModuleBase
{
LegsAnimator.Variable _AdjustPowerX;
LegsAnimator.Variable _AdjustPowerZ;
public override void OnInit(LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
_AdjustPowerX = helper.RequestVariable("Adjust X Positioning", 1f);
_AdjustPowerZ = helper.RequestVariable("Adjust Z Positioning", 1f);
#region Selective legs use implementation
// Prepare legs to work on
List<LegsAnimator.Leg> preLegs = new List<LegsAnimator.Leg>();
if (helper.customStringList == null || helper.customStringList.Count == 0)
{
for (int i = 0; i < LA.Legs.Count; i++) preLegs.Add(LA.Legs[i]); // Add All
}
else
for (int i = 0; i < helper.customStringList.Count; i++)
{
if (helper.customStringList[i] == "1") preLegs.Add(LA.Legs[i]); // Add Selective
}
if (preLegs.Count == 0)
{
helper.Enabled = false;
Debug.Log("[Legs Animator] Fade On Animation Module: No legs definition!");
return;
}
legs = preLegs.ToArray();
#endregion
}
[NonSerialized] LegsAnimator.Leg[] legs; // I have no idea but unity keeps creating serialization cycle on this variable, if not using [NonSerialized] even when it's private variable
public override void OnAfterAnimatorCaptureUpdate(LegsAnimatorCustomModuleHelper helper)
{
if (legs == null) return;
float blend = EffectBlend;
for (int l = 0; l < legs.Length; l++)
{
var leg = legs[l];
Vector3 mainLoc = LA.ToRootLocalSpace(leg._AnimatorEndBonePos);
Vector3 local = mainLoc;
local.x *= _AdjustPowerX.GetFloat();
local.z *= _AdjustPowerZ.GetFloat();
if (blend < 1f) local = Vector3.LerpUnclamped(mainLoc, local, blend);
leg.OverrideAnimatorAnklePosition(LA.RootToWorldSpace(local));
}
}
#region Editor Code
#if UNITY_EDITOR
public override void Editor_InspectorGUI(LegsAnimator legsAnimator, LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
EditorGUILayout.HelpBox("Multiplying IK positions in local space, so you can adjust legs spacing.", UnityEditor.MessageType.Info);
GUILayout.Space(3);
var adjPowerV = helper.RequestVariable("Adjust X Positioning", 1f);
adjPowerV.SetMinMaxSlider(0f, 2f);
adjPowerV.Editor_DisplayVariableGUI();
GUILayout.Space(3);
var rotateVar = helper.RequestVariable("Adjust Z Positioning", 1f);
rotateVar.SetMinMaxSlider(0f, 2f);
rotateVar.Editor_DisplayVariableGUI();
GUILayout.Space(3);
#region Draw legs list
if (helper.customStringList == null) helper.customStringList = new List<string>();
var list = helper.customStringList;
int targetCount = legsAnimator.Legs.Count;
if (list.Count < targetCount)
while (list.Count < targetCount) list.Add("1");
else
while (list.Count > targetCount) list.RemoveAt(list.Count - 1);
GUILayout.Space(5);
EditorGUILayout.LabelField("Select legs to apply module effect on:", EditorStyles.helpBox);
GUILayout.Space(3);
GUI.enabled = !legsAnimator.LegsInitialized;
for (int i = 0; i < list.Count; i++)
{
var boneStart = legsAnimator.Legs[i].BoneStart;
if (boneStart == null)
{
EditorGUILayout.LabelField("[" + (i + 1) + "] LEG LACKING BONE REFERENCES");
continue;
}
EditorGUILayout.BeginHorizontal();
var str = list[i];
bool target;
if (str.Length == 0 || str[0] != '1') target = false; else target = true;
target = EditorGUILayout.Toggle("[" + (i + 1) + "]: " + boneStart.name, target);
if (target == false)
list[i] = "0";
else
list[i] = "1";
EditorGUILayout.ObjectField(boneStart, typeof(Transform), true, GUILayout.Width(60));
EditorGUILayout.EndHorizontal();
}
GUI.enabled = true;
#endregion
}
#endif
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ec012c41db0f4b148b727ceb9c5a8aed
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: a5d56514a4a6be14bb151cf321fe54ef, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,98 @@
#if UNITY_EDITOR
using FIMSpace.FEditor;
using UnityEditor;
#endif
using UnityEngine;
using static FIMSpace.FProceduralAnimation.LegsAnimator;
namespace FIMSpace.FProceduralAnimation
{
//[CreateAssetMenu(fileName = "LAM_DesiredDirectionFromTransform", menuName = "FImpossible Creations/Legs Animator/LAM_DesiredDirectionFromTransform", order = 2)]
public class LAM_DesiredDirectionFromTransform : LegsAnimatorControlModuleBase
{
LegsAnimator.Variable _Reaction;
LegsAnimator.Variable _Thres;
LegsAnimator.Variable _IsMov;
Vector3 calculatedVelo = Vector3.zero;
Vector3 _sd_average = Vector3.zero;
Vector3 previousPosition = Vector3.zero;
public override void OnInit(LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
_Reaction = helper.RequestVariable("Reaction Speed", .8f);
_IsMov = helper.RequestVariable("Control 'IsMoving'", false);
_Thres = helper.RequestVariable("Not Moving Threshold", .2f);
previousPosition = LA.BaseTransform.position;
}
public override void OnUpdate(LegsAnimatorCustomModuleHelper helper)
{
Vector3 diff = LA.BaseTransform.position - previousPosition;
previousPosition = LA.BaseTransform.position;
Vector3 newVelo = diff / LA.DeltaTime;
newVelo = LA.ToRootLocalSpaceVec(newVelo);
newVelo.y = 0f; // Neutralize jump velo in LA local Up space
newVelo = LA.RootToWorldSpaceVec(newVelo);
float magnitude = calculatedVelo.magnitude;
newVelo = Vector3.Slerp(newVelo, newVelo.normalized, Mathf.InverseLerp(0f, magnitude, LA.ScaleReference));
calculatedVelo = Vector3.SmoothDamp(calculatedVelo, newVelo, ref _sd_average, (0.00005f + (1f - _Reaction.GetFloat()) * 0.15f), 100000f, LA.DeltaTime);
if (_IsMov.GetBool())
{
LA.User_SetIsMoving(magnitude > LA.ScaleReference * _Thres.GetFloat());
}
LA.User_SetDesiredMovementDirection(calculatedVelo);
}
#region Editor Code
#if UNITY_EDITOR
public override void Editor_InspectorGUI(LegsAnimator legsAnimator, LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
EditorGUILayout.HelpBox("Reading world translation of the character and providing as 'Desired Movement Direction'.\nIt's not precise as rigidbody desired direction read.", UnityEditor.MessageType.Info);
GUILayout.Space(3);
var adjPowerV = helper.RequestVariable("Reaction Speed", .8f);
adjPowerV.SetMinMaxSlider(0f, 1f);
adjPowerV.Editor_DisplayVariableGUI();
var adjIsMoving = helper.RequestVariable("Control 'IsMoving'", false);
if (!adjIsMoving.TooltipAssigned) adjIsMoving.AssignTooltip("Change IsMoving flag basing on computed transform velocity. ! Can be delayed, it's more precise to set it through Animator parameter, rigidbody or through coding !");
adjIsMoving.Editor_DisplayVariableGUI();
if (adjIsMoving.GetBool())
{
var adjThres = helper.RequestVariable("Not Moving Threshold", .2f);
adjThres.SetMinMaxSlider(0f, 0.5f);
if (!adjThres.TooltipAssigned) adjThres.AssignTooltip("Threshold on which IsMoving should be set to false (for quicker no-moving response)");
adjThres.Editor_DisplayVariableGUI();
}
GUILayout.Space(3);
EditorGUILayout.BeginVertical(FGUI_Resources.BGInBoxStyle);
if (legsAnimator.LegsInitialized)
{
EditorGUILayout.LabelField("Calculated Direction Velocity: " + calculatedVelo);
if (adjIsMoving.GetBool())
EditorGUILayout.LabelField("Is Moving: " + LA.IsMoving);
}
EditorGUILayout.EndVertical();
}
#endif
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 390566e04c68b984aabed63834364e2b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: a5d56514a4a6be14bb151cf321fe54ef, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,976 @@
using System;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
using FIMSpace.FEditor;
#endif
using UnityEngine;
using static FIMSpace.FProceduralAnimation.LegsAnimator;
using System.Linq;
namespace FIMSpace.FProceduralAnimation
{
/// <summary>
/// Examples class for customized controll over the Legs Animator IK Redirecting features
/// </summary>
[CreateAssetMenu(fileName = "LAM_DirectionalMovement", menuName = "FImpossible Creations/Legs Animator/Module - 360 Movement Animation", order = 2)]
public class LAM_DirectionalMovement : LegsAnimatorControlModuleBase
{
#region Module Instance Variables
LegsAnimatorCustomModuleHelper _useHelper = null;
Variable HipsRedirVar { get { return _useHelper.RequestVariable("Redirect Hips", 0.8f); } }
Variable _play_HipsRedir = null;
Variable FeetRedirVar { get { return _useHelper.RequestVariable("Redirect Feet", 0.8f); } }
Variable _play_FeetRedir = null;
Variable KneesRedirVar { get { return _useHelper.RequestVariable("Redirect Knees", 0.4f); } }
Variable _play_KneesRedir = null;
Variable TrDurationVar { get { return _useHelper.RequestVariable("Transitions Duration", 0.25f); } }
Variable _play_TrDur = null;
Variable LimitRaiseVar { get { return _useHelper.RequestVariable("Limit Leg Raise", 0.1f); } }
Variable _play_LimitRaise = null;
Variable FixFeetVar { get { return _useHelper.RequestVariable("Fix Backward Feet", 1f); } }
Variable _play_FixFeet = null;
Variable AdjustStretchVar { get { return _useHelper.RequestVariable("Adjust Stretched", 0.2f); } }
Variable _play_AdjustStretch = null;
Variable RestoreSpineVar { get { return _useHelper.RequestVariable("Restore Spine", 0.5f); } }
Variable _play_RestoreSpine = null;
Variable ExtraSmootherVar { get { return _useHelper.RequestVariable("Extra Smoother", 0f); } }
Variable _play_Smoother = null;
Variable ReAdjVar { get { return _useHelper.RequestVariable("Re-adjust with hips offset", false); } }
Variable _play_reAdj = null;
//Variable UseRigidVar { get { return _useHelper.RequestVariable("Use Rigidbody For Dir", false); } }
//Variable _play_UseRigid = null;
Variable FadeOffInAirVar { get { return _useHelper.RequestVariable("Disable When Jumping", false); } }
Variable _play_offInAir = null;
Variable XDirAnimVarVar { get { return _useHelper.RequestVariable("Animator World X Dir", ""); } }
int _hash_xDir = -1;
Variable ZDirAnimVarVar { get { return _useHelper.RequestVariable("Animator World Z Dir", ""); } }
int _hash_zDir = -1;
#endregion
#region Calculation Variables
Vector3 _calc_WorldDir = Vector3.zero;
Vector3 _calc_LocalDir = Vector3.zero;
Quaternion _calc_LocalRotDir = Quaternion.identity;
float _localTargetAngle = 0f;
float _wrappedAngle = 0f;
float _smoothedWrappedAngle = 0f;
float _calc_smoothedTargetAngle = 0f;
float _calc_angleDiffFactor = 0f;
float _calc_toNegativeXProgress = 0f;
internal float _calc_backAngleOff = 0f; // for later
float _calc_sideFactorL = 0f;
float _calc_sideFactorR = 0f;
internal float _calc_sideFactor = 0f; // for later
float _calc_deltaSpeed = 0f;
float _calc_deltaSpeedSlow = 0f;
float _var_raiseLimit = 0f;
float _var_fixFeet = 0f;
Vector3 _calc_hipsPositionOffsets = Vector3.zero;
Vector3 _calc_hipsRotationOffsets = Vector3.zero;
Vector3 _calc_hipsStretchOffset = Vector3.zero;
Vector3 _sd_hipsStretchOff = Vector3.zero;
Vector3 _calc_ikOff = Vector3.zero;
List<LegRedirectHelper> legRedirectHelpers = null;
[NonSerialized] public Transform SpineBone = null;
#endregion
/// <summary> Use it for custom multiply axis values for hips redirecting </summary>
[NonSerialized] public Vector3 User_MultiplyHipsOffsets = Vector3.one;
[System.Serializable]
public class AnglesSetup
{
public Vector3 AnglesOn0 = new Vector3(0f, 0f, 0f);
[Tooltip(" Hips rotations on reaching 45 angle movement")]
public Vector3 AnglesOn45 = new Vector3(-10f, 14f, -5f);
[Tooltip(" Hips rotations on reaching 90 angle movement")]
public Vector3 AnglesOn90 = new Vector3(-7f, 40f, -3f);
[Tooltip(" Hips rotations on reaching 135 angle movement")]
public Vector3 AnglesOn135 = new Vector3(-8f, -25f, -4f);
[Tooltip(" Hips rotations on reaching 180 angle movement")]
public Vector3 AnglesOn180 = new Vector3(-20f, 0f, 0f);
[Space(8)]
public Vector3 HipsOffsetOn0 = new Vector3(0f, 0f, 0f);
[Tooltip(" Hips position offset on reaching 45 angle movement")]
public Vector3 HipsOffsetOn45 = new Vector3(-0.05f, 0f, -0.05f);
[Tooltip(" Hips position offset on reaching 90 angle movement")]
public Vector3 HipsOffsetOn90 = new Vector3(-0.1f, 0f, 0.05f);
[Tooltip(" Hips position offset on reaching 135 angle movement")]
public Vector3 HipsOffsetOn135 = new Vector3(-0.1f, 0f, 0.1f);
[Tooltip(" Hips position offset on reaching 180 angle movement")]
public Vector3 HipsOffsetOn180 = new Vector3(0f, 0.05f, 0.2f);
[Space(8)]
public Vector3 IKsOffsetOn0 = new Vector3(0f, 0f, 0f);
[Tooltip(" Foot IK position offset on reaching 45 angle movement (x on left leg goes negative)")]
public Vector3 IKsOffsetOn45 = new Vector3(0f, 0f, -0.04f);
[Tooltip(" Foot IK position offset on reaching 90 angle movement (x on left leg goes negative)")]
public Vector3 IKsOffsetOn90 = new Vector3(0f, 0f, -0.08f);
[Tooltip(" Foot IK position offset on reaching 135 angle movement (x on left leg goes negative)")]
public Vector3 IKsOffsetOn135 = new Vector3(0f, 0f, 0.08f);
[Tooltip(" Foot IK position offset on reaching 180 angle movement (x on left leg goes negative)")]
public Vector3 IKsOffsetOn180 = new Vector3(0f, 0f, 0f);
}
[FPD_Header("Angles setup to drive procedural animation")]
public AnglesSetup Animation360Angles;
/// <summary> Rotating hips on stretch adjustements, default is 30 </summary>
[NonSerialized] public float User_StretchRotatorAnglePower = 30f;
[NonSerialized] public float User_StretchPositionMultiplier = 1f;
float _mainBlend = 1f;
public override void OnInit(LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
_useHelper = helper;
// Get variable references for quicker computing in playtime
_play_HipsRedir = HipsRedirVar;
_play_TrDur = TrDurationVar;
_play_LimitRaise = LimitRaiseVar;
_play_FeetRedir = FeetRedirVar;
_play_KneesRedir = KneesRedirVar;
_play_FixFeet = FixFeetVar;
_play_AdjustStretch = AdjustStretchVar;
_play_RestoreSpine = RestoreSpineVar;
_play_Smoother = ExtraSmootherVar;
//_play_UseRigid = UseRigidVar;
_play_offInAir = FadeOffInAirVar;
_play_reAdj = ReAdjVar;
_wasUpdated = false;
// Prepare leg redirecting helpers
legRedirectHelpers = new List<LegRedirectHelper>();
for (int i = 0; i < LA.Legs.Count; i++)
{
LegRedirectHelper lHelp = new LegRedirectHelper(this, LA.Legs[i]);
legRedirectHelpers.Add(lHelp);
}
for (int i = 0; i < LA.Legs.Count; i++) // After generating helpers, assign Opposite leg helpers
{
if (LA.Legs[i].OppositeLegIndex < 0) continue;
LegRedirectHelper lHelp = legRedirectHelpers[i];
lHelp.oppositeHelper = legRedirectHelpers[LA.Legs[i].OppositeLegIndex];
}
if (SpineBone == null)
{
if (LA.Hips.childCount > 0)
{
if (LA.Hips.childCount == 1) { SpineBone = LA.Hips.GetChild(0); }
else
{
for (int c = 0; c < LA.Hips.childCount; c++)
{
if (LA.Hips.GetChild(c).name.ToLower().Contains("spin")) { SpineBone = LA.Hips.GetChild(c); break; }
}
if (SpineBone == null) SpineBone = LA.Hips.GetChild(0);
}
}
}
if (LA.Mecanim)
{
var xdirV = XDirAnimVarVar;
if (!string.IsNullOrWhiteSpace(xdirV.GetString()))
{
_hash_xDir = Animator.StringToHash(xdirV.GetString());
var zdirV = ZDirAnimVarVar;
_hash_zDir = Animator.StringToHash(zdirV.GetString());
}
}
}
bool _wasUpdated;
public override void OnPreLateUpdate(LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
_mainBlend = LA._MainBlend * ModuleBlend;
if (_play_offInAir.GetBool()) _mainBlend *= LA.IsGroundedBlend;
// Calculate base orientation for redirecting and handle smooth transitioning
float trDurMul = _play_TrDur.GetFloat();
if (_mainBlend < 0.001f) return;
// There calculate transitioning values and other main calculations
#region Define desired movement direction
if (useOverridingDirection)
{
if (overrideDirectionFadeSpeed < 0.0001f) overrideDirectionBlend = 1f;
else overrideDirectionBlend = Mathf.MoveTowards(overrideDirectionBlend, 1f, Owner.DeltaTime * overrideDirectionFadeSpeed);
}
else
{
if (overrideDirectionFadeSpeed < 0.0001f) overrideDirectionBlend = 0f;
else overrideDirectionBlend = Mathf.MoveTowards(overrideDirectionBlend, 0f, Owner.DeltaTime * overrideDirectionFadeSpeed);
}
//bool usesRigid = false;
//if (_play_UseRigid.GetBool())
//{
// if (LA.Rigidbody != null)
// {
// usesRigid = true;
// _calc_WorldDir = LA.Rigidbody.velocity.normalized;
// }
//}
Vector3 _src_worldDir;
if (_hash_zDir != -1)
{
_src_worldDir = new Vector3(LA.Mecanim.GetFloat(_hash_xDir), 0f, LA.Mecanim.GetFloat(_hash_zDir)).normalized;
}
else // Default use variable
{
_src_worldDir = LA.DesiredMovementDirection;
_src_worldDir.y = 0f;
if (_src_worldDir.magnitude < 0.1f) _src_worldDir = Vector3.zero;
}
_calc_WorldDir = _src_worldDir;
if (overrideDirectionBlend > 0.0001f)
{
if (overrideDirectionBlend >= 1f)
_calc_WorldDir = overridingDirection;
else
_calc_WorldDir = Vector3.Slerp(_calc_WorldDir, overridingDirection, overrideDirectionBlend);
}
#endregion
_calc_LocalDir = LA.ToRootLocalSpaceVec(_calc_WorldDir);
_var_raiseLimit = _play_LimitRaise.GetFloat();
_var_fixFeet = _play_FixFeet.GetFloat();
if (_calc_LocalDir.sqrMagnitude < 0.00001f) _localTargetAngle = 0f; // Reset Angle protection
_localTargetAngle = FEngineering.GetAngleRad(_calc_LocalDir.x, _calc_LocalDir.z);
float deltaMul;
if (trDurMul <= 0f) deltaMul = 1000f;
else deltaMul = 3f * Mathf.Lerp(5f, .5f, trDurMul / 0.6f);
_calc_deltaSpeed = LA.DeltaTime * deltaMul;
_calc_deltaSpeedSlow = LA.DeltaTime * (deltaMul * 0.6f);
_calc_smoothedTargetAngle = Mathf.LerpAngle(_calc_smoothedTargetAngle, _localTargetAngle, _calc_deltaSpeedSlow);
_calc_angleDiffFactor = Mathf.InverseLerp(0.0001f, 0.25f, Mathf.Abs((_localTargetAngle - _calc_smoothedTargetAngle) / Mathf.PI));
_localTargetAngle *= Mathf.Rad2Deg;
// +- 180 angle conversion
_wrappedAngle = FormatAngleToPM180(_localTargetAngle);
#region Smooth wrapped angle and keep correctness
_smoothedWrappedAngle = Mathf.LerpAngle(_smoothedWrappedAngle, _wrappedAngle, _calc_deltaSpeed * 1.25f);
_smoothedWrappedAngle = FormatAngleToPM180(_smoothedWrappedAngle);
if (_smoothedWrappedAngle < -179.9f && _wrappedAngle > 0f) _smoothedWrappedAngle = -_smoothedWrappedAngle;
if (_smoothedWrappedAngle > 179.9f && _wrappedAngle < 0f) _smoothedWrappedAngle = -_smoothedWrappedAngle;
#endregion
#region Side factors calculate
float targetSFactor;
float sideCalcAngle = _wrappedAngle;
if (_wrappedAngle < 90f) targetSFactor = Mathf.InverseLerp(0f, 90f, sideCalcAngle);
else targetSFactor = Mathf.InverseLerp(180f, 90f, sideCalcAngle);
_calc_sideFactorR = Mathf.Lerp(_calc_sideFactorR, targetSFactor, _calc_deltaSpeed * 2f);
if (_wrappedAngle > -90f) targetSFactor = Mathf.InverseLerp(0f, -90f, sideCalcAngle);
else targetSFactor = Mathf.InverseLerp(-180f, -90f, sideCalcAngle);
_calc_sideFactorL = Mathf.Lerp(_calc_sideFactorL, targetSFactor, _calc_deltaSpeed * 2f);
if (_wrappedAngle < 0f) _calc_sideFactor = _calc_sideFactorL;
else _calc_sideFactor = _calc_sideFactorR;
#endregion
#region Negative Progress (backwards movement)
_calc_toNegativeXProgress = 0f;
float negProgAngle = _wrappedAngle;
if (negProgAngle < -90f)
_calc_toNegativeXProgress = Mathf.InverseLerp(-90f, -135f, negProgAngle);
else if (negProgAngle > 90f)
_calc_toNegativeXProgress = Mathf.InverseLerp(90f, 135f, negProgAngle);
#endregion
// Provide orientation reference
_calc_LocalRotDir = Quaternion.Euler(0f, _localTargetAngle, 0f);
_wasUpdated = true;
}
public override void OnAfterAnimatorCaptureUpdate(LegsAnimatorCustomModuleHelper helper)
{
if (!_wasUpdated) return;
float smoother = _play_Smoother.GetFloat() + 1;
for (int l = 0; l < LA.Legs.Count; l++)
{
var leg = LA.Legs[l];
// Apply target ik mods for each leg IK
// Read base IK data
Vector3 finalIKPos = leg._AnimatorEndBonePos;
Vector3 local = LA.ToRootLocalSpace(finalIKPos);
LegRedirectHelper lHelper = legRedirectHelpers[leg.PlaymodeIndex];
/*Vector3 finalLocal = */
lHelper.ComputeIKOffset(local, smoother); // Compute position with use of leg helper
// Apply
Vector3 targetPos = lHelper.LastComputedWorldSpaceLegPos;
if (_mainBlend < 1f) targetPos = Vector3.LerpUnclamped(finalIKPos, targetPos, _mainBlend); // support blending
Vector3 ikOff = _calc_ikOff;
if (LA.Legs[l].Side == ELegSide.Left) ikOff.x = -ikOff.x;
else if (LA.Legs[l].Side == ELegSide.Right) ikOff.z = -ikOff.z;
ikOff = LA.RootToWorldSpaceVec(ikOff);
targetPos += ikOff;
// Override reference animator ankle position with computed one
// so legs animator will treat new position as animator pose
leg.OverrideAnimatorAnklePosition(targetPos);
//leg.OverrideFinalIKPos(targetPos);
//leg.OverrideControlPositionsWithCurrentIKState();
//leg.OverrideSourceIKPos();
}
}
#region Override Move Direction Switch Support
Vector3 overridingDirection = Vector3.zero;
bool useOverridingDirection = false;
[NonSerialized] public float overrideDirectionFadeSpeed = 6f;
float overrideDirectionBlend = 0f;
/// <summary> If you want to force module to apply different legs direction than automatically calculated directions (useful when using root motion velocity animations) </summary>
public void OverrideMoveDirection(Vector3? direction)
{
if (direction == null) useOverridingDirection = false;
else
{
useOverridingDirection = true;
overridingDirection = direction.Value;
}
}
#endregion
float _calc_lStretch = 0f;
float _calc_rStretch = 0f;
public override void OnLateUpdatePreApply(LegsAnimatorCustomModuleHelper helper)
{
if (!_wasUpdated) return;
// Hips Adjustements Compute
// Prepare factors
float redirect = _play_HipsRedir.GetFloat();
float footsRedir = _play_FeetRedir.GetFloat();
float kneesRedir = _play_KneesRedir.GetFloat();
float hipsStretchAdj = _play_AdjustStretch.GetFloat();
float angleABS = Mathf.Abs(_wrappedAngle);
float strafeBlendIn = Mathf.InverseLerp(0f, 45f, angleABS);
// Analyze legs state for redirector animation
#region Legs stretching check and adjustements
float angle = _wrappedAngle;
float angleFootOffset = angle; // Support run back 360 as -90 to 90
if (footsRedir > 0f) // Remapping angle for foots rotation
{
if (angle < -90f)
{
if (angle > -135f) angleFootOffset = Mathf.Lerp(-90f, 40f, Mathf.InverseLerp(-90f, -135f, angle));
else angleFootOffset = Mathf.Lerp(40f, 0f, Mathf.InverseLerp(-135f, -180f, angle));
}
else if (angle > 90f)
{
if (angle < 135) angleFootOffset = Mathf.Lerp(90f, -40f, Mathf.InverseLerp(90f, 135f, angle));
else angleFootOffset = Mathf.Lerp(-40f, 0, Mathf.InverseLerp(135f, 180f, angle));
}
}
Vector3 extraHipsOffset = Vector3.zero;
float lStretch = 0f;
float rStretch = 0f;
float backTo135 = 0f;
if (angleABS > 135f) backTo135 = Mathf.InverseLerp(180f, 135f, angleABS);
else backTo135 = Mathf.InverseLerp(90f, 135f, angleABS);
backTo135 = Mathf.Lerp(1f, -0.5f, backTo135);
for (int i = 0; i < LA.Legs.Count; i++)
{
var leg = LA.Legs[i];
// Offset Y foots rotation to match movement direction
if (footsRedir > 0f)
{
float footYRot = angleFootOffset;
float redir = 1f - footsRedir; redir = redir * redir * redir;
footYRot *= 1f - redir;
Quaternion rot = Quaternion.AngleAxis(footYRot * 0.8f * _mainBlend, LA.BaseTransform.up);
rot = legRedirectHelpers[i].FootRedirectSmoother(rot); // Smooth rotation offset
leg.OverrideFinalIKRot(rot * leg.GetFinalIKRot());
}
// Knees redirection extra rotation
if (kneesRedir > 0f)
{
if (leg.Side == ELegSide.Left)
{
leg.IKProcessor.StartBoneRotationOffset = Quaternion.Euler(0f, -_calc_sideFactorR * Mathf.Min(35f, 50f * kneesRedir) * backTo135, 0f);
}
else if (leg.Side == ELegSide.Right)
{
leg.IKProcessor.StartBoneRotationOffset = Quaternion.Euler(0f, -_calc_sideFactorL * Mathf.Min(35f, 50f * kneesRedir) * backTo135, 0f);
}
}
else leg.IKProcessor.StartBoneRotationOffset = Quaternion.identity;
// Leg stretch detecting to move hips towards non-stretched pose
if (hipsStretchAdj > 0.01f)
{
float stretch = leg.IKProcessor.GetStretchValue(legRedirectHelpers[i].LastComputedWorldSpaceLegPos);
if (stretch > 0.9f)
{
float blendIn = Mathf.InverseLerp(0.9f, 1.125f, stretch);
if (leg.Side == ELegSide.Left) lStretch += blendIn;
else rStretch += blendIn;
Vector3 diff = leg._PreviousFinalIKPos - LA.BaseTransform.position;
diff = LA.ToRootLocalSpaceVec(diff);
diff.y *= -0.8f;
diff = LA.RootToWorldSpaceVec(diff);
extraHipsOffset += diff * (blendIn * 1f);
//if (leg.Side == ELegSide.Left) stretchLR -= stretchDiff;
//else if (leg.Side == ELegSide.Right) stretchLR += stretchDiff;
}
}
}
#endregion
#region Compute angle 0 - 45 - 90 - 135 - 180 parts factors
Vector3 targetHipsRots = Animation360Angles.AnglesOn0;
Vector3 targetHipsPosOff = Animation360Angles.HipsOffsetOn0;
Vector3 targetIKPosOff = Animation360Angles.IKsOffsetOn0;
if (angleABS > 0f)
{
if (angleABS < 90f) // To 45
{
float sideFactor = InverseLerpDoubleSide(0f, 45f, angleABS, 90f);
LerpIt(ref targetHipsRots, Animation360Angles.AnglesOn45, sideFactor);
LerpIt(ref targetHipsPosOff, Animation360Angles.HipsOffsetOn45, sideFactor);
LerpIt(ref targetIKPosOff, Animation360Angles.IKsOffsetOn45, sideFactor);
}
if (angleABS > 45f && angleABS < 135f) // To 90
{
float sideFactor = InverseLerpDoubleSide(45f, 90f, angleABS, 135f);
LerpIt(ref targetHipsRots, Animation360Angles.AnglesOn90, sideFactor);
LerpIt(ref targetHipsPosOff, Animation360Angles.HipsOffsetOn90, sideFactor);
LerpIt(ref targetIKPosOff, Animation360Angles.IKsOffsetOn90, sideFactor);
}
if (angleABS > 90f) // To 135
{
float sideFactor = InverseLerpDoubleSide(90f, 135f, angleABS, 180f);
LerpIt(ref targetHipsRots, Animation360Angles.AnglesOn135, sideFactor);
LerpIt(ref targetHipsPosOff, Animation360Angles.HipsOffsetOn135, sideFactor);
LerpIt(ref targetIKPosOff, Animation360Angles.IKsOffsetOn135, sideFactor);
}
if (angleABS > 135f) // To 180
{
float sideFactor = Mathf.InverseLerp(135f, 180f, angleABS);
LerpIt(ref targetHipsRots, Animation360Angles.AnglesOn180, sideFactor);
LerpIt(ref targetHipsPosOff, Animation360Angles.HipsOffsetOn180, sideFactor);
LerpIt(ref targetIKPosOff, Animation360Angles.IKsOffsetOn180, sideFactor);
}
if (_wrappedAngle < 0f)
{
targetHipsRots.y = -targetHipsRots.y;
targetHipsRots.z = -targetHipsRots.z;
targetHipsPosOff.x = -targetHipsPosOff.x;
targetIKPosOff.z = -targetIKPosOff.z;
}
targetHipsRots *= redirect;
targetHipsPosOff *= 0.7f * redirect;
targetHipsRots = Vector3.Scale(targetHipsRots, User_MultiplyHipsOffsets);
}
targetIKPosOff *= redirect * _mainBlend * LA.ScaleReference;
_calc_ikOff = targetIKPosOff;
float hipsRotStretchMul = 0.25f + hipsStretchAdj * 0.75f;
_calc_lStretch = Mathf.Lerp(_calc_lStretch, lStretch, _calc_deltaSpeed);
_calc_rStretch = Mathf.Lerp(_calc_rStretch, rStretch, _calc_deltaSpeed);
targetHipsRots.y -= _calc_lStretch * User_StretchRotatorAnglePower * redirect * hipsRotStretchMul;
targetHipsRots.y += _calc_rStretch * User_StretchRotatorAnglePower * redirect * hipsRotStretchMul;
_calc_hipsRotationOffsets.x = Mathf.LerpAngle(_calc_hipsRotationOffsets.x, targetHipsRots.x, _calc_deltaSpeed);
_calc_hipsRotationOffsets.y = Mathf.LerpAngle(_calc_hipsRotationOffsets.y, targetHipsRots.y, _calc_deltaSpeed);
_calc_hipsRotationOffsets.z = Mathf.LerpAngle(_calc_hipsRotationOffsets.z, targetHipsRots.z, _calc_deltaSpeed);
_calc_hipsPositionOffsets = Vector3.Lerp(_calc_hipsPositionOffsets, targetHipsPosOff, _calc_deltaSpeed);
targetHipsPosOff *= User_StretchPositionMultiplier;
#endregion
Quaternion preSpineRot = Quaternion.identity;
float restoreSpn = _play_RestoreSpine.GetFloat();
if (SpineBone != null) preSpineRot = SpineBone.rotation; else restoreSpn = 0f;
// Hips Rotation Adjust Apply
Quaternion hipsRotTo = Quaternion.AngleAxis(_calc_hipsRotationOffsets.y * _mainBlend, LA.BaseTransform.up);
hipsRotTo *= Quaternion.AngleAxis(_calc_hipsRotationOffsets.z * _mainBlend, LA.BaseTransform.forward);
hipsRotTo *= Quaternion.AngleAxis(_calc_hipsRotationOffsets.x * _mainBlend, LA.BaseTransform.right);
LA.Hips.rotation = hipsRotTo * LA.Hips.rotation;
if (restoreSpn > 0f) SpineBone.rotation = Quaternion.Slerp(SpineBone.rotation, preSpineRot, Mathf.Lerp(1f, restoreSpn, _mainBlend));
_calc_hipsStretchOffset = Vector3.SmoothDamp(_calc_hipsStretchOffset, strafeBlendIn * extraHipsOffset, ref _sd_hipsStretchOff, 0.2f + 0.3f * _play_TrDur.GetFloat(), 100000f, LA.DeltaTime);
// Hips Position Adjust Apply
Vector3 finalHipsOffset = LA.RootToWorldSpaceVec(_calc_hipsPositionOffsets * 0.5f * LA.ScaleReference) * _mainBlend;
LA._Hips_Modules_ExtraWOffset += finalHipsOffset + (_calc_hipsStretchOffset * hipsStretchAdj * _mainBlend);
if (_play_reAdj.GetBool())
for (int l = 0; l < LA.Legs.Count; l++) // Re-adjust feet ik position to offsetted hips
{
//Vector3 ikOff = targetIKPosOff;
//if (LA.Legs[l].Side == ELegSide.Left) ikOff.x = -ikOff.x;
//ikOff = LA.RootToWorldSpaceVec(ikOff);
LA.Legs[l].OverrideFinalIKPos(LA.Legs[l].GetFinalIKPos() - finalHipsOffset);
}
}
public override void OnPostLateUpdate(LegsAnimatorCustomModuleHelper helper)
{
if (_mainBlend < 0.001f) return;
if (!_wasUpdated) return;
// Foots back run rotation fix
if (_var_fixFeet > 0f)
{
for (int i = 0; i < LA.Legs.Count; i++)
{
var leg = LA.Legs[i];
Quaternion ikRot = leg.IKProcessor.EndIKBone.transform.rotation;
Quaternion newRot = leg.IKProcessor.EndIKBone.transform.parent.rotation * leg.IKProcessor.EndIKBone.InitialLocalRotation;
newRot = Quaternion.LerpUnclamped(ikRot, newRot, (1f - leg.A_AligningHelperBlend) * (_var_fixFeet) * LA.IsMovingBlend * _calc_toNegativeXProgress);
leg.IKProcessor.EndIKBone.transform.rotation = newRot;
}
}
}
// Class for smooth calculating IK redirecting for a single leg
class LegRedirectHelper
{
#region Base Variables
LAM_DirectionalMovement parent;
LegsAnimator.Leg leg;
internal LegRedirectHelper oppositeHelper = null;
public LegRedirectHelper(LAM_DirectionalMovement parent, LegsAnimator.Leg leg)
{
this.parent = parent;
this.leg = leg;
LastComputedWorldSpaceLegPos = leg.BoneEnd.position;
computedPosLocal = leg.Owner.ToRootLocalSpace(LastComputedWorldSpaceLegPos);
}
LegsAnimator LA { get { return parent.LA; } }
public Vector3 LastComputedWorldSpaceLegPos { get; private set; }
#endregion
#region Main Calculations for a single leg
Vector3 computedPosLocal = Vector3.zero;
public Vector3 ComputeIKOffset(Vector3 localPos, float smoother = 1f)
{
float trDurMul = parent._play_TrDur.GetFloat();
Vector3 targetLPos = parent._calc_LocalRotDir * localPos;
float diff = Vector3.Magnitude(targetLPos - computedPosLocal);
float diffNormalized = diff / leg.Owner.ScaleReferenceNoScale;
float diffDelayer = 0f;
if (diff > 0.2f)
{
diffDelayer = Mathf.InverseLerp(0.2f, 1f, diff);
diffDelayer *= 0.1f;
if (trDurMul < 0.1f) diffDelayer *= trDurMul / 0.1f;
}
// Negative animation support
if (parent._calc_toNegativeXProgress > 0f)
{
Vector3 negativeLocalPos = localPos;
negativeLocalPos.x *= -1f;
Vector3 targetLPosNegative = parent._calc_LocalRotDir * negativeLocalPos;
targetLPos = Vector3.Lerp(targetLPos, targetLPosNegative, parent._calc_toNegativeXProgress);
}
float diffMargin = 0f;
if (smoother >= 3f)
{
}
else if (smoother > 0f)
{
if (diffNormalized < 0.1f / smoother)
computedPosLocal = targetLPos;
else
diffMargin = Mathf.InverseLerp(1.5f * smoother, 0.1f / smoother, diffNormalized) * 6f;
diffMargin = Mathf.Max(0f, diffMargin);
}
else
{
computedPosLocal = targetLPos;
}
computedPosLocal = Vector3.Lerp(computedPosLocal, targetLPos, LA.DeltaTime * (Mathf.Lerp(20f, 4f, (parent._calc_angleDiffFactor * trDurMul + diffDelayer) * 1.5f) + diffMargin));
computedPosLocal.y = Mathf.Lerp(computedPosLocal.y, targetLPos.y, 0.5f);
// Leg raise limiting on strafes and on backwards
if (parent._var_raiseLimit > 0f)
{
float floorY = leg.C_AnimatedAnkleFlatHeight;
float toFloorProgr = 0f;
if (leg.Side == ELegSide.Left) toFloorProgr = Mathf.Lerp(0f, 0.5f, parent._calc_sideFactorR);
else if (leg.Side == ELegSide.Right) toFloorProgr = Mathf.Lerp(0f, 0.5f, parent._calc_sideFactorL);
if (parent._calc_toNegativeXProgress > 0f)
{
toFloorProgr = Mathf.Lerp(toFloorProgr, 1f, parent._calc_toNegativeXProgress);
}
computedPosLocal.y = Mathf.Lerp(computedPosLocal.y, floorY, toFloorProgr * parent._var_raiseLimit);
}
LastComputedWorldSpaceLegPos = LA.RootToWorldSpace(computedPosLocal);
return computedPosLocal;
}
Quaternion _footRedirCache = Quaternion.identity;
internal Quaternion FootRedirectSmoother(Quaternion target)
{
_footRedirCache = Quaternion.Lerp(_footRedirCache, target, parent._calc_deltaSpeedSlow);
return _footRedirCache;
}
#endregion
}
#region Utilities
public static float FormatAngleToPM180(float angle)
{
float wrappedAngle = angle % 360f;
if (wrappedAngle > 180) wrappedAngle -= 360;
if (wrappedAngle < -180) wrappedAngle += 360;
return wrappedAngle;
}
static float InverseLerpDoubleSide(float from, float to, float t, float toRange)
{
if (t > to)
return Mathf.InverseLerp(toRange, to, t);
else
return Mathf.InverseLerp(from, to, t);
}
static void LerpIt(ref Vector3 val, Vector3 to, float t)
{
val = Vector3.LerpUnclamped(val, to, t);
}
#endregion
#region Editor Code
#if UNITY_EDITOR
public override void Editor_OnSceneGUI(LegsAnimator legsAnimator, LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
if (!Initialized) return;
//UnityEditor.Handles.Label(LA.Hips.position, "extra = " + extraOff);
//UnityEditor.Handles.Label(LA.Hips.position, "Local Dir = " + _calc_LocalRotDir.eulerAngles + "\nAngle: " + _localTargetAngle + "\nLocal: " + _calc_LocalDir + "\nWorld: " + _calc_WorldDir);
}
[HideInInspector] public bool InfoDisplay = true;
public override void Editor_InspectorGUI(LegsAnimator legsAnimator, LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
_useHelper = helper;
if (InfoDisplay)
{
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button(FGUI_Resources.GUIC_Info, FGUI_Resources.ButtonStyle, GUILayout.Width(26))) InfoDisplay = !InfoDisplay; GUILayout.Space(4);
EditorGUILayout.HelpBox("Reading Legs Animator's .MovementDirection variable to drive this module.By default it will use rigidbody velocity to do it, so you don't need to code anything.\nBut if you need to drive direction manually, Use legsAnim.User_SetDesiredMovementDirection... or use unity Animator variables (assign animator under Extra/Control and check bottom of this module GUI)", UnityEditor.MessageType.None);
EditorGUILayout.EndHorizontal();
}
GUILayout.Space(4);
if (!InfoDisplay)
{
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button(FGUI_Resources.GUIC_Info, EditorStyles.label, GUILayout.Width(22))) InfoDisplay = !InfoDisplay;
}
EditorGUILayout.LabelField("Redirecting Legs IKs to generate 360 degrees movement\nautomatically, with use of single walk/run animation", EditorStyles.centeredGreyMiniLabel, GUILayout.Height(24));
if (!InfoDisplay) EditorGUILayout.EndHorizontal();
GUILayout.Space(4);
LegsAnimator.Variable hipsRedirectV = HipsRedirVar;
if (!hipsRedirectV.TooltipAssigned) hipsRedirectV.AssignTooltip("Overall blend for hips rotation / position adjustement on different movement angles.");
hipsRedirectV.SetMinMaxSlider(0f, 1f);
hipsRedirectV.Editor_DisplayVariableGUI();
LegsAnimator.Variable footRedirectV = FeetRedirVar;
if (!footRedirectV.TooltipAssigned) footRedirectV.AssignTooltip("Rotating feet towards desired movement direction to match it.");
footRedirectV.SetMinMaxSlider(0f, 1f);
footRedirectV.Editor_DisplayVariableGUI();
LegsAnimator.Variable kneesRedirV = KneesRedirVar;
if (kneesRedirV.TooltipAssigned) kneesRedirV.AssignTooltip("Adjusting IK knees bend direction to match movement direction.");
kneesRedirV.SetMinMaxSlider(0f, 1f);
kneesRedirV.Editor_DisplayVariableGUI();
GUILayout.Space(4);
LegsAnimator.Variable limitLegRaiseV = LimitRaiseVar;
if (!limitLegRaiseV.TooltipAssigned) limitLegRaiseV.AssignTooltip("Limiting how hight legs should be raised. It can be helpful when some running animations tends to raise legs too high on running backwards.");
limitLegRaiseV.SetMinMaxSlider(0f, 0.3f);
limitLegRaiseV.Editor_DisplayVariableGUI();
FGUI_Inspector.DrawUILineCommon();
LegsAnimator.Variable durationV = TrDurationVar;
if (!durationV.TooltipAssigned) durationV.AssignTooltip("How rapidly the procedural adjustements should be executed.");
durationV.SetMinMaxSlider(0f, 0.6f);
durationV.Editor_DisplayVariableGUI();
//LegsAnimator.Variable testVar = helper.RequestVariable("Debug", Vector3.zero);
//testVar.VariableType = Variable.EVariableType.Vector3;
//testVar.Editor_DisplayVariableGUI();
//LegsAnimator.Variable testVar2 = helper.RequestVariable("Debug2", Vector3.zero);
//testVar2.VariableType = Variable.EVariableType.Vector3;
//testVar2.Editor_DisplayVariableGUI();
GUILayout.Space(4);
LegsAnimator.Variable fixFeetV = FixFeetVar;
if (!fixFeetV.TooltipAssigned) fixFeetV.AssignTooltip("Fixing feet rotation which happens during running backwards.");
fixFeetV.SetMinMaxSlider(0f, 1f);
fixFeetV.Editor_DisplayVariableGUI();
LegsAnimator.Variable adjStrV = AdjustStretchVar;
if (!adjStrV.TooltipAssigned) adjStrV.AssignTooltip("Adjusting hips position and rotation when legs redirecting causes too big offset of feet from the hips.");
adjStrV.SetMinMaxSlider(0f, 1f);
adjStrV.Editor_DisplayVariableGUI();
LegsAnimator.Variable restoreSpn = RestoreSpineVar;
if (!restoreSpn.TooltipAssigned) restoreSpn.AssignTooltip("Restoring spine rotation which is rotated by the hips rotation adjuster, to face head forward instead of desired direction.");
restoreSpn.SetMinMaxSlider(0f, 1f);
restoreSpn.Editor_DisplayVariableGUI();
LegsAnimator.Variable smoothV = ExtraSmootherVar;
if (!smoothV.TooltipAssigned) smoothV.AssignTooltip("Applying extra smoothing to the leg motion.");
smoothV.SetMinMaxSlider(-1f, 2f);
smoothV.Editor_DisplayVariableGUI();
LegsAnimator.Variable reAdjIK = ReAdjVar;
if (!reAdjIK.TooltipAssigned) reAdjIK.AssignTooltip("Re-adjusting resulting feet ik positions with the hips offset. Can improve hips offset feeling but can cause minimalistic feet jitters during movement gluing.");
reAdjIK.Editor_DisplayVariableGUI();
GUILayout.Space(4);
var la = helper.Parent;
if (!la) return;
//bool usingRigid = false;
//if (la.Rigidbody != null)
//{
// LegsAnimator.Variable rigidV = UseRigidVar;
// if (rigidV.Tooltip == "") rigidV.AssignTooltip("Using attached rigidbody velocity to define current desired movement direction for the module's algorithm. (no need for coding!)";
// rigidV.Editor_DisplayVariableGUI();
// usingRigid = rigidV.GetBool();
//}
FadeOffInAirVar.Editor_DisplayVariableGUI();
//if (!usingRigid)
bool usingAnimParams = false;
if (la.Mecanim != null)
{
if (Application.isPlaying) GUI.enabled = false;
//EditorGUILayout.HelpBox("You can use Animator's variable to drive the module movement direction", MessageType.None);
LegsAnimator.Variable xDirV = XDirAnimVarVar;
if (!xDirV.TooltipAssigned) xDirV.AssignTooltip("(Optional) Using unity animator's variable to define X world direction for this module's algorithm. (no need for Legs Animator module access through code)");
if (string.IsNullOrEmpty(xDirV.GetString()))
GUI.color = new Color(1f, 1f, 1f, 0.4f);
xDirV.Editor_DisplayVariableGUI();
if (xDirV.GetString() != "")
{
usingAnimParams = true;
LegsAnimator.Variable zDirV = ZDirAnimVarVar;
if (!zDirV.TooltipAssigned) zDirV.AssignTooltip("(Optional) Using unity animator's variable to define Z world direction for this module's algorithm.");
zDirV.Editor_DisplayVariableGUI();
}
GUI.color = Color.white;
}
if (!usingAnimParams)
{
if (la.Rigidbody)
EditorGUILayout.HelpBox("Module will use rigidbody velocity to drive legs direction", UnityEditor.MessageType.None);
else
EditorGUILayout.HelpBox("You can assign 'Rigidbody' under Extra/Control to drive legs direction automatically! Or use legsAnimator.User_SetDesiredMovementDirection...", UnityEditor.MessageType.None);
}
LAM_DirectionalMovement dirMovPlaymode = helper.PlaymodeModule as LAM_DirectionalMovement;
if (dirMovPlaymode == null) return;
if (dirMovPlaymode._wasUpdated == false) return;
EditorGUILayout.LabelField("Dir: " + dirMovPlaymode._localTargetAngle, EditorStyles.centeredGreyMiniLabel);
//if (Application.isPlaying) return;
//FGUI_Inspector.DrawUILineCommon(4);
//if (GUILayout.Button("Go to module file for specific animation settings!"))
//{
// Selection.activeObject = helper.ModuleReference;
//}
//EditorGUILayout.HelpBox("Some settings needs to be kept inside module preset file - not in legs animator module displayer.\nIf you need specific redirecting settings for different characters, you will need to create few files of Redirect Module!", MessageType.None);
}
#endif
#endregion
#region Inspector Editor Class Ineritance
#if UNITY_EDITOR
[UnityEditor.CanEditMultipleObjects]
[UnityEditor.CustomEditor(typeof(LAM_DirectionalMovement))]
public class LAM_DirectionalMovementEditor : LegsAnimatorControlModuleBaseEditor
{
}
#endif
#endregion
}
}

View File

@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 879c0a0b11bcd7042b0d1842a4ad0a95
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: a5d56514a4a6be14bb151cf321fe54ef, type: 3}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 154245
packageName: Legs Animator
packageVersion: 1.0.1
assetPath: Assets/FImpossible Creations/Plugins - Animating/Legs Animator/Core/LegsA
Control Modules/LAM_DirectionalMovement.cs
uploadId: 631264

View File

@@ -0,0 +1,134 @@
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
//[CreateAssetMenu]
public class LAM_EdgeStepDetector : LegsAnimatorControlModuleBase
{
LegsAnimator.Variable iterationsV;
float initTime;
public override void OnInit( LegsAnimator.LegsAnimatorCustomModuleHelper helper )
{
initTime = Time.time;
iterationsV = helper.RequestVariable( "Iterations", 5 );
}
public override void OnReInitialize( LegsAnimator.LegsAnimatorCustomModuleHelper helper )
{
initTime = Time.time;
}
public override void Leg_LatePreRaycastingUpdate( LegsAnimator.LegsAnimatorCustomModuleHelper helper, LegsAnimator.Leg leg )
{
if( Time.time - initTime < 0.1f ) return; // Don't calculate for a short time after init to let character be grounded
if( leg.User_RaycastHittedSource ) { leg.User_RestoreRaycasting(); return; } // Hitted - no need to find edge
// Calculating box for boxcast from hips towards leg to find any ground
Vector3 start = LegsAnim.ToRootLocalSpace( leg.ParentHub.LastKeyframePosition );
Vector3 end = LegsAnim.ToRootLocalSpace( leg.lastRaycastingOrigin );
start.y = end.y; // Same height origin as casting origin
start.z = end.z; // Same front / back position
RaycastHit hit = new RaycastHit();
float castLength = Vector3.Distance( leg.lastRaycastingOrigin, leg.lastRaycastingEndPoint );
#region Commented but maybe for future use
// Box cast is not a good choice
//start = LegsAnim.RootToWorldSpace( start );
//end = LegsAnim.RootToWorldSpace( end );
//Vector3 diff = end - start;
//float ext = diff.magnitude / 2f;
//Quaternion towards = Quaternion.LookRotation( diff, leg.Owner.Up );
//Vector3 mid = Vector3.LerpUnclamped( start, end, 0.5f );
//if( Physics.BoxCast( mid, new Vector3( leg.Owner.ScaleReference * 0.1f, 0.01f, ext ), -LegsAnim.Up, out hit, towards, castLength, LegsAnim.GroundMask, QueryTriggerInteraction.Ignore ) )
//{
// UnityEngine.Debug.DrawRay( hit.point, hit.normal, Color.green, 1.01f );
//}
#endregion
float iterations = (float)iterationsV.GetInt();
for( float i = 1f; i <= iterations; i += 1 )
{
Vector3 pos = Vector3.LerpUnclamped( end, start, 0.1f + ( i / iterations ) );
pos = LegsAnim.RootToWorldSpace( pos );
if( Physics.Raycast( pos, -LegsAnim.Up, out hit, castLength * 1.01f, LegsAnim.GroundMask, QueryTriggerInteraction.Ignore ) )
{
break;
}
}
if( hit.transform == null ) { leg.User_RestoreRaycasting(); return; }
//UnityEngine.Debug.DrawRay( hit.point, Vector3.down, Color.green, 1.01f );
leg.User_OverrideRaycastHit( hit, false );
}
#region Editor Code
#if UNITY_EDITOR
public override void Editor_InspectorGUI( LegsAnimator legsAnimator, LegsAnimator.LegsAnimatorCustomModuleHelper helper )
{
EditorGUILayout.HelpBox( "Raycasting ground from leg towards hips position when not found ground below default leg position", UnityEditor.MessageType.None );
if( legsAnimator.ZeroStepsOnNoRaycast )
{
GUILayout.Space( 4 );
EditorGUILayout.HelpBox( "You're using Zero Steps On No Raycast, disable it to make Edge Detector work", UnityEditor.MessageType.Warning );
var clickRect = GUILayoutUtility.GetLastRect();
if( GUI.Button( clickRect, GUIContent.none, EditorStyles.label ) ) { legsAnimator.ZeroStepsOnNoRaycast = false; UnityEditor.EditorUtility.SetDirty( legsAnimator ); }
GUILayout.Space( 4 );
}
LegsAnimator.Variable iterations = helper.RequestVariable( "Iterations", 5 );
iterations.SetMinMaxSlider( 2, 6 );
iterations.AssignTooltip( "How many raycasts from leg end towards hips should be casted to find ground in between" );
iterations.Editor_DisplayVariableGUI();
}
public override void Editor_OnSceneGUI( LegsAnimator legsAnimator, LegsAnimator.LegsAnimatorCustomModuleHelper helper )
{
if( LegsAnim.LegsInitialized == false ) return;
#region Commented but maybe for future use
//foreach( var leg in legsAnimator.Legs )
//{
// Vector3 start = LegsAnim.ToRootLocalSpace( leg.ParentHub.LastKeyframePosition );
// Vector3 end = LegsAnim.ToRootLocalSpace( leg.lastRaycastingOrigin );
// start.y = end.y; // Same height origin as casting origin
// start.z = end.z; // Same front / back position
// start = LegsAnim.RootToWorldSpace( start );
// end = LegsAnim.RootToWorldSpace( end );
// Vector3 diff = end - start;
// float ext = diff.magnitude / 2f;
// Quaternion towards = Quaternion.LookRotation( diff, leg.Owner.Up );
// Vector3 mid = Vector3.LerpUnclamped( start, end, 0.5f );
// Matrix4x4 rotMx = Matrix4x4.TRS( mid, towards, new Vector3( legsAnimator.ScaleReference * 0.1f, 0.01f, ext ) );
// Handles.matrix = rotMx;
// Handles.DrawWireCube( Vector3.zero, Vector3.one );
//}
#endregion
}
#endif
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e4f6cdb9d8a22f24d841c9e7236e2697
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: a5d56514a4a6be14bb151cf321fe54ef, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,406 @@
#if UNITY_EDITOR
using FIMSpace.FEditor;
using UnityEditor;
#endif
using System.Collections.Generic;
using UnityEngine;
using System;
namespace FIMSpace.FProceduralAnimation
{
//[CreateAssetMenu(fileName = "LAM_FadeLegOnAnimator", menuName = "FImpossible Creations/Legs Animator/LAM_FadeLegOnAnimator", order = 1)]
public class LAM_FadeLegOnAnimator : LegsAnimatorControlModuleBase
{
LegsAnimator.Variable _fadeSpeedV;
LegsAnimator.Variable _layerV;
LegsAnimator.Variable _idleGlueV;
float enabledMultiplier = 1f;
float sd_eneMul = 0f;
[NonSerialized] LegsAnimator.Leg[] legs; // I have no idea but unity keeps creating serialization cycle on this variable, if not using [NonSerialized] even when it's private variable
List<int> stateHashes;
List<int> tagHashes;
enum ELayerSelectMode { ByIndex, Auto }
LegsAnimator.Variable _layerMode;
LegsAnimator.Variable _layerSkip;
List<int> layersToCheck = null;
int lastAutoWeightIndex = 0;
#region Auto Layers Check Init
bool InitLayerCheck(LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
if (helper.Parent.Mecanim == null) return false;
if (_layerMode.GetInt() == 0) return false;
if (_layerMode == null || _layerSkip == null) return false;
layersToCheck = new List<int>();
string[] args = _layerSkip.GetString().Split(',');
for (int i = 0; i < helper.Parent.Mecanim.layerCount; i++) layersToCheck.Add(i);
for (int a = 0; a < args.Length; a++)
{
int parsed;
if (int.TryParse(args[a], out parsed))
{
layersToCheck.Remove(parsed);
}
else
{
int layerNameIndex = -1;
for (int i = 0; i < helper.Parent.Mecanim.layerCount; i++)
{
if (helper.Parent.Mecanim.GetLayerName(i) == args[a])
{
layerNameIndex = i;
break;
}
}
if (layerNameIndex != -1) layersToCheck.Remove(layerNameIndex);
}
}
return true;
}
#endregion
public override void OnInit(LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
if (LA.Mecanim == null)
{
Debug.Log("[Legs Animator] Fade On Animation Module: Not found animator reference in legs animator Extra/Control!");
helper.Enabled = false;
return;
}
_layerV = helper.RequestVariable("Animation Layer", 0);
_fadeSpeedV = helper.RequestVariable("Fade Speed", 0.75f);
_idleGlueV = helper.RequestVariable("Idle Glue During Fade", false);
var tagsV = helper.RequestVariable("Animation State Tag", "");
var statesV = helper.RequestVariable("Animation State Name", "");
// Prepare target animation hashes for quick checking animator state
string animStates = statesV.GetString();
//animStates = animStates.Replace(" ", "");
var statesSeparated = animStates.Split(',');
#region Prepare mecanim hashes
if (statesSeparated.Length > 0)
{
stateHashes = new List<int>();
for (int i = 0; i < statesSeparated.Length; i++)
{
if (string.IsNullOrWhiteSpace(statesSeparated[i])) continue;
stateHashes.Add(Animator.StringToHash(statesSeparated[i]));
}
}
string tagNames = tagsV.GetString();
//tagNames = tagNames.Replace(" ", "");
var tagsSeparated = tagNames.Split(',');
if (tagsSeparated.Length > 0)
{
tagHashes = new List<int>();
for (int i = 0; i < tagsSeparated.Length; i++)
{
if (string.IsNullOrWhiteSpace(tagsSeparated[i])) continue;
tagHashes.Add(Animator.StringToHash(tagsSeparated[i]));
}
}
if (stateHashes.Count == 0 && tagHashes.Count == 0)
{
helper.Enabled = false;
Debug.Log("[Legs Animator] Fade On Animation Module: No assigned animation state names/tags to control module on!");
return;
}
#endregion
if (helper.customStringList == null)
{
helper.Enabled = false;
Debug.Log("[Legs Animator] Fade On Animation Module: No legs definition!");
return;
}
// Prepare legs to work on
List<LegsAnimator.Leg> preLegs = new List<LegsAnimator.Leg>();
for (int i = 0; i < helper.customStringList.Count; i++)
{
if (helper.customStringList[i] == "1") preLegs.Add(LA.Legs[i]);
}
if (preLegs.Count == 0)
{
helper.Enabled = false;
Debug.Log("[Legs Animator] Fade On Animation Module: No legs definition!");
return;
}
legs = preLegs.ToArray();
if (_layerV.GetInt() < 0) _layerV.SetValue(0); if (_layerV.GetInt() > LA.Mecanim.layerCount - 1) _layerV.SetValue(LA.Mecanim.layerCount - 1);
// Auto Layers Check
_layerMode = helper.RequestVariable("Mode", 0);
_layerSkip = helper.RequestVariable("Skip", "");
if (_layerMode.GetInt() == 1)
{
if (InitLayerCheck(helper) == false) _layerMode.SetValue(0);
}
}
public override void OnAfterAnimatorCaptureUpdate(LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
Animator anim = LA.Mecanim;
if (anim == null) return;
int layer = _layerV.GetInt();
if (_layerMode.GetInt() == 1)
{
#region Auto Layer Check
float mostWeight = 0f;
int mostWeightI = -1;
for (int i = layersToCheck.Count-1; i >= 0; i--) // Reverse for to stop checking on 100% weight top layer
{
int idx = layersToCheck[i];
float weight = helper.Parent.Mecanim.GetLayerWeight(idx);
if (weight > 0.95f) // Dont check if layer has
{
mostWeightI = idx;
break;
}
else
{
if ( weight > mostWeight)
{
mostWeight = weight;
mostWeightI = idx;
}
}
}
layer = mostWeightI;
lastAutoWeightIndex = layer;
#endregion
}
AnimatorStateInfo animatorInfo = anim.IsInTransition(layer) ? anim.GetNextAnimatorStateInfo(layer) : anim.GetCurrentAnimatorStateInfo(layer);
bool fadeOut = false;
for (int n = 0; n < stateHashes.Count; n++)
{
if (animatorInfo.shortNameHash == stateHashes[n]) { fadeOut = true; break; }
}
if (!fadeOut)
{
for (int t = 0; t < tagHashes.Count; t++)
{
if (animatorInfo.tagHash == tagHashes[t]) { fadeOut = true; break; }
}
}
float fadeDur = 0.3f - _fadeSpeedV.GetFloat() * 0.299f;
if (fadeOut)
{
enabledMultiplier = Mathf.SmoothDamp(enabledMultiplier, -0.001f, ref sd_eneMul, fadeDur * 0.9f, 100000f, LA.DeltaTime);
}
else
{
enabledMultiplier = Mathf.SmoothDamp(enabledMultiplier, 1.01f, ref sd_eneMul, fadeDur, 100000f, LA.DeltaTime);
}
enabledMultiplier = Mathf.Clamp01((float)enabledMultiplier);
for (int l = 0; l < legs.Length; l++)
{
legs[l].InternalModuleBlendWeight = enabledMultiplier;
legs[l].IK_UpdateParamsBase();
}
if (_idleGlueV.GetBool())
{
if (enabledMultiplier < 0.5f)
{
LA._glueModeExecuted = LegsAnimator.EGlueMode.Idle;
}
}
}
#region Editor Code
#if UNITY_EDITOR
public override void Editor_InspectorGUI(LegsAnimator legsAnimator, LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
if (legsAnimator.Mecanim == null)
{
EditorGUILayout.HelpBox("Unity Animator Reference (Mecanim) is required by this module. Go to Extra/Control category and assign Mecanim reference there!", UnityEditor.MessageType.Warning);
if (GUILayout.Button("Go to Extra/Control")) { legsAnimator._EditorCategory = LegsAnimator.EEditorCategory.Extra; legsAnimator._EditorExtraCategory = LegsAnimator.EEditorExtraCategory.Control; }
}
EditorGUILayout.HelpBox("This module will help to disable legs animator motion, when playing special animations!\nUseful when using Legs Animator on insect creature which playes attack animations using front legs.", UnityEditor.MessageType.Info);
Animator anim = legsAnimator.Mecanim;
bool drawLayer = true;
if (anim)
{
if (anim.layerCount < 2) drawLayer = false;
}
if (drawLayer)
{
EditorGUILayout.BeginHorizontal();
EditorGUIUtility.labelWidth = 34;
var layerMode = helper.RequestVariable("Mode", 0);
if (Initialized) GUI.enabled = false;
ELayerSelectMode selMode = (ELayerSelectMode)layerMode.GetInt();
selMode = (ELayerSelectMode)EditorGUILayout.EnumPopup(new GUIContent("", "If layer to read animator state/tag from should be selected by index, or by top layer with biggest weight fade"), selMode, GUILayout.MaxWidth(74));
layerMode.SetValue((int)selMode);
GUI.enabled = true;
EditorGUIUtility.labelWidth = 40;
if (selMode == ELayerSelectMode.ByIndex)
{
GUILayout.Space(6);
var layerInd = helper.RequestVariable("Animation Layer", 0);
EditorGUIUtility.labelWidth = 42;
int indx = EditorGUILayout.IntField(new GUIContent("Index:", "Index to read animator state/tag from"), layerInd.GetInt());
if (indx < 0) indx = 0;
if (anim) if (indx > anim.layerCount - 1) indx = anim.layerCount - 1;
layerInd.SetValue(indx);
}
else
{
GUILayout.Space(6);
var skipVar = helper.RequestVariable("Skip", "");
EditorGUIUtility.labelWidth = 35;
string skip = skipVar.GetString();
if (Initialized) GUI.enabled = false;
skip = EditorGUILayout.TextField(new GUIContent("Skip:", "Write here indexes of upper body layers to skip checking them. You can also write here layer names. To skip multiple layers, use command ',' like: 3,4,6"), skip);
skipVar.SetValue(skip);
GUI.enabled = true;
}
EditorGUILayout.EndHorizontal();
EditorGUIUtility.labelWidth = 0;
if (selMode == ELayerSelectMode.Auto) EditorGUILayout.HelpBox("Automatic Layer: " + lastAutoWeightIndex, UnityEditor.MessageType.None);
}
#region Draw legs list
if (helper.customStringList == null) helper.customStringList = new List<string>();
var list = helper.customStringList;
int targetCount = legsAnimator.Legs.Count;
if (list.Count < targetCount)
while (list.Count < targetCount) list.Add("");
else
while (list.Count > targetCount) list.RemoveAt(list.Count - 1);
GUILayout.Space(5);
EditorGUILayout.LabelField("Select legs to DO FADE-OUT EFFECT ON:", EditorStyles.helpBox);
GUILayout.Space(3);
GUI.enabled = !legsAnimator.LegsInitialized;
for (int i = 0; i < list.Count; i++)
{
var boneStart = legsAnimator.Legs[i].BoneStart;
if (boneStart == null)
{
EditorGUILayout.LabelField("[" + (i + 1) + "] LEG LACKING BONE REFERENCES");
continue;
}
EditorGUILayout.BeginHorizontal();
var str = list[i];
bool target;
if (str.Length == 0 || str[0] != '1') target = false; else target = true;
target = EditorGUILayout.Toggle("[" + (i + 1) + "]: " + boneStart.name, target);
if (target == false)
list[i] = "0";
else
list[i] = "1";
EditorGUILayout.ObjectField(boneStart, typeof(Transform), true, GUILayout.Width(60));
EditorGUILayout.EndHorizontal();
}
GUI.enabled = true;
#endregion
GUILayout.Space(6);
var fadeSpd = helper.RequestVariable("Fade Speed", 0.75f);
fadeSpd.SetMinMaxSlider(0f, 1f);
fadeSpd.Editor_DisplayVariableGUI();
if (legsAnimator.UseGluing)
{
var idleglueV = helper.RequestVariable("Idle Glue During Fade", false);
if (!idleglueV.TooltipAssigned) idleglueV.AssignTooltip("Switch to idle glue mode during the fade - it can make possible slow-steps-moving when static animation is being played!");
idleglueV.Editor_DisplayVariableGUI();
}
GUILayout.Space(4);
FGUI_Inspector.DrawUILineCommon(8);
GUI.enabled = !legsAnimator.LegsInitialized;
EditorGUILayout.LabelField("Disable Legs On:", EditorStyles.centeredGreyMiniLabel);
var hipsVar = helper.RequestVariable("Animation State Tag", "");
hipsVar.Editor_DisplayVariableGUI();
GUILayout.Space(3);
var extraMultiplier = helper.RequestVariable("Animation State Name", "");
extraMultiplier.Editor_DisplayVariableGUI();
EditorGUILayout.LabelField("Use commas ',' to take into account multiple clips/tags", EditorStyles.centeredGreyMiniLabel);
GUILayout.Space(3);
GUI.enabled = true;
if (legsAnimator.LegsInitialized)
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
GUI.enabled = false;
EditorGUILayout.Slider("Current Weight: ", enabledMultiplier, 0f, 1f);
GUI.enabled = true;
EditorGUILayout.EndVertical();
}
}
#endif
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d1df4ac1577807249b2d20981c03f496
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: a5d56514a4a6be14bb151cf321fe54ef, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,68 @@
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
namespace FIMSpace.FProceduralAnimation
{
//[CreateAssetMenu(fileName = "LAM_HardGlueOnStop", menuName = "FImpossible Creations/Legs Animator/Control Module - Hard Glue On Stop", order = 1)]
public class LAM_HardGlueOnStop : LegsAnimatorControlModuleBase
{
public float FrontMargin = 0.3f;
public float ForceForSeconds = 0.6f;
LegsAnimator.Variable _beforeV;
public override void OnInit(LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
_beforeV = helper.RequestVariable("Hard Glue Before Move", 0.0f);
}
public override void OnPreLateUpdate(LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
if (!LA.IsMoving && LA.IsGrounded && LA.StoppedTime < ForceForSeconds)
{
for (int i = 0; i < LA.Legs.Count; i++)
{
LegsAnimator.Leg leg = LA.Legs[i];
// If leg is not behind character (swing back run) just front foot gluing
if (leg.AnkleH.LastKeyframeRootPos.z > -LA.ScaleReferenceNoScale * FrontMargin) leg.G_CustomForceAttach = true;
}
}
if (_beforeV.GetFloat() > 0f)
{
if (LA.IsMoving && LA.IsGrounded && LA.MovingTime < _beforeV.GetFloat())
for (int i = 0; i < LA.Legs.Count; i++)
{
LegsAnimator.Leg leg = LA.Legs[i];
// If leg is not stretching too much, then still hard glue it
if (leg.IKProcessor.GetStretchValue(leg.IKProcessor.IKTargetPosition) < 1.01f)
leg.G_CustomForceAttach = true;
}
}
}
#region Editor Code
#if UNITY_EDITOR
public override void Editor_InspectorGUI(LegsAnimator legsAnimator, LegsAnimator.LegsAnimatorCustomModuleHelper helper)
{
EditorGUILayout.HelpBox("Trying to quickly glue foot on ground when LegsAnimator IsMoving changes from true to false.", UnityEditor.MessageType.Info);
GUILayout.Space(5);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("(Optional)", EditorStyles.centeredGreyMiniLabel, GUILayout.Width(54));
var beforeV = helper.RequestVariable("Hard Glue Before Move", 0.0f);
beforeV.SetMinMaxSlider(0f, 0.3f);
if (!beforeV.TooltipAssigned) beforeV.AssignTooltip("(Optional Parameter) Keep feet glued on ground for a shorty moment before character's velocity builds up");
beforeV.Editor_DisplayVariableGUI();
EditorGUILayout.EndHorizontal();
GUILayout.Space(5);
}
#endif
#endregion
}
}

Some files were not shown because too many files have changed in this diff Show More