146 lines
6.6 KiB
C#
146 lines
6.6 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEditor.Animations;
|
|
using LinearAlgebra;
|
|
|
|
namespace CreatureControl {
|
|
|
|
public class CreatureAnimator : MonoBehaviour {
|
|
public Creature creature;
|
|
public RuntimeAnimatorController animatorController;
|
|
private Animator animator;
|
|
|
|
public enum RootMotionMode {
|
|
Normal,
|
|
Inverse
|
|
};
|
|
public RootMotionMode rootMotionMode = RootMotionMode.Normal;
|
|
|
|
[System.Serializable]
|
|
public class RootMotion {
|
|
public string clipName;
|
|
public Vector3 linearVelocity;
|
|
public Vector3 angularVelocity;
|
|
|
|
public readonly static RootMotion zero = new() {
|
|
linearVelocity = Vector3.zero,
|
|
angularVelocity = Vector3.zero
|
|
};
|
|
}
|
|
//public Dictionary<string, RootMotion> rootVelocities = new();
|
|
public List<RootMotion> rootVelocities = new();
|
|
public RootMotion GetRootMotion(string name) {
|
|
return this.rootVelocities.Find(rm => rm.clipName == name);
|
|
}
|
|
|
|
public Dictionary<Vector3, string> animationDirections = new();
|
|
public float rootMotionScaleForward = 1;
|
|
public float rootMotionScaleRight = 1;
|
|
public float rootMotionScaleRotate = 1;
|
|
|
|
public void CalculateRootMotionScale() {
|
|
this.rootMotionScaleForward = 0;
|
|
this.rootMotionScaleRight = 0;
|
|
this.rootMotionScaleRotate = 0;
|
|
// We may want to check for consistency:
|
|
// rm scale for an axis should be the same for all animations
|
|
foreach (RootMotion rootVelocity in this.rootVelocities) {
|
|
if (IsDirectionCloseTo(Vector3.forward, rootVelocity.linearVelocity)) {
|
|
if (rootVelocity.linearVelocity.z > 0)
|
|
this.rootMotionScaleForward = rootVelocity.linearVelocity.z;
|
|
}
|
|
else if (IsDirectionCloseTo(Vector3.right, rootVelocity.linearVelocity)) {
|
|
if (rootVelocity.linearVelocity.x > 0)
|
|
this.rootMotionScaleRight = rootVelocity.linearVelocity.x;
|
|
}
|
|
else if (IsDirectionCloseTo(Vector3.up, rootVelocity.angularVelocity)) {
|
|
if (rootVelocity.angularVelocity.y > 0)
|
|
this.rootMotionScaleRotate = rootVelocity.angularVelocity.y;
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool IsDirectionCloseTo(Vector3 direction, Vector3 vector, float threshold = 0.9f) {
|
|
if (vector.sqrMagnitude == 0)
|
|
return false;
|
|
// Normalize the target vector to ensure comparisons are based on direction
|
|
Vector3 normalizedTarget = vector.normalized;
|
|
return Vector3.Dot(normalizedTarget, direction) > threshold;
|
|
}
|
|
|
|
protected virtual void Start() {
|
|
this.creature = GetComponent<Creature>();
|
|
this.animator = creature.animator; // GetComponent<Animator>();
|
|
|
|
this.animator.applyRootMotion = this.rootMotionMode == RootMotionMode.Normal;
|
|
}
|
|
|
|
protected virtual void Update() {
|
|
if (rootMotionMode == RootMotionMode.Inverse) {
|
|
InverseRootMotionUpdate();
|
|
}
|
|
else {
|
|
this.creature.targetRig.transform.GetPositionAndRotation(out Vector3 targetRigPosition, out Quaternion targetRigOrientation);
|
|
this.creature.transform.SetPositionAndRotation(targetRigPosition, targetRigOrientation);
|
|
this.creature.targetRig.transform.SetPositionAndRotation(targetRigPosition, targetRigOrientation);
|
|
}
|
|
}
|
|
|
|
private Vector3 lastPosition;
|
|
private Quaternion lastOrientation;
|
|
protected void InverseRootMotionUpdate() {
|
|
if (lastPosition.sqrMagnitude > 0) {
|
|
Vector3 translation = this.transform.position - lastPosition;
|
|
Vector3 worldVelocity = translation / Time.deltaTime;
|
|
Vector3 localVelocity = transform.InverseTransformDirection(worldVelocity);
|
|
|
|
float fwdAnimationSpeed = localVelocity.z / rootMotionScaleForward;
|
|
this.animator.SetFloat("Forward", fwdAnimationSpeed);
|
|
float rightAnimationSpeed = localVelocity.x / rootMotionScaleRight;
|
|
this.animator.SetFloat("Right", rightAnimationSpeed);
|
|
|
|
Quaternion rotation = this.transform.rotation * Quaternion.Inverse(lastOrientation);
|
|
float rotationAngleY = Angles.Normalize(rotation.eulerAngles.y);
|
|
float rotationSpeed = rotationAngleY / Time.deltaTime;
|
|
float rotAnimationSpeed = rotationSpeed / rootMotionScaleRotate;
|
|
this.animator.SetFloat("Rotation", rotAnimationSpeed);
|
|
}
|
|
lastPosition = this.transform.position;
|
|
lastOrientation = this.transform.rotation;
|
|
}
|
|
|
|
void RetrieveBlendTreeAnimations(Animator animator, string parameterName, float targetValue) {
|
|
AnimatorController ac = animator.runtimeAnimatorController as AnimatorController;
|
|
// Check if the Animator Controller is valid
|
|
if (ac == null) {
|
|
Debug.LogError("Animator Controller is not set correctly.");
|
|
return;
|
|
}
|
|
|
|
// Iterate through each layer in the Animator Controller
|
|
foreach (AnimatorControllerLayer layer in ac.layers) {
|
|
// Access the state machine
|
|
foreach (ChildAnimatorState state in layer.stateMachine.states) {
|
|
// Check if the state contains a Blend Tree
|
|
if (state.state.motion is BlendTree blendTree) {
|
|
// Iterate through blend tree children
|
|
foreach (ChildMotion child in blendTree.children) {
|
|
if (child.directBlendParameter == parameterName) {
|
|
// Simulate parameter setting
|
|
//animator.SetFloat(parameterName, targetValue);
|
|
|
|
// Get the animation clip information
|
|
if (child.motion is AnimationClip clip) {
|
|
Debug.Log($"Animation Clip: {clip.name}, Speed: {child.timeScale}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
} |