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}");
}
}
}
}
}
}
}
}
}