< 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:114
Coverable lines:132
Total lines:337
Line coverage:13.6% (18 of 132)
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%
BindBodyShape(...)0%30500%
Prepare(...)0%6200%
PlayExpression(...)0%110100%
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 BaseClipsIds
 31    {
 32        public string idle;
 33        public string walk;
 34        public string run;
 35        public string jump;
 36        public string 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 AvatarAnimationsVariable maleAnimations;
 53    [SerializeField] internal AvatarAnimationsVariable femaleAnimations;
 54
 55    public new Animation animation;
 56    public BaseClipsIds baseClipsIds;
 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    AvatarAnimationsVariable currentAnimations;
 67    bool isOwnPlayer = false;
 68    private AvatarAnimationEventHandler animEventHandler;
 69
 114670    public void Start() { OnPoolGet(); }
 71
 72    public void OnPoolGet()
 73    {
 57374        if (DCLCharacterController.i != null)
 75        {
 57376            isOwnPlayer = DCLCharacterController.i.transform == transform.parent;
 77
 78            // NOTE: disable MonoBehaviour's update to use DCLCharacterController event instead
 57379            this.enabled = !isOwnPlayer;
 80
 57381            if (isOwnPlayer)
 82            {
 57183                DCLCharacterController.i.OnUpdateFinish += Update;
 84            }
 85        }
 86
 57387        currentState = State_Init;
 57388    }
 89
 90    public void OnPoolRelease()
 91    {
 092        if (isOwnPlayer && DCLCharacterController.i)
 93        {
 094            DCLCharacterController.i.OnUpdateFinish -= Update;
 95        }
 096    }
 97
 698    void Update() { Update(Time.deltaTime); }
 99
 100    void Update(float deltaTime)
 101    {
 6002102        if (target == null || animation == null)
 6002103            return;
 104
 0105        blackboard.deltaTime = deltaTime;
 0106        UpdateInterface();
 0107        currentState?.Invoke(blackboard);
 0108    }
 109
 110    void UpdateInterface()
 111    {
 0112        Vector3 velocityTargetPosition = target.position;
 0113        Vector3 flattenedVelocity = velocityTargetPosition - lastPosition;
 114
 115        //NOTE(Brian): Vertical speed
 0116        float verticalVelocity = flattenedVelocity.y;
 0117        blackboard.verticalSpeed = verticalVelocity;
 118
 0119        flattenedVelocity.y = 0;
 120
 0121        if (isOwnPlayer)
 0122            blackboard.movementSpeed = flattenedVelocity.magnitude - DCLCharacterController.i.movingPlatformSpeed;
 123        else
 0124            blackboard.movementSpeed = flattenedVelocity.magnitude;
 125
 0126        Vector3 rayOffset = Vector3.up * RAY_OFFSET_LENGTH;
 127        //NOTE(Brian): isGrounded?
 0128        blackboard.isGrounded = Physics.Raycast(target.transform.position + rayOffset,
 129            Vector3.down,
 130            RAY_OFFSET_LENGTH - ELEVATION_OFFSET,
 131            DCLCharacterController.i.groundLayers);
 132
 133#if UNITY_EDITOR
 0134        Debug.DrawRay(target.transform.position + rayOffset, Vector3.down * (RAY_OFFSET_LENGTH - ELEVATION_OFFSET), blac
 135#endif
 136
 0137        lastPosition = velocityTargetPosition;
 0138    }
 139
 140    void State_Init(BlackBoard bb)
 141    {
 0142        if (bb.isGrounded == true)
 143        {
 0144            currentState = State_Ground;
 0145        }
 146        else
 147        {
 0148            currentState = State_Air;
 149        }
 0150    }
 151
 152    void State_Ground(BlackBoard bb)
 153    {
 0154        if (bb.deltaTime <= 0)
 155        {
 0156            Debug.LogError("deltaTime should be > 0", gameObject);
 0157            return;
 158        }
 159
 0160        animation[baseClipsIds.run].normalizedSpeed = bb.movementSpeed / bb.deltaTime * bb.runSpeedFactor;
 0161        animation[baseClipsIds.walk].normalizedSpeed = bb.movementSpeed / bb.deltaTime * bb.walkSpeedFactor;
 162
 0163        float movementSpeed = bb.movementSpeed / bb.deltaTime;
 164
 0165        if (movementSpeed > runMinSpeed)
 166        {
 0167            animation.CrossFade(baseClipsIds.run, RUN_TRANSITION_TIME);
 0168        }
 0169        else if (movementSpeed > walkMinSpeed)
 170        {
 0171            animation.CrossFade(baseClipsIds.walk, WALK_TRANSITION_TIME);
 0172        }
 173        else
 174        {
 0175            animation.CrossFade(baseClipsIds.idle, IDLE_TRANSITION_TIME);
 176        }
 177
 0178        if (!bb.isGrounded)
 179        {
 0180            currentState = State_Air;
 0181            Update(bb.deltaTime);
 182        }
 0183    }
 184
 185    void State_Air(BlackBoard bb)
 186    {
 0187        if (bb.verticalSpeed > 0)
 188        {
 0189            animation.CrossFade(baseClipsIds.jump, JUMP_TRANSITION_TIME, PlayMode.StopAll);
 0190        }
 191        else
 192        {
 0193            animation.CrossFade(baseClipsIds.fall, FALL_TRANSITION_TIME, PlayMode.StopAll);
 194        }
 195
 0196        if (bb.isGrounded)
 197        {
 0198            animation.Blend(baseClipsIds.jump, 0, AIR_EXIT_TRANSITION_TIME);
 0199            animation.Blend(baseClipsIds.fall, 0, AIR_EXIT_TRANSITION_TIME);
 0200            currentState = State_Ground;
 0201            Update(bb.deltaTime);
 202        }
 0203    }
 204
 205    internal void State_Expression(BlackBoard bb)
 206    {
 0207        var animationInfo = animation[bb.expressionTriggerId];
 0208        animation.CrossFade(bb.expressionTriggerId, EXPRESSION_TRANSITION_TIME, PlayMode.StopAll);
 209
 0210        var mustExit = Math.Abs(bb.movementSpeed) > Mathf.Epsilon || animationInfo.length - animationInfo.time < EXPRESS
 0211        if (mustExit)
 212        {
 0213            animation.Blend(bb.expressionTriggerId, 0, EXPRESSION_TRANSITION_TIME);
 0214            bb.expressionTriggerId = null;
 0215            if (!bb.isGrounded)
 0216                currentState = State_Air;
 217            else
 0218                currentState = State_Ground;
 219
 0220            Update(bb.deltaTime);
 0221        }
 222        else
 223        {
 0224            animation.Blend(bb.expressionTriggerId, 1, EXPRESSION_TRANSITION_TIME / 2f);
 225        }
 0226    }
 227
 228    public void SetExpressionValues(string expressionTriggerId, long expressionTriggerTimestamp)
 229    {
 2230        if (animation == null)
 2231            return;
 232
 0233        if (string.IsNullOrEmpty(expressionTriggerId))
 0234            return;
 235
 0236        var mustTriggerAnimation = !string.IsNullOrEmpty(expressionTriggerId) && blackboard.expressionTriggerTimestamp !
 237
 0238        blackboard.expressionTriggerId = expressionTriggerId;
 0239        blackboard.expressionTriggerTimestamp = expressionTriggerTimestamp;
 240
 0241        if (mustTriggerAnimation)
 242        {
 0243            if (!string.IsNullOrEmpty(expressionTriggerId))
 244            {
 0245                animation.Stop(expressionTriggerId);
 246            }
 247
 0248            currentState = State_Expression;
 0249            Update();
 250        }
 0251    }
 252
 253    public void Reset()
 254    {
 0255        if (animation == null)
 0256            return;
 257
 258        //It will set the animation to the first frame, but due to the nature of the script and its Update. It wont stop
 0259        animation.Stop();
 0260    }
 261
 0262    public void SetIdleFrame() { animation.Play(baseClipsIds.idle); }
 263
 264    public void BindBodyShape(Animation animation, string bodyShapeType, Transform target)
 265    {
 0266        this.target = target;
 0267        this.animation = animation;
 268
 0269        if (bodyShapeType.Contains(WearableLiterals.BodyShapes.MALE))
 270        {
 0271            currentAnimations = maleAnimations;
 0272        }
 0273        else if (bodyShapeType.Contains(WearableLiterals.BodyShapes.FEMALE))
 274        {
 0275            currentAnimations = femaleAnimations;
 276        }
 277
 0278        for (var i = 0; i < currentAnimations.Get().Length; i++)
 279        {
 0280            var animationToId = currentAnimations.Get()[i];
 0281            if (this.animation.GetClip(animationToId.id) == null)
 282            {
 283                // animationToId.id and animationToId.clip.name must be the same or we get big performance drop here
 284                // Already coordinated with art team to have the animations with the correct ids
 0285                this.animation.AddClip(animationToId.clip, animationToId.id);
 286            }
 287        }
 288
 0289        SetIdleFrame();
 0290        animation.Sample();
 0291    }
 292
 293    // AvatarSystem entry points
 294    public bool Prepare(string bodyshapeId, GameObject container)
 295    {
 0296        if (!container.transform.TryFindChildRecursively("Armature", out Transform armature))
 297        {
 0298            Debug.LogError($"Couldn't find Armature for AnimatorLegacy in path: {transform.GetHierarchyPath()}");
 0299            return false;
 300        }
 0301        Transform armatureParent = armature.parent;
 0302        animation = armatureParent.gameObject.GetOrCreateComponent<Animation>();
 0303        armatureParent.gameObject.GetOrCreateComponent<StickerAnimationListener>();
 304
 0305        BindBodyShape(animation, bodyshapeId, target);
 0306        InitializeAvatarAudioAndParticleHandlers(animation);
 0307        return true;
 308    }
 309
 4310    public void PlayExpression(string expressionId, long timestamps) { SetExpressionValues(expressionId, timestamps); }
 311
 312    private void InitializeAvatarAudioAndParticleHandlers(Animation createdAnimation)
 313    {
 314        //NOTE(Mordi): Adds handler for animation events, and passes in the audioContainer for the avatar
 0315        AvatarAnimationEventHandler animationEventHandler = createdAnimation.gameObject.AddComponent<AvatarAnimationEven
 0316        AudioContainer audioContainer = transform.GetComponentInChildren<AudioContainer>();
 0317        if (audioContainer != null)
 318        {
 0319            animationEventHandler.Init(audioContainer);
 320
 321            //NOTE(Mordi): If this is a remote avatar, pass the animation component so we can keep track of whether it i
 0322            AvatarAudioHandlerRemote audioHandlerRemote = audioContainer.GetComponent<AvatarAudioHandlerRemote>();
 0323            if (audioHandlerRemote != null)
 324            {
 0325                audioHandlerRemote.Init(createdAnimation.gameObject);
 326            }
 327        }
 328
 0329        animEventHandler = animationEventHandler;
 0330    }
 331
 332    private void OnDestroy()
 333    {
 1329334        if (animEventHandler != null)
 0335            Destroy(animEventHandler);
 1329336    }
 337}