using UnityEngine; using System.Collections; using UnityEngine.Events; namespace RootMotion.Dynamics { /// /// The base abstract class for all Puppet Behaviours. /// public abstract class BehaviourBase : MonoBehaviour { /// /// Gets the PuppetMaster associated with this behaviour. Returns null while the behaviour is not initiated by the PuppetMaster. /// [HideInInspector] public PuppetMaster puppetMaster; public delegate void BehaviourDelegate(); public delegate void BehaviourUpdateDelegate(float deltaTime); public delegate void HitDelegate(MuscleHit hit); public delegate void CollisionDelegate(MuscleCollision collision); public abstract void OnReactivate(); public BehaviourDelegate OnPreActivate; public BehaviourDelegate OnPreInitiate; public BehaviourUpdateDelegate OnPreFixedUpdate; public BehaviourUpdateDelegate OnPreUpdate; public BehaviourUpdateDelegate OnPreLateUpdate; public BehaviourUpdateDelegate OnPreRead; public BehaviourUpdateDelegate OnPreWrite; public BehaviourDelegate OnPreDeactivate; public BehaviourDelegate OnPreFixTransforms; public HitDelegate OnPreMuscleHit; public CollisionDelegate OnPreMuscleCollision; public CollisionDelegate OnPreMuscleCollisionExit; public BehaviourDelegate OnHierarchyChanged; public virtual void Resurrect() { } public virtual void Freeze() { } public virtual void Unfreeze() { } public virtual void KillStart() { } public virtual void KillEnd() { } public virtual void OnTeleport(Quaternion deltaRotation, Vector3 deltaPosition, Vector3 pivot, bool moveToTarget) { } public virtual void OnMuscleDisconnected(Muscle m) { } public virtual void OnMuscleReconnected(Muscle m) { } public virtual void OnMuscleAdded(Muscle m) { if (OnHierarchyChanged != null) OnHierarchyChanged(); } public virtual void OnMuscleRemoved(Muscle m) { if (OnHierarchyChanged != null) OnHierarchyChanged(); } protected virtual void OnActivate() { } protected virtual void OnDeactivate() { } protected virtual void OnInitiate() { } protected virtual void OnFixedUpdate(float deltaTime) { } protected virtual void OnUpdate(float deltaTime) { } protected virtual void OnLateUpdate(float deltaTime) { } protected virtual void OnReadBehaviour(float deltaTime) { } protected virtual void OnWriteBehaviour(float deltaTime) { } protected virtual void OnDrawGizmosBehaviour() { } protected virtual void OnFixTransformsBehaviour() { } protected virtual void OnMuscleHitBehaviour(MuscleHit hit) { } protected virtual void OnMuscleCollisionBehaviour(MuscleCollision collision) { } protected virtual void OnMuscleCollisionExitBehaviour(MuscleCollision collision) { } public BehaviourDelegate OnPostActivate; public BehaviourDelegate OnPostInitiate; public BehaviourUpdateDelegate OnPostFixedUpdate; public BehaviourUpdateDelegate OnPostUpdate; public BehaviourUpdateDelegate OnPostLateUpdate; public BehaviourUpdateDelegate OnPostRead; public BehaviourUpdateDelegate OnPostWrite; public BehaviourDelegate OnPostDeactivate; public BehaviourDelegate OnPostDrawGizmos; public BehaviourDelegate OnPostFixTransforms; public HitDelegate OnPostMuscleHit; public CollisionDelegate OnPostMuscleCollision; public CollisionDelegate OnPostMuscleCollisionExit; [HideInInspector] public bool deactivated; public bool forceActive { get; protected set; } private bool initiated = false; public void Initiate() { initiated = true; if (OnPreInitiate != null) OnPreInitiate(); OnInitiate(); if (OnPostInitiate != null) OnPostInitiate(); } public void OnFixTransforms() { if (!initiated) return; if (!enabled) return; if (OnPreFixTransforms != null) OnPreFixTransforms(); OnFixTransformsBehaviour(); if (OnPostFixTransforms != null) OnPostFixTransforms(); } public void OnRead(float deltaTime) { if (!initiated) return; if (!enabled) return; if (OnPreRead != null) OnPreRead(deltaTime); OnReadBehaviour(deltaTime); if (OnPostRead != null) OnPostRead(deltaTime); } public void OnWrite(float deltaTime) { if (!initiated) return; if (!enabled) return; if (OnPreWrite != null) OnPreWrite(deltaTime); OnWriteBehaviour(deltaTime); if (OnPostWrite != null) OnPostWrite(deltaTime); } public void OnMuscleHit(MuscleHit hit) { if (!initiated) return; if (OnPreMuscleHit != null) OnPreMuscleHit(hit); OnMuscleHitBehaviour(hit); if (OnPostMuscleHit != null) OnPostMuscleHit(hit); } public void OnMuscleCollision(MuscleCollision collision) { if (!initiated) return; if (OnPreMuscleCollision != null) OnPreMuscleCollision(collision); OnMuscleCollisionBehaviour(collision); if (OnPostMuscleCollision != null) OnPostMuscleCollision(collision); } public void OnMuscleCollisionExit(MuscleCollision collision) { if (!initiated) return; if (OnPreMuscleCollisionExit != null) OnPreMuscleCollisionExit(collision); OnMuscleCollisionExitBehaviour(collision); if (OnPostMuscleCollisionExit != null) OnPostMuscleCollisionExit(collision); } public void Activate() { foreach (BehaviourBase b in puppetMaster.behaviours) { b.enabled = b == this; } if (OnPreActivate != null) OnPreActivate(); OnActivate(); if (OnPostActivate != null) OnPostActivate(); } void OnDisable() { if (!initiated) return; if (OnPreDeactivate != null) OnPreDeactivate(); OnDeactivate(); if (OnPostDeactivate != null) OnPostDeactivate(); } public void FixedUpdateB(float deltaTime) { if (!initiated) return; if (!enabled) return; if (puppetMaster.muscles.Length <= 0) return; if (OnPreFixedUpdate != null && enabled) OnPreFixedUpdate(deltaTime); OnFixedUpdate(deltaTime); if (OnPostFixedUpdate != null && enabled) OnPostFixedUpdate(deltaTime); } public void UpdateB(float deltaTime) { if (!initiated) return; if (!enabled) return; if (puppetMaster.muscles.Length <= 0) return; if (OnPreUpdate != null && enabled) OnPreUpdate(deltaTime); OnUpdate(deltaTime); if (OnPostUpdate != null && enabled) OnPostUpdate(deltaTime); } public void LateUpdateB(float deltaTime) { if (!initiated) return; if (!enabled) return; if (puppetMaster.muscles.Length <= 0) return; if (OnPreLateUpdate != null && enabled) OnPreLateUpdate(deltaTime); OnLateUpdate(deltaTime); if (OnPostLateUpdate != null && enabled) OnPostLateUpdate(deltaTime); } protected virtual void OnDrawGizmos() { if (!initiated) return; OnDrawGizmosBehaviour(); if (OnPostDrawGizmos != null) OnPostDrawGizmos(); } protected virtual string GetTypeSpring() { return typeSpringBase; } private const string typeSpringBase = "BehaviourBase"; /// /// Defines actions taken on certain events defined by the Puppet Behaviours. /// [System.Serializable] public struct PuppetEvent { [TooltipAttribute("Another Puppet Behaviour to switch to on this event. This must be the exact Type of the the Behaviour, careful with spelling.")] /// /// Another Puppet Behaviour to switch to on this event. This must be the exact Type of the the Behaviour, careful with spelling. /// public string switchToBehaviour; [TooltipAttribute("Animations to cross-fade to on this event. This is separate from the UnityEvent below because UnityEvents can't handle calls with more than one parameter such as Animator.CrossFade.")] /// /// Animations to cross-fade to on this event. This is separate from the UnityEvent below because UnityEvents can't handle calls with more than one parameter such as Animator.CrossFade. /// public AnimatorEvent[] animations; [TooltipAttribute("The UnityEvent to invoke on this event.")] /// /// The UnityEvent to invoke on this event. /// public UnityEvent unityEvent; public bool switchBehaviour { get { return switchToBehaviour != string.Empty && switchToBehaviour != empty; } } private const string empty = ""; public void Trigger(PuppetMaster puppetMaster, bool switchBehaviourEnabled = true) { unityEvent.Invoke(); foreach (AnimatorEvent animatorEvent in animations) animatorEvent.Activate(puppetMaster.targetAnimator, puppetMaster.targetAnimation); if (switchBehaviour) { bool found = false; foreach (BehaviourBase behaviour in puppetMaster.behaviours) { if (behaviour != null && behaviour.GetTypeSpring() == switchToBehaviour) { found = true; behaviour.Activate(); break; } } if (!found) { Debug.LogError("No Puppet Behaviour of type '" + switchToBehaviour + "' was found. Can not switch to the behaviour, please check the spelling (also for empty spaces)."); } } } } /// /// Cross-fades to an animation state. UnityEvent can not be used for cross-fading, it requires multiple parameters. /// [System.Serializable] public class AnimatorEvent { /// /// The name of the animation state /// public string animationState; /// /// The crossfading time /// public float crossfadeTime = 0.3f; /// /// The layer of the animation state (if using Legacy, the animation state will be forced to this layer) /// public int layer; /// /// Should the animation always start from 0 normalized time? /// public bool resetNormalizedTime; private const string empty = ""; // Activate the animation public void Activate(Animator animator, Animation animation) { if (animator != null) Activate(animator); if (animation != null) Activate(animation); } // Activate a Mecanim animation private void Activate(Animator animator) { if (animationState == empty) return; if (resetNormalizedTime) { if (crossfadeTime > 0f) animator.CrossFadeInFixedTime(animationState, crossfadeTime, layer, 0f); else animator.Play(animationState, layer, 0f); } else { if (crossfadeTime > 0f) { animator.CrossFadeInFixedTime(animationState, crossfadeTime, layer); } else animator.Play(animationState, layer); } } // Activate a Legacy animation private void Activate(Animation animation) { if (animationState == empty) return; if (resetNormalizedTime) animation[animationState].normalizedTime = 0f; animation[animationState].layer = layer; animation.CrossFade(animationState, crossfadeTime); } } protected void RotateTargetToRootMuscle() { Vector3 hipsForward = Quaternion.Inverse(puppetMaster.muscles[0].target.rotation) * puppetMaster.targetRoot.forward; Vector3 forward = puppetMaster.muscles[0].rigidbody.rotation * hipsForward; forward.y = 0f; puppetMaster.targetRoot.rotation = Quaternion.LookRotation(forward); } protected void TranslateTargetToRootMuscle(float maintainY) { puppetMaster.muscles[0].target.position = new Vector3( puppetMaster.muscles[0].transform.position.x, Mathf.Lerp(puppetMaster.muscles[0].transform.position.y, puppetMaster.muscles[0].target.position.y, maintainY), puppetMaster.muscles[0].transform.position.z); } protected void RemovePropMuscles() { while (ContainsRemovablePropMuscle()) { for (int i = 0; i < puppetMaster.muscles.Length; i++) { if (puppetMaster.muscles[i].props.group == Muscle.Group.Prop && !puppetMaster.muscles[i].isPropMuscle) { puppetMaster.RemoveMuscleRecursive(puppetMaster.muscles[i].joint, true); break; } } } } protected virtual void GroundTarget(LayerMask layers) { Ray ray = new Ray(puppetMaster.targetRoot.position + puppetMaster.targetRoot.up, -puppetMaster.targetRoot.up); RaycastHit hit; if (Physics.Raycast(ray, out hit, 4f, layers)) { if (!float.IsNaN(hit.point.x) && !float.IsNaN(hit.point.y) && !float.IsNaN(hit.point.z)) { puppetMaster.targetRoot.position = hit.point; } else { Debug.LogWarning("Raycasting against a large collider has produced a NaN hit point.", transform); } } } protected bool ContainsRemovablePropMuscle() { foreach (Muscle m in puppetMaster.muscles) { if (m.props.group == Muscle.Group.Prop && !m.isPropMuscle) return true; } return false; } } }