< Summary

Class:AvatarAnimatorLegacy
Assembly:AvatarShape
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Components/Avatar/AvatarAnimatorLegacy.cs
Covered lines:18
Uncovered lines:121
Coverable lines:139
Total lines:341
Line coverage:12.9% (18 of 139)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
AvatarAnimatorLegacy()0%110100%
Start()0%110100%
OnPoolGet()0%330100%
OnPoolRelease()0%12300%
Update()0%110100%
Update(...)0%8.744033.33%
UpdateInterface()0%12300%
State_Init(...)0%6200%
State_Ground(...)0%30500%
State_Air(...)0%12300%
State_Expression(...)0%30500%
SetExpressionValues(...)0%27.816015.38%
Reset()0%6200%
SetIdleFrame()0%2100%
PrepareLocomotionAnims(...)0%12300%
Prepare(...)0%6200%
PlayEmote(...)0%110100%
EquipEmote(...)0%6200%
UnequipEmote(...)0%6200%
InitializeAvatarAudioAndParticleHandlers(...)0%12300%
OnDestroy()0%2.152066.67%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Components/Avatar/AvatarAnimatorLegacy.cs

#LineLine coverage
 1using System;
 2using AvatarSystem;
 3using DCL;
 4using DCL.Components;
 5using DCL.Helpers;
 6using UnityEngine;
 7
 8public class AvatarAnimatorLegacy : MonoBehaviour, IPoolLifecycleHandler, IAnimator
 9{
 10    const float IDLE_TRANSITION_TIME = 0.2f;
 11    const float STRAFE_TRANSITION_TIME = 0.25f;
 12    const float RUN_TRANSITION_TIME = 0.15f;
 13    const float WALK_TRANSITION_TIME = 0.15f;
 14    const float JUMP_TRANSITION_TIME = 0.01f;
 15    const float FALL_TRANSITION_TIME = 0.5f;
 16    const float EXPRESSION_TRANSITION_TIME = 0.2f;
 17
 18    const float AIR_EXIT_TRANSITION_TIME = 0.2f;
 19    const float GROUND_BLENDTREE_TRANSITION_TIME = 0.15f;
 20
 21    const float RUN_SPEED_THRESHOLD = 0.05f;
 22    const float WALK_SPEED_THRESHOLD = 0.03f;
 23
 24    const float ELEVATION_OFFSET = 0.6f;
 25    const float RAY_OFFSET_LENGTH = 3.0f;
 26
 27    const float MAX_VELOCITY = 6.25f;
 28
 29    [System.Serializable]
 30    public class AvatarLocomotion
 31    {
 32        public AnimationClip idle;
 33        public AnimationClip walk;
 34        public AnimationClip run;
 35        public AnimationClip jump;
 36        public AnimationClip fall;
 37    }
 38
 39    [System.Serializable]
 40    public class BlackBoard
 41    {
 42        public float walkSpeedFactor;
 43        public float runSpeedFactor;
 44        public float movementSpeed;
 45        public float verticalSpeed;
 46        public bool isGrounded;
 47        public string expressionTriggerId;
 48        public long expressionTriggerTimestamp;
 49        public float deltaTime;
 50    }
 51
 52    [SerializeField] internal AvatarLocomotion femaleLocomotions;
 53    [SerializeField] internal AvatarLocomotion maleLocomotions;
 54    AvatarLocomotion currentLocomotions;
 55
 56    public new Animation animation;
 57    public BlackBoard blackboard;
 58    public Transform target;
 59
 133260    [SerializeField] float runMinSpeed = 6f;
 133261    [SerializeField] float walkMinSpeed = 0.1f;
 62
 63    internal System.Action<BlackBoard> currentState;
 64
 65    Vector3 lastPosition;
 66    bool isOwnPlayer = false;
 67    private AvatarAnimationEventHandler animEventHandler;
 68
 114869    public void Start() { OnPoolGet(); }
 70
 71    public void OnPoolGet()
 72    {
 57473        if (DCLCharacterController.i != null)
 74        {
 57475            isOwnPlayer = DCLCharacterController.i.transform == transform.parent;
 76
 77            // NOTE: disable MonoBehaviour's update to use DCLCharacterController event instead
 57478            this.enabled = !isOwnPlayer;
 79
 57480            if (isOwnPlayer)
 81            {
 57282                DCLCharacterController.i.OnUpdateFinish += Update;
 83            }
 84        }
 85
 57486        currentState = State_Init;
 57487    }
 88
 89    public void OnPoolRelease()
 90    {
 091        if (isOwnPlayer && DCLCharacterController.i)
 92        {
 093            DCLCharacterController.i.OnUpdateFinish -= Update;
 94        }
 095    }
 96
 697    void Update() { Update(Time.deltaTime); }
 98
 99    void Update(float deltaTime)
 100    {
 4979101        if (target == null || animation == null)
 4979102            return;
 103
 0104        blackboard.deltaTime = deltaTime;
 0105        UpdateInterface();
 0106        currentState?.Invoke(blackboard);
 0107    }
 108
 109    void UpdateInterface()
 110    {
 0111        Vector3 velocityTargetPosition = target.position;
 0112        Vector3 flattenedVelocity = velocityTargetPosition - lastPosition;
 113
 114        //NOTE(Brian): Vertical speed
 0115        float verticalVelocity = flattenedVelocity.y;
 0116        blackboard.verticalSpeed = verticalVelocity;
 117
 0118        flattenedVelocity.y = 0;
 119
 0120        if (isOwnPlayer)
 0121            blackboard.movementSpeed = flattenedVelocity.magnitude - DCLCharacterController.i.movingPlatformSpeed;
 122        else
 0123            blackboard.movementSpeed = flattenedVelocity.magnitude;
 124
 0125        Vector3 rayOffset = Vector3.up * RAY_OFFSET_LENGTH;
 126        //NOTE(Brian): isGrounded?
 0127        blackboard.isGrounded = Physics.Raycast(target.transform.position + rayOffset,
 128            Vector3.down,
 129            RAY_OFFSET_LENGTH - ELEVATION_OFFSET,
 130            DCLCharacterController.i.groundLayers);
 131
 132#if UNITY_EDITOR
 0133        Debug.DrawRay(target.transform.position + rayOffset, Vector3.down * (RAY_OFFSET_LENGTH - ELEVATION_OFFSET), blac
 134#endif
 135
 0136        lastPosition = velocityTargetPosition;
 0137    }
 138
 139    void State_Init(BlackBoard bb)
 140    {
 0141        if (bb.isGrounded == true)
 142        {
 0143            currentState = State_Ground;
 0144        }
 145        else
 146        {
 0147            currentState = State_Air;
 148        }
 0149    }
 150
 151    void State_Ground(BlackBoard bb)
 152    {
 0153        if (bb.deltaTime <= 0)
 154        {
 0155            Debug.LogError("deltaTime should be > 0", gameObject);
 0156            return;
 157        }
 158
 0159        animation[currentLocomotions.run.name].normalizedSpeed = bb.movementSpeed / bb.deltaTime * bb.runSpeedFactor;
 0160        animation[currentLocomotions.walk.name].normalizedSpeed = bb.movementSpeed / bb.deltaTime * bb.walkSpeedFactor;
 161
 0162        float movementSpeed = bb.movementSpeed / bb.deltaTime;
 163
 0164        if (movementSpeed > runMinSpeed)
 165        {
 0166            animation.CrossFade(currentLocomotions.run.name, RUN_TRANSITION_TIME);
 0167        }
 0168        else if (movementSpeed > walkMinSpeed)
 169        {
 0170            animation.CrossFade(currentLocomotions.walk.name, WALK_TRANSITION_TIME);
 0171        }
 172        else
 173        {
 0174            animation.CrossFade(currentLocomotions.idle.name, IDLE_TRANSITION_TIME);
 175        }
 176
 0177        if (!bb.isGrounded)
 178        {
 0179            currentState = State_Air;
 0180            Update(bb.deltaTime);
 181        }
 0182    }
 183
 184    void State_Air(BlackBoard bb)
 185    {
 0186        if (bb.verticalSpeed > 0)
 187        {
 0188            animation.CrossFade(currentLocomotions.jump.name, JUMP_TRANSITION_TIME, PlayMode.StopAll);
 0189        }
 190        else
 191        {
 0192            animation.CrossFade(currentLocomotions.fall.name, FALL_TRANSITION_TIME, PlayMode.StopAll);
 193        }
 194
 0195        if (bb.isGrounded)
 196        {
 0197            animation.Blend(currentLocomotions.jump.name, 0, AIR_EXIT_TRANSITION_TIME);
 0198            animation.Blend(currentLocomotions.fall.name, 0, AIR_EXIT_TRANSITION_TIME);
 0199            currentState = State_Ground;
 0200            Update(bb.deltaTime);
 201        }
 0202    }
 203
 204    internal void State_Expression(BlackBoard bb)
 205    {
 0206        var animationInfo = animation[bb.expressionTriggerId];
 0207        animation.CrossFade(bb.expressionTriggerId, EXPRESSION_TRANSITION_TIME, PlayMode.StopAll);
 208
 0209        var mustExit = Math.Abs(bb.movementSpeed) > Mathf.Epsilon || animationInfo.length - animationInfo.time < EXPRESS
 0210        if (mustExit)
 211        {
 0212            animation.Blend(bb.expressionTriggerId, 0, EXPRESSION_TRANSITION_TIME);
 0213            bb.expressionTriggerId = null;
 0214            if (!bb.isGrounded)
 0215                currentState = State_Air;
 216            else
 0217                currentState = State_Ground;
 218
 0219            Update(bb.deltaTime);
 0220        }
 221        else
 222        {
 0223            animation.Blend(bb.expressionTriggerId, 1, EXPRESSION_TRANSITION_TIME / 2f);
 224        }
 0225    }
 226
 227    public void SetExpressionValues(string expressionTriggerId, long expressionTriggerTimestamp)
 228    {
 2229        if (animation == null)
 2230            return;
 231
 0232        if (string.IsNullOrEmpty(expressionTriggerId))
 0233            return;
 234
 0235        var mustTriggerAnimation = !string.IsNullOrEmpty(expressionTriggerId) && blackboard.expressionTriggerTimestamp !
 236
 0237        blackboard.expressionTriggerId = expressionTriggerId;
 0238        blackboard.expressionTriggerTimestamp = expressionTriggerTimestamp;
 239
 0240        if (mustTriggerAnimation)
 241        {
 0242            if (!string.IsNullOrEmpty(expressionTriggerId))
 243            {
 0244                animation.Stop(expressionTriggerId);
 245            }
 246
 0247            currentState = State_Expression;
 0248            Update();
 249        }
 0250    }
 251
 252    public void Reset()
 253    {
 0254        if (animation == null)
 0255            return;
 256
 257        //It will set the animation to the first frame, but due to the nature of the script and its Update. It wont stop
 0258        animation.Stop();
 0259    }
 260
 0261    public void SetIdleFrame() { animation.Play(currentLocomotions.idle.name); }
 262
 263    public void PrepareLocomotionAnims(string bodyshapeId)
 264    {
 0265        if (bodyshapeId.Contains(WearableLiterals.BodyShapes.MALE))
 266        {
 0267            currentLocomotions = maleLocomotions;
 0268        }
 0269        else if (bodyshapeId.Contains(WearableLiterals.BodyShapes.FEMALE))
 270        {
 0271            currentLocomotions = femaleLocomotions;
 272        }
 273
 0274        EquipEmote(currentLocomotions.idle.name, currentLocomotions.idle);
 0275        EquipEmote(currentLocomotions.walk.name, currentLocomotions.walk);
 0276        EquipEmote(currentLocomotions.run.name, currentLocomotions.run);
 0277        EquipEmote(currentLocomotions.jump.name, currentLocomotions.jump);
 0278        EquipEmote(currentLocomotions.fall.name, currentLocomotions.fall);
 0279    }
 280
 281    // AvatarSystem entry points
 282    public bool Prepare(string bodyshapeId, GameObject container)
 283    {
 0284        if (!container.transform.TryFindChildRecursively("Armature", out Transform armature))
 285        {
 0286            Debug.LogError($"Couldn't find Armature for AnimatorLegacy in path: {transform.GetHierarchyPath()}");
 0287            return false;
 288        }
 0289        Transform armatureParent = armature.parent;
 0290        animation = armatureParent.gameObject.GetOrCreateComponent<Animation>();
 0291        armatureParent.gameObject.GetOrCreateComponent<StickerAnimationListener>();
 292
 0293        PrepareLocomotionAnims(bodyshapeId);
 0294        SetIdleFrame();
 0295        animation.Sample();
 0296        InitializeAvatarAudioAndParticleHandlers(animation);
 0297        return true;
 298    }
 299
 4300    public void PlayEmote(string emoteId, long timestamps) { SetExpressionValues(emoteId, timestamps); }
 301
 302    public void EquipEmote(string emoteId, AnimationClip clip)
 303    {
 0304        if (animation.GetClip(emoteId) != null)
 0305            animation.RemoveClip(emoteId);
 0306        animation.AddClip(clip, emoteId);
 0307    }
 308
 309    public void UnequipEmote(string emoteId)
 310    {
 0311        if (animation.GetClip(emoteId) == null)
 0312            return;
 0313        animation.RemoveClip(emoteId);
 0314    }
 315
 316    private void InitializeAvatarAudioAndParticleHandlers(Animation createdAnimation)
 317    {
 318        //NOTE(Mordi): Adds handler for animation events, and passes in the audioContainer for the avatar
 0319        AvatarAnimationEventHandler animationEventHandler = createdAnimation.gameObject.AddComponent<AvatarAnimationEven
 0320        AudioContainer audioContainer = transform.GetComponentInChildren<AudioContainer>();
 0321        if (audioContainer != null)
 322        {
 0323            animationEventHandler.Init(audioContainer);
 324
 325            //NOTE(Mordi): If this is a remote avatar, pass the animation component so we can keep track of whether it i
 0326            AvatarAudioHandlerRemote audioHandlerRemote = audioContainer.GetComponent<AvatarAudioHandlerRemote>();
 0327            if (audioHandlerRemote != null)
 328            {
 0329                audioHandlerRemote.Init(createdAnimation.gameObject);
 330            }
 331        }
 332
 0333        animEventHandler = animationEventHandler;
 0334    }
 335
 336    private void OnDestroy()
 337    {
 1329338        if (animEventHandler != null)
 0339            Destroy(animEventHandler);
 1329340    }
 341}