#if NotEditor #endif using System; using System.Collections.Generic; using System.Diagnostics; using UnityEngine; using UnityEngine.Animations; using UnityEngine.Playables; [AddComponentMenu("")] public class AnimationGraphBuilder : MonoBehaviour { public enum ParamInWrapper { None, Vanilla, Weapon, Attachments, Both } private Animator animator; private RuntimeAnimatorController vanillaRuntimeController; private Avatar vanillaAvatar; private PlayableGraph graph; private AnimationLayerMixerPlayable mixer; private AnimatorControllerPlayable weaponControllerPlayable; private AnimatorControllerPlayable vanillaControllerPlayable; private Avatar weaponAvatar; private AvatarMask weaponMask; private bool isFpv; private bool isLocalPlayer = true; private readonly List graphRelatedBehaviours = new List(); private AnimatorControllerParameter[] parameters; private readonly Dictionary paramMapping = new Dictionary(); private readonly Dictionary paramMappingDebug = new Dictionary(); //private Animator[] childAnimators = Array.Empty(); public bool HasWeaponOverride => graph.IsValid(); //public AnimatorControllerPlayable VanillaPlayable => vanillaControllerPlayable; //public AnimatorControllerPlayable WeaponPlayable => weaponControllerPlayable; public AnimatorControllerParameter[] Parameters => parameters; private AnimationTargetsAbs CurrentTarget { get; set; } public IAnimatorWrapper VanillaWrapper { get; private set; } public IAnimatorWrapper WeaponWrapper { get; private set; } public AttachmentWrapper AttachmentWrapper { get; private set; } public static IAnimatorWrapper DummyWrapper { get; } = new AnimatorWrapper(null); #if NotEditor public EntityPlayer Player { get; private set; } #endif private void Awake() { isFpv = transform.name == "baseRigFP"; animator = GetComponent(); animator.logWarnings = false; VanillaWrapper = new AnimatorWrapper(animator); WeaponWrapper = new AnimatorWrapper(null); parameters = animator.parameters; UpdateParamMapping(); vanillaAvatar = animator.avatar; #if NotEditor vanillaRuntimeController = isFpv ? SDCSUtils.FPAnimController : SDCSUtils.TPAnimController; isLocalPlayer = (Player = GetComponentInParent()) is EntityPlayerLocal; #else vanillaRuntimeController = animator.runtimeAnimatorController; #endif } private void UpdateParamMapping() { paramMapping.Clear(); paramMappingDebug.Clear(); var paramList = new List(); paramList.AddRange(animator.parameters); for (int i = 0; i < VanillaWrapper.GetParameterCount(); i++) { paramMapping.Add(VanillaWrapper.GetParameter(i).nameHash, ParamInWrapper.Vanilla); paramMappingDebug.Add(VanillaWrapper.GetParameter(i).name, ParamInWrapper.Vanilla); } if (WeaponWrapper != null && WeaponWrapper.IsValid) { for (int i = 0; i < WeaponWrapper.GetParameterCount(); i++) { var param = WeaponWrapper.GetParameter(i); if (paramMapping.ContainsKey(param.nameHash)) { paramMapping[param.nameHash] = ParamInWrapper.Both; paramMappingDebug[param.name] = ParamInWrapper.Both; } else { paramMapping.Add(param.nameHash, ParamInWrapper.Weapon); paramMappingDebug.Add(param.name, ParamInWrapper.Weapon); paramList.Add(param); } } } if (AttachmentWrapper != null && AttachmentWrapper.IsValid) { foreach (var animator in AttachmentWrapper.animators) { for (int i = 0; i < animator.parameterCount; i++) { var param = animator.GetParameter(i); if (!paramMapping.ContainsKey(param.nameHash)) { paramMapping[param.nameHash] = ParamInWrapper.Attachments; paramList.Add(param); } } } } parameters = paramList.ToArray(); } public ParamInWrapper GetWrapperRoleByParamHash(int nameHash) { if (paramMapping.TryGetValue(nameHash, out var role)) return role; return ParamInWrapper.None; } public ParamInWrapper GetWrapperRoleByParamName(string name) { if (string.IsNullOrEmpty(name) || !paramMapping.TryGetValue(Animator.StringToHash(name), out var role)) return ParamInWrapper.None; return role; } public ParamInWrapper GetWrapperRoleByParam(AnimatorControllerParameter param) { if (param == null || !paramMapping.TryGetValue(param.nameHash, out var role)) return ParamInWrapper.None; return role; } public void SetChildFloat(int nameHash, float value) { if (AttachmentWrapper != null && AttachmentWrapper.IsValid) { AttachmentWrapper.SetFloat(nameHash, value); } } public void SetChildBool(int nameHash, bool value) { if (AttachmentWrapper != null && AttachmentWrapper.IsValid) { AttachmentWrapper.SetBool(nameHash, value); } } public void SetChildInteger(int nameHash, int value) { if (AttachmentWrapper != null && AttachmentWrapper.IsValid) { AttachmentWrapper.SetInteger(nameHash, value); } } public void SetChildTrigger(int nameHash) { if (AttachmentWrapper != null && AttachmentWrapper.IsValid) { AttachmentWrapper.SetTrigger(nameHash); } } public void ResetChildTrigger(int nameHash) { if (AttachmentWrapper != null && AttachmentWrapper.IsValid) { AttachmentWrapper.ResetTrigger(nameHash); } } private void InitGraph() { animator.runtimeAnimatorController = null; if (graph.IsValid()) { return; } graph = PlayableGraph.Create(); mixer = AnimationLayerMixerPlayable.Create(graph, 2); var output = AnimationPlayableOutput.Create(graph, "output", animator); output.SetSourcePlayable(mixer); InitVanilla(); graph.Play(); } private void InitVanilla() { vanillaControllerPlayable = AnimatorControllerPlayable.Create(graph, vanillaRuntimeController); mixer.ConnectInput(0, vanillaControllerPlayable, 0, isFpv ? 0 : 1.0f); mixer.SetLayerAdditive(0, false); } public void InitWeapon(Transform weaponRoot, RuntimeAnimatorController weaponRuntimeController, AvatarMask weaponMask) { InitGraph(); InitBehaviours(weaponRoot); weaponAvatar = AvatarBuilder.BuildGenericAvatar(gameObject, "Origin"); animator.avatar = weaponAvatar; weaponControllerPlayable = AnimatorControllerPlayable.Create(graph, weaponRuntimeController); mixer.ConnectInput(1, weaponControllerPlayable, 0, 1.0f); mixer.SetLayerAdditive(1, false); if (weaponMask) { mixer.SetLayerMaskFromAvatarMask(1, weaponMask); } this.weaponMask = weaponMask; } private void DestroyGraph() { CleanupBehaviours(); weaponMask = null; if (graph.IsValid()) { graph.Destroy(); } if (animator) { animator.runtimeAnimatorController = vanillaRuntimeController; animator.avatar = vanillaAvatar; } } private void DestroyWeapon() { CleanupBehaviours(); weaponMask = null; if (weaponControllerPlayable.IsValid()) { mixer.DisconnectInput(1); weaponControllerPlayable.Destroy(); } animator.avatar = vanillaAvatar; Destroy(weaponAvatar); animator.runtimeAnimatorController = vanillaRuntimeController; } public void SetCurrentTarget(AnimationTargetsAbs target) { if (CurrentTarget == target) { UpdateChildAnimatorArray(target); return; } var sw = new Stopwatch(); sw.Start(); #if NotEditor bool wasCrouching = VanillaWrapper != null && VanillaWrapper.IsValid && VanillaWrapper.GetBool(AvatarController.isCrouchingHash); #endif //var rb = animator.transform.AddMissingComponent(); //rb.enabled = false; if (CurrentTarget) { CurrentTarget.SetEnabled(false); } bool useGraph = target && (target is PlayGraphTargets || (target.ItemTpv && !isFpv)); if (HasWeaponOverride) { if (useGraph) { DestroyWeapon(); } else { DestroyGraph(); } } CurrentTarget = target; if (target) { target.SetEnabled(true); } // Log.Out($"\n#=================Rebuild Start"); //#if NotEditor // Log.Out($"Remaining RigLayers on build:\n{string.Join("\n", rb.layers.Select(layer => layer.name))}"); //#endif // animator.UnbindAllSceneHandles(); // animator.UnbindAllStreamHandles(); // rb.enabled = true; // animator.Rebind(); // Log.Out($"#=================Rebuild Finish\n"); animator.WriteDefaultValues(); if (useGraph) { VanillaWrapper = new PlayableWrapper(vanillaControllerPlayable); WeaponWrapper = new PlayableWrapper(weaponControllerPlayable); } else { VanillaWrapper = new AnimatorWrapper(animator); if (target) { WeaponWrapper = new AnimatorWrapper(target.ItemAnimator); } else { WeaponWrapper = DummyWrapper; } } UpdateChildAnimatorArray(target); UpdateParamMapping(); #if NotEditor animator.SetWrappedBool(AvatarController.isCrouchingHash, wasCrouching); if (isFpv) { VanillaWrapper.Play("idle", 0, 0f); VanillaWrapper.SetInteger(AvatarController.weaponHoldTypeHash, -1); } if (wasCrouching && !isFpv && VanillaWrapper.GetLayerCount() > 4) { VanillaWrapper.Play("2HGeneric", 4, 0); } #endif sw.Stop(); Log.Out($"changing animation target to {(target ? target.name : "null")} took {sw.ElapsedMilliseconds}"); } private void UpdateChildAnimatorArray(AnimationTargetsAbs target) { Animator[] childAnimators; if (target && target.ItemCurrentOrDefault) { List animators = new List(); foreach (Transform trans in target.ItemCurrentOrDefault) { animators.AddRange(trans.GetComponentsInChildren()); } if (target.ItemAnimator) { animators.Remove(target.ItemAnimator); } childAnimators = animators.ToArray(); } else { childAnimators = Array.Empty(); } AttachmentWrapper = new AttachmentWrapper(childAnimators); } private void InitBehaviours(Transform weaponRoot) { foreach (var scripts in weaponRoot.GetComponents()) { var behaviour = scripts.Init(transform, isLocalPlayer); if (behaviour) { graphRelatedBehaviours.Add(behaviour); } } } private void CleanupBehaviours() { foreach (var behaviour in graphRelatedBehaviours) { if (behaviour) { (behaviour as IPlayableGraphRelated)?.Disable(transform); } } graphRelatedBehaviours.Clear(); } private void OnDisable() { SetCurrentTarget(null); } }