/// --------------------------------------------- /// Ultimate Character Controller /// Copyright (c) Opsive. All Rights Reserved. /// https://www.opsive.com /// --------------------------------------------- namespace Opsive.UltimateCharacterController.Character { using Opsive.Shared.Events; using Opsive.Shared.Game; using Opsive.Shared.Utility; using Opsive.UltimateCharacterController.Items; using Opsive.UltimateCharacterController.Inventory; using Opsive.UltimateCharacterController.Utility; using Opsive.UltimateCharacterController.StateSystem; using UnityEngine; /// /// The AnimatorMonitor acts as a bridge for the parameters on the Animator component. /// If an Animator component is not attached to the character (such as for first person view) then the updates will be forwarded to the item's Animator. /// public class AnimatorMonitor : StateBehavior { #if UNITY_EDITOR [Tooltip("Should the Animator log any changes to the item parameters?")] [SerializeField] protected bool m_LogAbilityParameterChanges; [Tooltip("Should the Animator log any changes to the item parameters?")] [SerializeField] protected bool m_LogItemParameterChanges; [Tooltip("Should the Animator log any events that it sends?")] [SerializeField] protected bool m_LogEvents; #endif [Tooltip("The damping time for the Horizontal Movement parameter. The higher the value the slower the parameter value changes.")] [SerializeField] protected float m_HorizontalMovementDampingTime = 0.1f; [Tooltip("The damping time for the Forward Movement parameter. The higher the value the slower the parameter value changes.")] [SerializeField] protected float m_ForwardMovementDampingTime = 0.1f; [Tooltip("The damping time for the Pitch parameter. The higher the value the slower the parameter value changes.")] [SerializeField] protected float m_PitchDampingTime = 0.1f; [Tooltip("The damping time for the Yaw parameter. The higher the value the slower the parameter value changes.")] [SerializeField] protected float m_YawDampingTime = 0.1f; #if UNITY_EDITOR public bool LogEvents { get { return m_LogEvents; } } #endif public float HorizontalMovementDampingTime { get { return m_HorizontalMovementDampingTime; } set { m_HorizontalMovementDampingTime = value; } } public float ForwardMovementDampingTime { get { return m_ForwardMovementDampingTime; } set { m_ForwardMovementDampingTime = value; } } public float PitchDampingTime { get { return m_PitchDampingTime; } set { m_PitchDampingTime = value; } } public float YawDampingTime { get { return m_YawDampingTime; } set { m_YawDampingTime = value; } } private static int s_HorizontalMovementHash = Animator.StringToHash("HorizontalMovement"); private static int s_ForwardMovementHash = Animator.StringToHash("ForwardMovement"); private static int s_PitchHash = Animator.StringToHash("Pitch"); private static int s_YawHash = Animator.StringToHash("Yaw"); private static int s_SpeedHash = Animator.StringToHash("Speed"); private static int s_HeightHash = Animator.StringToHash("Height"); private static int s_MovingHash = Animator.StringToHash("Moving"); private static int s_AimingHash = Animator.StringToHash("Aiming"); private static int s_MovementSetIDHash = Animator.StringToHash("MovementSetID"); private static int s_AbilityIndexHash = Animator.StringToHash("AbilityIndex"); private static int s_AbilityChangeHash = Animator.StringToHash("AbilityChange"); private static int s_AbilityIntDataHash = Animator.StringToHash("AbilityIntData"); private static int s_AbilityFloatDataHash = Animator.StringToHash("AbilityFloatData"); private static int[] s_ItemSlotIDHash; private static int[] s_ItemSlotStateIndexHash; private static int[] s_ItemSlotStateIndexChangeHash; private static int[] s_ItemSlotSubstateIndexHash; protected GameObject m_GameObject; protected Transform m_Transform; protected Animator m_Animator; private float m_HorizontalMovement; private float m_ForwardMovement; private float m_Pitch; private float m_Yaw; private float m_Speed; private int m_Height; private bool m_Moving; private bool m_Aiming; private int m_MovementSetID; private int m_AbilityIndex; private int m_AbilityIntData; private float m_AbilityFloatData; private bool m_HasItemParameters; private int[] m_ItemSlotID; private int[] m_ItemSlotStateIndex; private int[] m_ItemSlotSubstateIndex; private Item[] m_EquippedItems; private bool m_EquippedItemsDirty; public bool AnimatorEnabled { get { return m_Animator != null && m_Animator.enabled; } } public bool FixedUpdateMode { get { return m_Animator != null && m_Animator.updateMode == AnimatorUpdateMode.Fixed; } } public float HorizontalMovement { get { return m_HorizontalMovement; } } public float ForwardMovement { get { return m_ForwardMovement; } } public float Pitch { get { return m_Pitch; } } public float Yaw { get { return m_Yaw; } } public float Speed { get { return m_Speed; } } public int Height { get { return m_Height; } } public bool Moving { get { return m_Moving; } } public bool Aiming { get { return m_Aiming; } } public int MovementSetID { get { return m_MovementSetID; } } public int AbilityIndex { get { return m_AbilityIndex; } } public bool AbilityChange { get { return (m_Animator != null) && m_Animator.GetBool(s_AbilityChangeHash); } } public int AbilityIntData { get { return m_AbilityIntData; } } public float AbilityFloatData { get { return m_AbilityFloatData; } } public bool HasItemParameters { get { return m_HasItemParameters; } } public int ParameterSlotCount { get { return m_ItemSlotID.Length; } } public int[] ItemSlotID { get { return m_ItemSlotID; } } public int[] ItemSlotStateIndex { get { return m_ItemSlotStateIndex; } } public int[] ItemSlotSubstateIndex { get { return m_ItemSlotSubstateIndex; } } [Snapshot] protected Item[] EquippedItems { get { return m_EquippedItems; } set { m_EquippedItems = value; } } /// /// Initialize the default values. /// protected override void Awake() { base.Awake(); m_GameObject = gameObject; m_Transform = transform; m_Animator = m_GameObject.GetCachedComponent(); #if UNITY_EDITOR // If the animator doesn't have the required parameters then it's not a valid animator. if (m_Animator != null) { if (!HasParameter(s_HorizontalMovementHash) || !HasParameter(s_ForwardMovementHash) || !HasParameter(s_AbilityChangeHash)) { Debug.LogError($"Error: The animator {m_Animator.name} is not designed to work with the Ultimate Character Controller. " + "Ensure the animator has all of the required parameters."); return; } } #endif InitializeItemParameters(); EventHandler.RegisterEvent(m_GameObject, "OnAbilityWillEquipItem", OnWillEquipItem); EventHandler.RegisterEvent(m_GameObject, "OnAbilityUnequipItemComplete", OnUnequipItem); EventHandler.RegisterEvent(m_GameObject, "OnInventoryRemoveItem", OnUnequipItem); if (m_Animator != null) { EventHandler.RegisterEvent(m_GameObject, "OnCharacterSnapAnimator", SnapAnimator); EventHandler.RegisterEvent(m_GameObject, "OnCharacterChangeUpdateLocation", OnChangeUpdateLocation); EventHandler.RegisterEvent(m_GameObject, "OnCharacterChangeTimeScale", OnChangeTimeScale); } } #if UNITY_EDITOR /// /// Does the animator have the specified parameter? /// /// The hash of the parameter. /// True if the animator has the specified parameter. private bool HasParameter(int parameterHash) { for (int i = 0; i < m_Animator.parameterCount; ++i) { if (m_Animator.parameters[i].nameHash == parameterHash) { return true; } } return false; } #endif /// /// Initializes the item parameters. /// public void InitializeItemParameters() { if (m_HasItemParameters) { return; } // The Animator Controller may not have the item parameters if the character can never equip an item. m_HasItemParameters = m_GameObject.GetComponentInChildren() != null; var inventory = m_GameObject.GetComponent(); if (inventory != null) { var slotCount = inventory.SlotCount; m_EquippedItems = new Item[slotCount]; m_ItemSlotID = new int[slotCount]; m_ItemSlotStateIndex = new int[slotCount]; m_ItemSlotSubstateIndex = new int[slotCount]; if (s_ItemSlotIDHash == null || s_ItemSlotIDHash.Length < slotCount) { s_ItemSlotIDHash = new int[slotCount]; s_ItemSlotStateIndexHash = new int[slotCount]; s_ItemSlotStateIndexChangeHash = new int[slotCount]; s_ItemSlotSubstateIndexHash = new int[slotCount]; for (int i = 0; i < slotCount; ++i) { s_ItemSlotIDHash[i] = Animator.StringToHash(string.Format("Slot{0}ItemID", i)); s_ItemSlotStateIndexHash[i] = Animator.StringToHash(string.Format("Slot{0}ItemStateIndex", i)); s_ItemSlotStateIndexChangeHash[i] = Animator.StringToHash(string.Format("Slot{0}ItemStateIndexChange", i)); s_ItemSlotSubstateIndexHash[i] = Animator.StringToHash(string.Format("Slot{0}ItemSubstateIndex", i)); } } } } /// /// Prepares the Animator parameters for start. /// protected virtual void Start() { SnapAnimator(); if (m_Animator != null) { var characterLocomotion = m_GameObject.GetCachedComponent(); OnChangeUpdateLocation(characterLocomotion.UpdateLocation == Game.KinematicObjectManager.UpdateLocation.FixedUpdate); OnChangeTimeScale(characterLocomotion.TimeScale); } } /// /// Snaps the animator to the default values. /// protected virtual void SnapAnimator() { // A first person view may not use an Animator. if (m_Animator != null) { // The values should be reset enabled so the animator will snap to the correct animation. m_Animator.SetFloat(s_HorizontalMovementHash, m_HorizontalMovement, 0, 0); m_Animator.SetFloat(s_ForwardMovementHash, m_ForwardMovement, 0, 0); m_Animator.SetFloat(s_PitchHash, m_Pitch, 0, 0); m_Animator.SetFloat(s_YawHash, m_Yaw, 0, 0); m_Animator.SetFloat(s_SpeedHash, m_Speed, 0, 0); m_Animator.SetFloat(s_HeightHash, m_Height, 0, 0); m_Animator.SetBool(s_MovingHash, m_Moving); m_Animator.SetBool(s_AimingHash, m_Aiming); m_Animator.SetInteger(s_MovementSetIDHash, m_MovementSetID); m_Animator.SetInteger(s_AbilityIndexHash, m_AbilityIndex); m_Animator.SetTrigger(s_AbilityChangeHash); m_Animator.SetInteger(s_AbilityIntDataHash, m_AbilityIntData); m_Animator.SetFloat(s_AbilityFloatDataHash, m_AbilityFloatData, 0, 0); if (m_HasItemParameters) { UpdateItemIDParameters(); for (int i = 0; i < m_EquippedItems.Length; ++i) { m_Animator.SetInteger(s_ItemSlotIDHash[i], m_ItemSlotID[i]); m_Animator.SetTrigger(s_ItemSlotStateIndexChangeHash[i]); m_Animator.SetInteger(s_ItemSlotStateIndexHash[i], m_ItemSlotStateIndex[i]); m_Animator.SetInteger(s_ItemSlotSubstateIndexHash[i], m_ItemSlotSubstateIndex[i]); } } EventHandler.ExecuteEvent(m_GameObject, "OnAnimatorWillSnap"); // Root motion should not move the character when snapping. var position = m_Transform.position; var rotation = m_Transform.rotation; // Update 0 will force the changes. m_Animator.Update(0); // Keep updating the Animator until it is no longer in a transition. This will snap the animator to the correct state immediately. while (IsInTrasition()) { m_Animator.Update(Time.fixedDeltaTime); } m_Animator.Update(0); // The animator should be positioned at the start of each state. for (int i = 0; i < m_Animator.layerCount; ++i) { m_Animator.Play(m_Animator.GetCurrentAnimatorStateInfo(i).fullPathHash, i, 0); } m_Animator.Update(Time.fixedDeltaTime); // Prevent the change parameters from staying triggered when the animator is on the idle state. SetAbilityChangeParameter(false); m_Transform.position = position; m_Transform.rotation = rotation; } // The item animators should also snap. if (m_EquippedItems != null) { for (int i = 0; i < m_EquippedItems.Length; ++i) { SetItemStateIndexChangeParameter(i, false); if (m_EquippedItems[i] != null) { m_EquippedItems[i].SnapAnimator(); } } } EventHandler.ExecuteEvent(m_GameObject, "OnAnimatorSnapped"); } /// /// Is the Animator Controller currently in a transition? /// /// True if any layer within the Animator Controller is within a transition. private bool IsInTrasition() { for (int i = 0; i < m_Animator.layerCount; ++i) { if (m_Animator.IsInTransition(i)) { return true; } } return false; } /// /// Returns true if the specified layer is in transition. /// /// The layer to determine if it is in transition. /// True if the specified layer is in transition. public bool IsInTransition(int layerIndex) { if (m_Animator == null) { return false; } return m_Animator.IsInTransition(layerIndex); } /// /// Sets the Horizontal Movement parameter to the specified value. /// /// The new value. /// The time scale of the character. public void SetHorizontalMovementParameter(float value, float timeScale) { SetHorizontalMovementParameter(value, timeScale, m_HorizontalMovementDampingTime); } /// /// Sets the Horizontal Movement parameter to the specified value. /// /// The new value. /// The time scale of the character. /// The time allowed for the parameter to reach the value. /// True if the parameter was changed. public virtual bool SetHorizontalMovementParameter(float value, float timeScale, float dampingTime) { var change = m_HorizontalMovement != value; if (change) { if (m_Animator != null) { m_Animator.SetFloat(s_HorizontalMovementHash, value, dampingTime, TimeUtility.DeltaTimeScaled / timeScale); m_HorizontalMovement = m_Animator.GetFloat(s_HorizontalMovementHash); if (Mathf.Abs(m_HorizontalMovement) < 0.001f) { m_HorizontalMovement = 0; } } else { m_HorizontalMovement = value; } } // The item's Animator should also be aware of the updated parameter value. if (m_EquippedItems != null) { for (int i = 0; i < m_EquippedItems.Length; ++i) { if (m_EquippedItems[i] != null) { m_EquippedItems[i].SetHorizontalMovementParameter(value, timeScale, dampingTime); } } } return change; } /// /// Sets the Forward Movement parameter to the specified value. /// /// The new value. /// The time scale of the character. public void SetForwardMovementParameter(float value, float timeScale) { SetForwardMovementParameter(value, timeScale, m_ForwardMovementDampingTime); } /// /// Sets the Forward Movement parameter to the specified value. /// /// The new value. /// The time scale of the character. /// The time allowed for the parameter to reach the value. /// True if the parameter was changed. public virtual bool SetForwardMovementParameter(float value, float timeScale, float dampingTime) { var change = m_ForwardMovement != value; if (change) { if (m_Animator != null) { m_Animator.SetFloat(s_ForwardMovementHash, value, dampingTime, TimeUtility.DeltaTimeScaled / timeScale); m_ForwardMovement = m_Animator.GetFloat(s_ForwardMovementHash); if (Mathf.Abs(m_ForwardMovement) < 0.001f) { m_ForwardMovement = 0; } } else { m_ForwardMovement = value; } } // The item's Animator should also be aware of the updated parameter value. if (m_EquippedItems != null) { for (int i = 0; i < m_EquippedItems.Length; ++i) { if (m_EquippedItems[i] != null) { m_EquippedItems[i].SetForwardMovementParameter(value, timeScale, dampingTime); } } } return change; } /// /// Sets the Pitch parameter to the specified value. /// /// The new value. /// True if the parameter was changed. public void SetPitchParameter(float value, float timeScale) { SetPitchParameter(value, timeScale, m_PitchDampingTime); } /// /// Sets the Pitch parameter to the specified value. /// /// The new value. /// The time allowed for the parameter to reach the value. /// The time scale of the character. /// True if the parameter was changed. public virtual bool SetPitchParameter(float value, float timeScale, float dampingTime) { var change = m_Pitch != value; if (change) { if (m_Animator != null) { m_Animator.SetFloat(s_PitchHash, value, dampingTime, TimeUtility.DeltaTimeScaled / timeScale); m_Pitch = m_Animator.GetFloat(s_PitchHash); if (Mathf.Abs(m_Pitch) < 0.001f) { m_Pitch = 0; } } else { m_Pitch = value; } } // The item's Animator should also be aware of the updated parameter value. if (m_EquippedItems != null) { for (int i = 0; i < m_EquippedItems.Length; ++i) { if (m_EquippedItems[i] != null) { m_EquippedItems[i].SetPitchParameter(value, timeScale, dampingTime); } } } return change; } /// /// Sets the Yaw parameter to the specified value. /// /// The new value. /// The time scale of the character. /// True if the parameter was changed. public void SetYawParameter(float value, float timeScale) { SetYawParameter(value, timeScale, m_YawDampingTime); } /// /// Sets the Yaw parameter to the specified value. /// /// The new value. /// The time scale of the character. /// The time allowed for the parameter to reach the value. /// True if the parameter was changed. public virtual bool SetYawParameter(float value, float timeScale, float dampingTime) { var change = m_Yaw != value; if (change) { if (m_Animator != null) { m_Animator.SetFloat(s_YawHash, value, dampingTime, TimeUtility.DeltaTimeScaled / timeScale); m_Yaw = m_Animator.GetFloat(s_YawHash); if (Mathf.Abs(m_Yaw) < 0.001f) { m_Yaw = 0; } } else { m_Yaw = value; } } // The item's Animator should also be aware of the updated parameter value. if (m_EquippedItems != null) { for (int i = 0; i < m_EquippedItems.Length; ++i) { if (m_EquippedItems[i] != null) { m_EquippedItems[i].SetYawParameter(value, timeScale, dampingTime); } } } return change; } /// /// Sets the Speed parameter to the specified value. /// /// The new value. /// The time scale of the character. public void SetSpeedParameter(float value, float timeScale) { SetSpeedParameter(value, timeScale, 0); } /// /// Sets the Speed parameter to the specified value. /// /// The new value. /// The time scale of the character. /// The time allowed for the parameter to reach the value. /// True if the parameter was changed. public virtual bool SetSpeedParameter(float value, float timeScale, float dampingTime) { var change = m_Speed != value; if (change) { if (m_Animator != null) { m_Animator.SetFloat(s_SpeedHash, value, dampingTime, TimeUtility.DeltaTimeScaled / timeScale); m_Speed = m_Animator.GetFloat(s_SpeedHash); if (Mathf.Abs(m_Speed) < 0.001f) { m_Speed = 0; } } else { m_Speed = value; } } // The item's Animator should also be aware of the updated parameter value. if (m_EquippedItems != null) { for (int i = 0; i < m_EquippedItems.Length; ++i) { if (m_EquippedItems[i] != null) { m_EquippedItems[i].SetSpeedParameter(value, timeScale, dampingTime); } } } return change; } /// /// Sets the Height parameter to the specified value. /// /// The new value. /// True if the parameter was changed. public virtual bool SetHeightParameter(int value) { var change = m_Height != value; if (change) { if (m_Animator != null) { m_Animator.SetFloat(s_HeightHash, value, 0, 0); m_Height = (int)m_Animator.GetFloat(s_HeightHash); if (Mathf.Abs(m_Height) < 0.001f) { m_Height = 0; } } else { m_Height = value; } } // The item's Animator should also be aware of the updated parameter value. if (m_EquippedItems != null) { for (int i = 0; i < m_EquippedItems.Length; ++i) { if (m_EquippedItems[i] != null) { m_EquippedItems[i].SetHeightParameter(value); } } } return change; } /// /// Sets the Moving parameter to the specified value. /// /// The new value. /// True if the parameter was changed. public virtual bool SetMovingParameter(bool value) { var change = m_Moving != value; if (change) { if (m_Animator != null) { m_Animator.SetBool(s_MovingHash, value); } m_Moving = value; } // The item's Animator should also be aware of the updated parameter value. if (m_EquippedItems != null) { for (int i = 0; i < m_EquippedItems.Length; ++i) { if (m_EquippedItems[i] != null) { m_EquippedItems[i].SetMovingParameter(value); } } } return change; } /// /// Sets the Aiming parameter to the specified value. /// /// The new value. /// True if the parameter was changed. public virtual bool SetAimingParameter(bool value) { var change = m_Aiming != value; if (change) { if (m_Animator != null) { m_Animator.SetBool(s_AimingHash, value); } m_Aiming = value; } // The item's Animator should also be aware of the updated parameter value. if (m_EquippedItems != null) { for (int i = 0; i < m_EquippedItems.Length; ++i) { if (m_EquippedItems[i] != null) { m_EquippedItems[i].SetAimingParameter(value); } } } return change; } /// /// Sets the Movement Set ID parameter to the specified value. /// /// The new value. /// True if the parameter was changed. public virtual bool SetMovementSetIDParameter(int value) { var change = m_MovementSetID != value; if (change) { if (m_Animator != null) { m_Animator.SetInteger(s_MovementSetIDHash, value); } m_MovementSetID = value; } // The item's Animator should also be aware of the updated parameter value. if (m_EquippedItems != null) { for (int i = 0; i < m_EquippedItems.Length; ++i) { if (m_EquippedItems[i] != null) { m_EquippedItems[i].SetMovementSetIDParameter(value); } } } return change; } /// /// Sets the Ability Index parameter to the specified value. /// /// The new value. /// True if the parameter was changed. public virtual bool SetAbilityIndexParameter(int value) { var change = m_AbilityIndex != value; if (change) { #if UNITY_EDITOR if (m_LogAbilityParameterChanges) { Debug.Log(Time.frameCount + " Changed AbilityIndex to " + value + "."); } #endif if (m_Animator != null) { m_Animator.SetInteger(s_AbilityIndexHash, value); SetAbilityChangeParameter(true); } m_AbilityIndex = value; } // The item's Animator should also be aware of the updated parameter value. if (m_EquippedItems != null) { for (int i = 0; i < m_EquippedItems.Length; ++i) { if (m_EquippedItems[i] != null) { m_EquippedItems[i].SetAbilityIndexParameter(value); } } } return change; } /// /// Sets the Ability Change parameter to the specified value. /// /// The new value. /// True if the parameter was changed. public virtual bool SetAbilityChangeParameter(bool value) { if (m_Animator != null && m_Animator.GetBool(s_AbilityChangeHash) != value) { if (value) { m_Animator.SetTrigger(s_AbilityChangeHash); } else { m_Animator.ResetTrigger(s_AbilityChangeHash); } return true; } return false; } /// /// Sets the Int Data parameter to the specified value. /// /// The new value. /// True if the parameter was changed. public virtual bool SetAbilityIntDataParameter(int value) { var change = m_AbilityIntData != value; if (change) { #if UNITY_EDITOR if (m_LogAbilityParameterChanges) { Debug.Log(Time.frameCount + " Changed AbilityIntData to " + value + "."); } #endif if (m_Animator != null) { m_Animator.SetInteger(s_AbilityIntDataHash, value); } m_AbilityIntData = value; } // The item's Animator should also be aware of the updated parameter value. if (m_EquippedItems != null) { for (int i = 0; i < m_EquippedItems.Length; ++i) { if (m_EquippedItems[i] != null) { m_EquippedItems[i].SetAbilityIntDataParameter(value); } } } return change; } /// /// Sets the Float Data parameter to the specified value. /// /// The new value. /// The time allowed for the parameter to reach the value. public void SetAbilityFloatDataParameter(float value, float timeScale) { SetAbilityFloatDataParameter(value, timeScale, 0); } /// /// Sets the Float Data parameter to the specified value. /// /// The new value. /// The time scale of the character. /// The time allowed for the parameter to reach the value. /// True if the parameter was changed. public virtual bool SetAbilityFloatDataParameter(float value, float timeScale, float dampingTime) { var change = m_AbilityFloatData != value; if (change) { if (m_Animator != null) { m_Animator.SetFloat(s_AbilityFloatDataHash, value, dampingTime, TimeUtility.DeltaTimeScaled / timeScale); m_AbilityFloatData = m_Animator.GetFloat(s_AbilityFloatDataHash); } else { m_AbilityFloatData = value; } } // The item's Animator should also be aware of the updated parameter value. if (m_EquippedItems != null) { for (int i = 0; i < m_EquippedItems.Length; ++i) { if (m_EquippedItems[i] != null) { m_EquippedItems[i].SetAbilityFloatDataParameter(value, timeScale, dampingTime); } } } return change; } /// /// Sets the Item ID parameter with the indicated slot to the specified value. /// /// The slot that the item occupies. /// The new value. public virtual bool SetItemIDParameter(int slotID, int value) { var change = m_ItemSlotID[slotID] != value; if (change) { #if UNITY_EDITOR if (m_LogItemParameterChanges) { Debug.Log(string.Format("{0} Changed Slot{1}ItemID to {2}.", Time.frameCount, slotID, value)); } #endif if (m_Animator != null) { m_Animator.SetInteger(s_ItemSlotIDHash[slotID], value); // Even though no state index was changed the trigger should be set to true so the animator can transition to the new item id. SetItemStateIndexChangeParameter(slotID, value != 0); } m_ItemSlotID[slotID] = value; } // The item's Animator should also be aware of the updated parameter value. if (m_EquippedItems != null) { for (int i = 0; i < m_EquippedItems.Length; ++i) { if (m_EquippedItems[i] != null) { m_EquippedItems[i].SetItemIDParameter(slotID, value); } } } return change; } /// /// Sets the Primary Item State Index parameter with the indicated slot to the specified value. /// /// The slot that the item occupies. /// The new value. /// True if the parameter was changed. public virtual bool SetItemStateIndexParameter(int slotID, int value) { var change = m_ItemSlotStateIndex[slotID] != value; if (change) { #if UNITY_EDITOR if (m_LogItemParameterChanges) { Debug.Log(string.Format("{0} Changed Slot{1}ItemStateIndex to {2}.", Time.frameCount, slotID, value)); } #endif if (m_Animator != null) { m_Animator.SetInteger(s_ItemSlotStateIndexHash[slotID], value); SetItemStateIndexChangeParameter(slotID, value != 0); } m_ItemSlotStateIndex[slotID] = value; } // The item's Animator should also be aware of the updated parameter value. if (m_EquippedItems != null) { for (int i = 0; i < m_EquippedItems.Length; ++i) { if (m_EquippedItems[i] != null) { m_EquippedItems[i].SetItemStateIndexParameter(slotID, value); } } } return change; } /// /// Sets the Item State Index Change parameter with the indicated slot to the specified value. /// /// The slot of that item that should be set. /// The new value. /// True if the parameter was changed. public virtual bool SetItemStateIndexChangeParameter(int slotID, bool value) { if (m_Animator != null && m_Animator.GetBool(s_ItemSlotStateIndexChangeHash[slotID]) != value) { if (value) { m_Animator.SetTrigger(s_ItemSlotStateIndexChangeHash[slotID]); } else { m_Animator.ResetTrigger(s_ItemSlotStateIndexChangeHash[slotID]); } return true; } return false; } /// /// Sets the Item Substate Index parameter with the indicated slot to the specified value. /// /// The slot that the item occupies. /// The new value. /// True if the parameter was changed. public virtual bool SetItemSubstateIndexParameter(int slotID, int value) { var change = m_ItemSlotSubstateIndex[slotID] != value; if (change) { #if UNITY_EDITOR if (m_LogItemParameterChanges) { Debug.Log(string.Format("{0} Changed Slot{1}ItemSubstateIndex to {2}.", Time.frameCount, slotID, value)); } #endif if (m_Animator != null) { m_Animator.SetInteger(s_ItemSlotSubstateIndexHash[slotID], value); } m_ItemSlotSubstateIndex[slotID] = value; } // The item's Animator should also be aware of the updated parameter value. if (m_EquippedItems != null) { for (int i = 0; i < m_EquippedItems.Length; ++i) { if (m_EquippedItems[i] != null) { m_EquippedItems[i].SetItemSubstateIndexParameter(slotID, value); } } } return change; } /// /// Executes an event on the EventHandler. /// /// The name of the event. public virtual void ExecuteEvent(string eventName) { #if UNITY_EDITOR if (m_LogEvents) { Debug.Log(string.Format("{0} Execute {1}", Time.frameCount, eventName)); } #endif EventHandler.ExecuteEvent(m_GameObject, eventName); } /// /// The specified item will be equipped. /// /// The item that will be equipped. /// The slot that the item will occupy. private void OnWillEquipItem(Item item, int slotID) { m_EquippedItems[slotID] = item; m_EquippedItemsDirty = true; } /// /// An item has been unequipped. /// /// The item that was unequipped. /// The slot that the item was unequipped from. private void OnUnequipItem(Item item, int slotID) { if (item != m_EquippedItems[slotID]) { return; } m_EquippedItems[slotID] = null; m_EquippedItemsDirty = true; } /// /// Updates the ItemID and MovementSetID parameters to the equipped items. /// public void UpdateItemIDParameters() { if (m_EquippedItemsDirty) { var movementSetID = 0; for (int i = 0; i < m_EquippedItems.Length; ++i) { var itemID = 0; if (m_EquippedItems[i] != null) { if (m_EquippedItems[i].DominantItem) { movementSetID = m_EquippedItems[i].AnimatorMovementSetID; } itemID = m_EquippedItems[i].AnimatorItemID; } SetItemIDParameter(i, itemID); } SetMovementSetIDParameter(movementSetID); m_EquippedItemsDirty = false; } } /// /// The character has changed between Update and FixedUpdate location. /// /// Should the Animator update within the FixedUpdate loop? private void OnChangeUpdateLocation(bool fixedUpdate) { m_Animator.updateMode = fixedUpdate ? AnimatorUpdateMode.Fixed : AnimatorUpdateMode.Normal; } /// /// The character's local timescale has changed. /// /// The new timescale. private void OnChangeTimeScale(float timeScale) { m_Animator.speed = timeScale; } /// /// Enables or disables the Animator. /// /// Should the animator be enabled? public void EnableAnimator(bool enable) { m_Animator.enabled = enable; } /// /// The GameObject has been destroyed. /// private void OnDestroy() { EventHandler.UnregisterEvent(m_GameObject, "OnAbilityWillEquipItem", OnWillEquipItem); EventHandler.UnregisterEvent(m_GameObject, "OnAbilityUnequipItemComplete", OnUnequipItem); EventHandler.UnregisterEvent(m_GameObject, "OnInventoryRemoveItem", OnUnequipItem); if (m_Animator != null) { EventHandler.UnregisterEvent(m_GameObject, "OnCharacterSnapAnimator", SnapAnimator); EventHandler.UnregisterEvent(m_GameObject, "OnCharacterChangeUpdateLocation", OnChangeUpdateLocation); EventHandler.UnregisterEvent(m_GameObject, "OnCharacterChangeTimeScale", OnChangeTimeScale); } } #if UNITY_2019_3_OR_NEWER /// /// Reset the static variables for domain reloading. /// [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void DomainReset() { s_ItemSlotIDHash = null; s_ItemSlotStateIndexHash = null; s_ItemSlotStateIndexChangeHash = null; s_ItemSlotSubstateIndexHash = null; } #endif } }