382 lines
12 KiB
C#
382 lines
12 KiB
C#
#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<MonoBehaviour> graphRelatedBehaviours = new List<MonoBehaviour>();
|
|
private AnimatorControllerParameter[] parameters;
|
|
private readonly Dictionary<int, ParamInWrapper> paramMapping = new Dictionary<int, ParamInWrapper>();
|
|
private readonly Dictionary<string, ParamInWrapper> paramMappingDebug = new Dictionary<string, ParamInWrapper>();
|
|
//private Animator[] childAnimators = Array.Empty<Animator>();
|
|
|
|
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>();
|
|
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<EntityPlayer>()) is EntityPlayerLocal;
|
|
#else
|
|
vanillaRuntimeController = animator.runtimeAnimatorController;
|
|
#endif
|
|
}
|
|
|
|
private void UpdateParamMapping()
|
|
{
|
|
paramMapping.Clear();
|
|
paramMappingDebug.Clear();
|
|
var paramList = new List<AnimatorControllerParameter>();
|
|
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<RigBuilder>();
|
|
//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<Animator> animators = new List<Animator>();
|
|
foreach (Transform trans in target.ItemCurrentOrDefault)
|
|
{
|
|
animators.AddRange(trans.GetComponentsInChildren<Animator>());
|
|
}
|
|
if (target.ItemAnimator)
|
|
{
|
|
animators.Remove(target.ItemAnimator);
|
|
}
|
|
childAnimators = animators.ToArray();
|
|
}
|
|
else
|
|
{
|
|
childAnimators = Array.Empty<Animator>();
|
|
}
|
|
AttachmentWrapper = new AttachmentWrapper(childAnimators);
|
|
}
|
|
|
|
private void InitBehaviours(Transform weaponRoot)
|
|
{
|
|
foreach (var scripts in weaponRoot.GetComponents<IPlayableGraphRelated>())
|
|
{
|
|
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);
|
|
}
|
|
}
|