/// --------------------------------------------- /// Ultimate Character Controller /// Copyright (c) Opsive. All Rights Reserved. /// https://www.opsive.com /// --------------------------------------------- namespace Opsive.UltimateCharacterController.ThirdPersonController.Items { using Opsive.Shared.Events; using Opsive.Shared.Game; using Opsive.Shared.Utility; using Opsive.UltimateCharacterController.Character; using Opsive.UltimateCharacterController.Items; using UnityEngine; /// /// Component which represents the item object actually rendererd. /// public class ThirdPersonPerspectiveItem : PerspectiveItem { [Tooltip("Should the Object object be spawned based on the character's humanoid bone?")] [SerializeField] protected bool m_UseParentHumanoidBone = true; [Tooltip("If using the humanoid bone, specifies which bone to use.")] [SerializeField] protected HumanBodyBones m_ParentHumanoidBone = HumanBodyBones.RightHand; [Tooltip("The location of the non-dominant hand which should be placed by the IK implementation.")] [SerializeField] protected Transform m_NonDominantHandIKTarget; [Tooltip("The location of the non-dominant hand hint which should be placed by the IK implementation.")] [SerializeField] protected Transform m_NonDominantHandIKTargetHint; [Tooltip("The transform that the item should be holstered to when unequipped.")] [SerializeField] protected Transform m_HolsterTarget; [Tooltip("The ID of the ObjectIdentifier component that the item should be holstered to when unequipped. This id will be used when holster target is null and the the ID is -1.")] [SerializeField] protected int m_HolsterID = -1; [NonSerialized] public Transform NonDominantHandIKTarget { get { return m_NonDominantHandIKTarget; } set { m_NonDominantHandIKTarget = value; } } [NonSerialized] public Transform NonDominantHandIKTargetHint { get { return m_NonDominantHandIKTargetHint; } set { m_NonDominantHandIKTargetHint = value; } } [NonSerialized] public Transform HolsterTarget { get { return m_HolsterTarget; } set { m_HolsterTarget = value; } } private CharacterIKBase m_CharacterIK; private Transform m_ParentBone; private Transform m_ObjectTransform; private Transform m_StartParentTransform; private Vector3 m_StartLocalPosition; private Quaternion m_StartLocalRotation; private bool m_PickedUp; public override bool FirstPersonItem { get { return false; } } /// /// Initialize the perspective item. /// /// The character GameObject that the item is parented to. /// True if the item was initialized successfully. public override bool Initialize(GameObject character) { if (!base.Initialize(character)) { return false; } m_CharacterIK = m_Character.GetCachedComponent(); if (m_Object != null) { m_ObjectTransform = m_Object.transform; m_StartParentTransform = m_ObjectTransform.parent; // Represents the Items GameObject. m_StartLocalPosition = m_ObjectTransform.localPosition; m_StartLocalRotation = m_ObjectTransform.localRotation; m_ParentBone = m_StartParentTransform.parent; // Represents the bone that the item is equipped to. } // If the holster ID isn't -1 then the HolsterTarget reference will contain the Transform that the item should be attached to. if (m_HolsterTarget == null && m_HolsterID != -1) { var objectIDs = m_Character.GetComponentsInChildren(); for (int i = 0; i < objectIDs.Length; ++i) { if (m_HolsterID == objectIDs[i].ID) { m_HolsterTarget = objectIDs[i].transform; break; } } } if (m_HolsterTarget != null) { // The holster target will be enabled when the item is picked up. m_HolsterTarget.gameObject.SetActive(false); } EventHandler.RegisterEvent(m_Character, "OnDeath", OnDeath); EventHandler.RegisterEvent(m_Character, "OnRespawn", OnRespawn); return true; } /// /// Returns the parent that the VisibleItem object should spawn at. /// /// The character that the item should spawn under. /// The character slot that the VisibleItem object should spawn under. /// Should the object be parented to the item slot ID? /// The parent that the VisibleItem object should spawn at. protected override Transform GetSpawnParent(GameObject character, int slotID, bool parentToItemSlotID) { Transform parent = null; // If using a humanoid bone then use the character's Animator to find the bone and then get the ItemPlacement component from that bone. if (m_UseParentHumanoidBone) { var characterAnimator = character.GetCachedComponent(); if (characterAnimator != null) { parent = characterAnimator.GetBoneTransform(m_ParentHumanoidBone); if (parent != null) { var itemSlot = parent.GetComponentInChildren(true); if (itemSlot != null) { parent = itemSlot.transform; } } } } // Fallback to using the ItemSlot ID. if (parent == null) { var itemSlots = character.GetComponentsInChildren(true); for (int i = 0; i < itemSlots.Length; ++i) { if (itemSlots[i].ID == slotID) { parent = itemSlots[i].transform; break; } } } return parent; } /// /// Is the VisibleItem active? /// /// True if the VisibleItem is active. public override bool IsActive() { // If a holster target is specified then the VisibleItem will never completely deactivate. Determine if it is active by the Transform parent. if (m_HolsterTarget != null) { return m_ObjectTransform.parent == m_StartParentTransform; } else { if (m_Object == null) { return m_Item.VisibleObjectActive; } return base.IsActive(); } } /// /// Activates or deactivates the VisibleItem. /// /// Should the VisibleItem be activated? public override void SetActive(bool active) { // If a holster target is specified then deactivating the VisibleItem will mean setting the parent transform of the object to that holster target. if (m_HolsterTarget != null) { if (active) { m_ObjectTransform.parent = m_StartParentTransform; m_ObjectTransform.localPosition = m_StartLocalPosition; m_ObjectTransform.localRotation = m_StartLocalRotation; } else { m_ObjectTransform.parent = m_HolsterTarget; m_ObjectTransform.localPosition = Vector3.zero; m_ObjectTransform.localRotation = Quaternion.identity; } } else if (m_Object != null) { // Allow the base object to activate or deactivate the actual object. base.SetActive(active); } // When the item activates or deactivates it should specify the IK target of the non-dominant hand (if any). if (m_CharacterIK != null) { m_CharacterIK.SetItemIKTargets(active ? m_ObjectTransform : null, m_ParentBone, active ? m_NonDominantHandIKTarget : null, active ? m_NonDominantHandIKTargetHint : null); } } /// /// The VisibleItem has been picked up by the character. /// public override void Pickup() { base.Pickup(); m_PickedUp = true; // The object should always be active if it is holstered. if (m_HolsterTarget != null) { m_Object.SetActive(true); // The holster target will be disabled until the character picks up the item. m_HolsterTarget.gameObject.SetActive(true); } } /// /// The item has been removed. /// public override void Remove() { base.Remove(); m_PickedUp = false; // The object should always be active if it is holstered. if (m_HolsterTarget != null) { m_HolsterTarget.gameObject.SetActive(false); } } /// /// The character has died. /// /// The position of the force. /// The amount of force which killed the character. /// The GameObject that killed the character. private void OnDeath(Vector3 position, Vector3 force, GameObject attacker) { // When the character dies disable the holster. Do not disable the item's object because that may not be activated again // when the character respawns, whereas the holster target should always be activated since it's an empty GameObject. if (m_HolsterTarget != null && m_PickedUp) { m_HolsterTarget.gameObject.SetActive(false); } } /// /// The character has respawned. /// private void OnRespawn() { if (m_HolsterTarget != null && m_PickedUp) { m_HolsterTarget.gameObject.SetActive(true); } } /// /// Called when the item is destroyed. /// private void OnDestroy() { if (m_Character == null) { return; } EventHandler.UnregisterEvent(m_Character, "OnDeath", OnDeath); EventHandler.UnregisterEvent(m_Character, "OnRespawn", OnRespawn); } } }