< 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:123
Coverable lines:141
Total lines:344
Line coverage:12.7% (18 of 141)
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%38.97013.33%
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
 133360    [SerializeField] float runMinSpeed = 6f;
 133361    [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    {
 5014101        if (target == null || animation == null)
 5014102            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        if (animation.GetClip(expressionTriggerId) == null)
 0236            return;
 237
 0238        var mustTriggerAnimation = !string.IsNullOrEmpty(expressionTriggerId) && blackboard.expressionTriggerTimestamp !
 239
 0240        blackboard.expressionTriggerId = expressionTriggerId;
 0241        blackboard.expressionTriggerTimestamp = expressionTriggerTimestamp;
 242
 0243        if (mustTriggerAnimation)
 244        {
 0245            if (!string.IsNullOrEmpty(expressionTriggerId))
 246            {
 0247                animation.Stop(expressionTriggerId);
 248            }
 249
 0250            currentState = State_Expression;
 0251            Update();
 252        }
 0253    }
 254
 255    public void Reset()
 256    {
 0257        if (animation == null)
 0258            return;
 259
 260        //It will set the animation to the first frame, but due to the nature of the script and its Update. It wont stop
 0261        animation.Stop();
 0262    }
 263
 0264    public void SetIdleFrame() { animation.Play(currentLocomotions.idle.name); }
 265
 266    public void PrepareLocomotionAnims(string bodyshapeId)
 267    {
 0268        if (bodyshapeId.Contains(WearableLiterals.BodyShapes.MALE))
 269        {
 0270            currentLocomotions = maleLocomotions;
 0271        }
 0272        else if (bodyshapeId.Contains(WearableLiterals.BodyShapes.FEMALE))
 273        {
 0274            currentLocomotions = femaleLocomotions;
 275        }
 276
 0277        EquipEmote(currentLocomotions.idle.name, currentLocomotions.idle);
 0278        EquipEmote(currentLocomotions.walk.name, currentLocomotions.walk);
 0279        EquipEmote(currentLocomotions.run.name, currentLocomotions.run);
 0280        EquipEmote(currentLocomotions.jump.name, currentLocomotions.jump);
 0281        EquipEmote(currentLocomotions.fall.name, currentLocomotions.fall);
 0282    }
 283
 284    // AvatarSystem entry points
 285    public bool Prepare(string bodyshapeId, GameObject container)
 286    {
 0287        if (!container.transform.TryFindChildRecursively("Armature", out Transform armature))
 288        {
 0289            Debug.LogError($"Couldn't find Armature for AnimatorLegacy in path: {transform.GetHierarchyPath()}");
 0290            return false;
 291        }
 0292        Transform armatureParent = armature.parent;
 0293        animation = armatureParent.gameObject.GetOrCreateComponent<Animation>();
 0294        armatureParent.gameObject.GetOrCreateComponent<StickerAnimationListener>();
 295
 0296        PrepareLocomotionAnims(bodyshapeId);
 0297        SetIdleFrame();
 0298        animation.Sample();
 0299        InitializeAvatarAudioAndParticleHandlers(animation);
 0300        return true;
 301    }
 302
 4303    public void PlayEmote(string emoteId, long timestamps) { SetExpressionValues(emoteId, timestamps); }
 304
 305    public void EquipEmote(string emoteId, AnimationClip clip)
 306    {
 0307        if (animation.GetClip(emoteId) != null)
 0308            animation.RemoveClip(emoteId);
 0309        animation.AddClip(clip, emoteId);
 0310    }
 311
 312    public void UnequipEmote(string emoteId)
 313    {
 0314        if (animation.GetClip(emoteId) == null)
 0315            return;
 0316        animation.RemoveClip(emoteId);
 0317    }
 318
 319    private void InitializeAvatarAudioAndParticleHandlers(Animation createdAnimation)
 320    {
 321        //NOTE(Mordi): Adds handler for animation events, and passes in the audioContainer for the avatar
 0322        AvatarAnimationEventHandler animationEventHandler = createdAnimation.gameObject.AddComponent<AvatarAnimationEven
 0323        AudioContainer audioContainer = transform.GetComponentInChildren<AudioContainer>();
 0324        if (audioContainer != null)
 325        {
 0326            animationEventHandler.Init(audioContainer);
 327
 328            //NOTE(Mordi): If this is a remote avatar, pass the animation component so we can keep track of whether it i
 0329            AvatarAudioHandlerRemote audioHandlerRemote = audioContainer.GetComponent<AvatarAudioHandlerRemote>();
 0330            if (audioHandlerRemote != null)
 331            {
 0332                audioHandlerRemote.Init(createdAnimation.gameObject);
 333            }
 334        }
 335
 0336        animEventHandler = animationEventHandler;
 0337    }
 338
 339    private void OnDestroy()
 340    {
 1330341        if (animEventHandler != null)
 0342            Destroy(animEventHandler);
 1330343    }
 344}