using System; using System.Collections.Generic; using Unity.BossRoom.Gameplay.GameplayObjects.AnimationCallbacks; using UnityEngine; namespace Unity.BossRoom.Gameplay.GameplayObjects.Character { /// /// Responsible for storing of all of the pieces of a character, and swapping the pieces out en masse when asked to. /// public class CharacterSwap : MonoBehaviour { [System.Serializable] public class CharacterModelSet { public GameObject ears; public GameObject head; public GameObject mouth; public GameObject hair; public GameObject eyes; public GameObject torso; public GameObject gearRightHand; public GameObject gearLeftHand; public GameObject handRight; public GameObject handLeft; public GameObject shoulderRight; public GameObject shoulderLeft; public GameObject handSocket; public AnimatorTriggeredSpecialFX specialFx; public AnimatorOverrideController animatorOverrides; // references a separate stand-alone object in the project private List m_CachedRenderers; public void SetFullActive(bool isActive) { ears.SetActive(isActive); head.SetActive(isActive); mouth.SetActive(isActive); hair.SetActive(isActive); eyes.SetActive(isActive); torso.SetActive(isActive); gearLeftHand.SetActive(isActive); gearRightHand.SetActive(isActive); handRight.SetActive(isActive); handLeft.SetActive(isActive); shoulderRight.SetActive(isActive); shoulderLeft.SetActive(isActive); } public List GetAllBodyParts() { if (m_CachedRenderers == null) { m_CachedRenderers = new List(); AddRenderer(ref m_CachedRenderers, ears); AddRenderer(ref m_CachedRenderers, head); AddRenderer(ref m_CachedRenderers, mouth); AddRenderer(ref m_CachedRenderers, hair); AddRenderer(ref m_CachedRenderers, torso); AddRenderer(ref m_CachedRenderers, gearRightHand); AddRenderer(ref m_CachedRenderers, gearLeftHand); AddRenderer(ref m_CachedRenderers, handRight); AddRenderer(ref m_CachedRenderers, handLeft); AddRenderer(ref m_CachedRenderers, shoulderRight); AddRenderer(ref m_CachedRenderers, shoulderLeft); } return m_CachedRenderers; } private void AddRenderer(ref List rendererList, GameObject bodypartGO) { if (!bodypartGO) { return; } var bodyPartRenderer = bodypartGO.GetComponent(); if (!bodyPartRenderer) { return; } rendererList.Add(bodyPartRenderer); } } [SerializeField] CharacterModelSet m_CharacterModel; public CharacterModelSet CharacterModel => m_CharacterModel; /// /// Reference to our shared-characters' animator. /// Can be null, but if so, animator overrides are not supported! /// [SerializeField] private Animator m_Animator; /// /// Reference to the original controller in our Animator. /// We switch back to this whenever we don't have an Override. /// private RuntimeAnimatorController m_OriginalController; [SerializeField] [Tooltip("Special Material we plug in when the local player is \"stealthy\"")] private Material m_StealthySelfMaterial; [SerializeField] [Tooltip("Special Material we plug in when another player is \"stealthy\"")] private Material m_StealthyOtherMaterial; public enum SpecialMaterialMode { None, StealthySelf, StealthyOther, } /// /// When we swap all our Materials out for a special material, /// we keep the old references here, so we can swap them back. /// private Dictionary m_OriginalMaterials = new Dictionary(); ClientCharacter m_ClientCharacter; void Awake() { m_ClientCharacter = GetComponentInParent(); m_Animator = m_ClientCharacter.OurAnimator; m_OriginalController = m_Animator.runtimeAnimatorController; } private void OnDisable() { // It's important that the original Materials that we pulled out of the renderers are put back. // Otherwise nothing will Destroy() them and they will leak! (Alternatively we could manually // Destroy() these in our OnDestroy(), but in this case it makes more sense just to put them back.) ClearOverrideMaterial(); } /// /// Swap the visuals of the character to the index passed in. /// /// Special Material to apply to all body parts public void SwapToModel(SpecialMaterialMode specialMaterialMode = SpecialMaterialMode.None) { ClearOverrideMaterial(); if (m_CharacterModel.specialFx) { m_CharacterModel.specialFx.enabled = true; } if (m_Animator) { // plug in the correct animator override... or plug the original non - overridden version back in! if (m_CharacterModel.animatorOverrides) { m_Animator.runtimeAnimatorController = m_CharacterModel.animatorOverrides; } else { m_Animator.runtimeAnimatorController = m_OriginalController; } } // lastly, now that we're all assembled, apply any override material. switch (specialMaterialMode) { case SpecialMaterialMode.StealthySelf: SetOverrideMaterial(m_StealthySelfMaterial); break; case SpecialMaterialMode.StealthyOther: SetOverrideMaterial(m_StealthyOtherMaterial); break; } } private void ClearOverrideMaterial() { foreach (var entry in m_OriginalMaterials) { if (entry.Key) { entry.Key.material = entry.Value; } } m_OriginalMaterials.Clear(); } private void SetOverrideMaterial(Material overrideMaterial) { ClearOverrideMaterial(); // just sanity-checking; this should already have been called! foreach (var bodyPart in m_CharacterModel.GetAllBodyParts()) { if (bodyPart) { m_OriginalMaterials[bodyPart] = bodyPart.material; bodyPart.material = overrideMaterial; } } } } }