• RuntimesUnity
  • Toggle Active State impacting animation update frame

Hey all,

I hope everyone is doing well. I'm working on a small project where someone can aim and shoot from a side scrolling character. When starting a scene with the gameObject already active, this works fine. No issues. If I have it toggled off and then set it to toggle on (think Megaman starting a stage), the projectile seems to come out at the last known position of the arm I'm aiming, even though the animation is updating. This occurs whether the scene has started or not (i.e., the character was off at the start of the scene, or on, the toggled off and back on) I believe the animation is occurring a frame late, because I set the animation to move forward on Update(), and my projectile logic to occur on LateUpdate() and I can't think of how this would happen otherwise. Since the code works unless I toggle the character off and on, I don't think the logic is the issue, itself. I can't the be first person to have this problem. What are people doing to fix this? Thanks for your help!

    Related Discussions
    ...

    It seems like the issue you're experiencing is related to the timing of when animations and logic are updated in relation to the toggling of the game object. When you toggle the game object off and then back on, the animation state might not be fully synchronized with the logic that handles the projectile. One approach to address this is to ensure that the animation state is correctly reset or updated when the game object is toggled back on. You can try explicitly setting the animation state or calling setToSetupPose to ensure the skeleton is in the correct pose before any logic is executed. Additionally, make sure that the animation and logic updates are correctly ordered, possibly by adjusting the execution order of scripts or ensuring that the animation state is updated before any logic that depends on it.

      Spinebot

      Hi! Thanks for your help!

      I tried the Skeleton.SetToSteupPose() command in the Start() function, but this didn't change anything. Thinking I misunderstod how this works, I tried moving this command around the script. Moving this command around a function "checkIfFiring()" prior to and after animation calls led to breaking animations further buy not allowing aiming at all or stopping the animation partway. I moved my projectile script (what fires objects from my pool) from the parent into the child/animation object, after my animation script, but the problem persists. Any other thoughts on what I can do here?

        @ultrafop For general info on what updates when in the spine-unity runtime, see:
        https://esotericsoftware.com/spine-unity-main-components#Script-Execution-Order

        ultrafop If I have it toggled off and then set it to toggle on (think Megaman starting a stage), the projectile seems to come out at the last known position of the arm I'm aiming, even though the animation is updating.

        I'm not sure how you determine the spawning position. If you use the BoneFollower component, that might not have been updated this frame between setting and applying the animation and spawning the projectile.

        BoneFollower updates the position in LateUpdate, so you might want to make sure your component which determines the location executes after the BoneFollower component, or call boneFollower.LateUpdate() manually before querying the position.

        Another potential source of error might be from where (from which update method) you are enabling the GameObject. This might be out of order regarding the other components as well.

        ultrafop I moved my projectile script (what fires objects from my pool) from the parent into the child/animation object, after my animation script, but the problem persists.

        Note that it makes no difference in regards to script execution order if you move a component from a parent to a child GameObject. Also it makes no difference whether your component is shown above or below other components at the same GameObject. See the Unity documentation:

        You cannot specify the order in which an event function is called for different instances of the same MonoBehaviour subclass. For example, the Update function of one MonoBehaviour might be called before or after the Update function for the same MonoBehaviour on another GameObject — including its own parent or child GameObjects.

        Hey! Thank you for the thorough reply! I'm not using the Bone Follower script. I think what I'm using is probably closer to the PointFollower script. I adjusted it a long time ago so I could be wrong, but I believe it's very close. Here it is:

        void OnValidate(){
                
            	if (skeletonAnimation == null) skeletonAnimation = GetComponent<SkeletonAnimation>();
                if (playerTransform == null) playerTransform = GetComponent<Transform>();
        
        }
        
        // Start is called before the first frame update
        void Awake()
        {
            bone = skeletonAnimation.Skeleton.FindBone(boneName);
            playerTransform = skeletonAnimation.transform; 
        
            Attachment attachment = skeletonAnimation.Skeleton.GetAttachment(slotName, attachmentName);
            if (attachment == null){
            Debug.Log("attachment not found");
            return;
            }
        
             pointAttachment = attachment as PointAttachment; 
        
            if(pointAttachment == null){
                Debug.Log("attachment is not a PointAttachment");
                return;
            }
        
             slot = skeletonAnimation.Skeleton.FindSlot(slotName);
        }
        
        
        void Update()
        {
            horOffset = offsets.Horizontal;
            verOffset = offsets.Vertical; 
        
            torsoPoint = pointAttachment.GetWorldPosition(slot, skeletonAnimation.transform);
            torsoPoint = skeletonAnimation.transform.InverseTransformPoint(torsoPoint);
        
            aimmer = new Vector3 (horOffset * 10, verOffset * 10, 0f); 
         
            if( horOffset != 0 || verOffset != 0){
            if(offsets.isRight){
                bone.SetLocalPosition(new Vector3(aimmer.x + torsoPoint.x,  aimmer.y + torsoPoint.y, 0f));
                 }
        
            else{
                bone.SetLocalPosition(new Vector3(-aimmer.x + torsoPoint.x,  aimmer.y + torsoPoint.y, 0f));
                 }
            }
            else{
                bone.SetLocalPosition(new Vector3(10,  2.5f, 0));
            }
        }`

        As for the code for firing the projectile, it occurs in LateUpdate() as follows:
        if (Input.GetButtonDown(fireButton) && Weapon[weaponsSelector] == 0 && scriptAccess.canShoot)
        {
        var newfireball = fireballPool.Get();
        newfireball.transform.position = pointAttachment.GetWorldPosition(slot, skelAnim.transform);
        newfireball.transform.rotation = movementAngle;
        newfireball.gameObject.SetActive(true);
        }

        I can take a look at the script execution order, but if you see anything that would obviously lead to this execution order error when disabling an activating the player gameobject in these scripts I'd be very appreciative.

        @ultrafop Thanks for the additional details. If you're using a single script to query the location in LateUpdate, then it should not be an issue with script execution order of LateUpdate in relation to other scripts, if there is only one script involved.

        To diagnose the issue better: Are the bone locations behind just the first frame after enabling your skeleton GameObject? Or is it behind a frame all the time during playback? You could quickly test this by assigning the queried projectile spawn location every frame to a GameObject's Transform position.

        My current best guess would be that the script that enables the skeleton GameObject is late, so that the enable-script's Update method is called after SkeletonAnimation.Update, enabling the skeleton but the Update is occurring next frame. Thus you could have a try calling SkeletonAnimation.Update(0) after enabling your skeleton GameObject, and after setting the desired animation.

          Harald

          Hey! Sorry for my late response. I wasn't able to get to the project until today. This SeketonAnimation.Update(0) code fixed the issue! It didn't work when enabling the script, but did work after the firing animation was called. I'm wondering, would just need to call this once to sync things back up, or would I need to call this after all animations?

          Thanks again for your help!

            ultrafop I'm wondering, would just need to call this once to sync things back up, or would I need to call this after all animations?

            It depends on where you are setting your animations. To save additional manual updates, you should be setting any animation in Update before SkeletonAnimation.Update(), so ensure that script execution order executes your component before SkeletonAnimation. Then SkeletonAnimation.Update will update and apply any animations accordingly in the same frame. Otherwise you're changing animations after they have been applied to the skeleton already, having an effect only next frame.