Anyone know why this little script doesn't work consistently? [Solved]

I have a little script that tells my animator when the character has moved. It works fine at times, but other times it just doesn’t work at all. I put a debug.log in there to see if anything happens, and sure as taxes nothing happens. This script seems to become especially buggy the moment I reload a level. Anyone know what’s up here?

    public class MovementChecker : MonoBehaviour
    {
        private Vector3 _curpos;
        private Vector3 _lastpos;

        private void Update()
        {
            _curpos = gameObject.transform.position;
        }

        private void LateUpdate()
        {
            var an = gameObject.GetComponent<Animator>();
            _lastpos = gameObject.transform.position;

            if (_curpos != _lastpos)
            {
                Debug.Log("This fella is moving!");
                an.SetBool("IsMoving", true);
            }
            else if (_curpos == _lastpos)
            {
                an.SetBool("IsMoving", false);
            }
        }
    }

UPDATE:
I think I fixed it, by changing Update() to FixedUpdate(). I guess sometimes update and lateupdate are just too close together to register a difference.

UPDATE 2:
Thank you for the feedback guys. Here’s the new and improved script for anyone else that wishes to make use of it:

public class MovementChecker : MonoBehaviour
    {
        private Vector3 _curpos;
        private Vector3 _lastpos;
        private Animator _animator;

        private void Awake()
        {
            _animator = gameObject.GetComponent<Animator>();
            _lastpos = transform.position;
        }

        private void LateUpdate()
        {
            _curpos = gameObject.transform.position;

            if (_curpos != _lastpos)
            {
                _animator.SetBool("IsMoving", true);
                _lastpos = _curpos;
            }
            else if (_curpos == _lastpos)
            {
                _animator.SetBool("IsMoving", false);
            }
        }
    }

One, save your Animator component so you’re not getting it every frame.
Secondly, I’m not sure how you’re moving the object for it to be different every frame? I assume another script is modifying the position? There could be a lot of scenarios though where _curpos and _lastpos is the same due to the order Unity executes stuff or something else… so I will suggest checking between frames rather than at different stages during the same frame, which is what you are doing currently.

Something like this should work, you can also remove the Awake method if you place the Animator component into the slot inside the editor for this component.

public class MovementChecker : MonoBehaviour
{
    [SerializeField] Animator _animator;

    private Vector3 _curpos;
    private Vector3 _lastpos;
   
    void Awake()
    {
        _animator = GetComponent<Animator>();
    }
   
    void Start()
    {
        // So we don't accidentaly set ourselves as moving at the start
        _curpos = transform.position;
    }

    private void Update()
    {
        _lastpos = _curpos;
        _curpos = gameObject.transform.position;
       
        if (_curpos != _lastpos)
        {
            Debug.Log("This fella is moving!");
            _animator.SetBool("IsMoving", true);
        }
        else if (_curpos == _lastpos)
        {
            _animator.SetBool("IsMoving", false);
        }
    }
}
2 Likes

You should take the GetComponent out of the update.
And I change some things. checking position between an update, fixed and lateupdate i wouldn’t do this because i don’t think there will be much change
I would do something like this

    public class MovementChecker : MonoBehaviour {
        private Vector3 lastPos;
        private Animator animator;

        private void Start() {
            animator = gameObject.GetComponent<Animator>();
            lastPos = transform.position;
        }

        private void Update() {
            CheckStuff();        
        }

        private void CheckStuff() {
            if (transform.position != lastPos) {
                Debug.Log("This fella is moving!");
                animator.SetBool("IsMoving", true);
            } else if (transform.position == lastPos) {
                animator.SetBool("IsMoving", false);
            }
            lastPos = transform.position;
        }
    }

Not even close. Update is ALWAYS called before LateUpdate. Always.

In fact, it’s FixedUpdate that is not reliable - in this case, if your framerate goes above 50fps (assuming you have the default FixedTimestep of 0.02 seconds), then you will start to see LateUpdate sometimes happening without FixedUpdate having been called that frame. Don’t use FixedUpdate for anything other than physics.

As for your initial problem: To do “difference detection”, you’ll want to set your “previous position” at the END of your function. And you don’t need to split this between Update and LateUpdate to do so.

Vector3 lastPosition;
void LateUpdate() {
Vector3 thisPosition = transform.position;
if (thisPosition != lastPosition) {
//stuff
}
lastPosition = thisPosition;
}

This is correct GiTS_, this script’s only purpose is to check if the character is Indeed moving. The moving itself is handled in another script. Though I might merge these two together in future since there are actually times I wouldn’t want the player doing the running animation while he’s moving (being on a moving platform or getting knocked back etc). Thank you for the tip mate, I have moved the getComponent to the awake function, and actually just went through all my other scripts to do the same :slight_smile:

Also, setting the _curpos at the start’s clever! I changed the script and my character had a slight jerky motion at the start that I didn’t even notice before reading this. After applying your fix it’s totally clean.

Thank you for the feedback martinmr. A little further down I have incorporated StarManta’s method of checking it all in one frame and updating the old position while the character is moving.

StarManta thank you mate, this is much cleaner now than before, I like that the function checks and cleans up after itself in one go.

For anyone else that may be struggling to get this to work, here’s the new and improved script that seems to be working 100%:

public class MovementChecker : MonoBehaviour
    {
        private Vector3 _curpos;
        private Vector3 _lastpos;
        private Animator _animator;

        private void Awake()
        {
            _animator = gameObject.GetComponent<Animator>();
            _lastpos = transform.position;
        }

        private void LateUpdate()
        {
            _curpos = gameObject.transform.position;

            if (_curpos != _lastpos)
            {
                _animator.SetBool("IsMoving", true);
                _lastpos = _curpos;
            }
            else if (_curpos == _lastpos)
            {
                _animator.SetBool("IsMoving", false);
            }
        }
    }