Trouble with MoveTowards and async

Hey all, Im having trouble where a MoveTowards within an async function just isnt working properly. The character locks up at a few intervals and stops moving entirely. Heres the code.

public async void MoveToDest(DisplayCharacter chara, List<Vector3Int> path)
    {
        if(path.Count != 0)
        {
            Debug.Log("Path > 0");
            for (int i = 0; i < path.Count; i++)
            {
                Debug.Log("to path " + (i + 1) + " - " + path[i + 1]);
                await MoveNextTile(chara.gameObject, path[i + 1], chara.speed);
            }
        }
        else
        {
            Debug.LogWarning("Path Length <= 0");
        }
    }

    private async Task MoveNextTile(GameObject chara, Vector3Int next, float speed)
    {
        Vector3 targetPos = new Vector3(mapManager.tileArray[next.x, next.y].transform.position.x,
                                        mapManager.tileArray[next.x, next.y].transform.position.y + .5f,
                                        mapManager.tileArray[next.x, next.y].transform.position.z);
        Debug.Log("target pos = " + targetPos);
        while (chara.transform.position != targetPos)
        {
            chara.transform.position = Vector3.MoveTowards(chara.transform.position, targetPos, speed * Time.deltaTime);
            Debug.Log("Current = " + chara.transform.position + " Target = " + targetPos);
            await Task.Yield();
        }
    }

Overall im very confused as the whole thing seems to be working correctly, just that the character locks up whilst trying to move towards locations.

For context, this is meant to be a tile based movement script where the player moves across tiles one by one. The character is locking up when going between the starting tile to the first tile in the sequence.

I didn’t end up looking through your code, but is there a reason you chose you use asynchronous code instead of something simpler like coroutines? It just seems like you’re risking thread safety for little payoff.

Largely due to the chaining of them being much cleaner and simpler. Im aiming for a turn-based isometric so im going to have to chain a lot of coroutines/async functions. i figured asyncs would be better in that case.

Default async tasks really aren’t made to be used for these sorts of things. Coroutines exist for this very reason, and it’s really not hard to yield return another coroutine to string them together.

The default C# Task class is kinda heavyweight for game use as well. If you feel you really must use async code, I would suggest the Cysharp’s UniTask struct: GitHub - Cysharp/UniTask: Provides an efficient allocation free async/await integration for Unity.

It can be used in conjunction with Coroutines, or allows async methods to be used as coroutines in a way.

That said, because they’re Tasks, they’re a lot more cumbersome to stop should you want to interrupt them.

what im getting from your reply is thats its possible to just yield return StartCoroutine(MoveNextTile(chara.gameObject, path[i + 1], chara.speed)); right? regardless i’ve changed up the code to coroutines and im still having trouble. Heres the new code.

public IEnumerator MoveToDest(DisplayCharacter chara, List<Vector3Int> path)
    {
        if(path.Count != 0)
        {
            Debug.Log("Path > 0");
            for (int i = 0; i < path.Count - 1; i++)
            {
                Debug.Log("to path " + (i + 1) + " - " + path[i + 1]);
                yield return StartCoroutine(MoveNextTile(chara.gameObject, path[i + 1], chara.speed));
                Debug.Log("finished moving");
            }
        }
        else
        {
            Debug.LogWarning("Path Length <= 0");
        }
    }

    private IEnumerator MoveNextTile(GameObject chara, Vector3Int next, float speed)
    {
        Vector3 targetPos = new Vector3(mapManager.tileArray[next.x, next.y].transform.position.x,
                                        mapManager.tileArray[next.x, next.y].transform.position.y + .5f,
                                        mapManager.tileArray[next.x, next.y].transform.position.z);
        Debug.Log("target pos = " + targetPos);
        while (Vector3.Distance(chara.transform.position, targetPos) < 0.001f)
        {
            chara.transform.position = Vector3.MoveTowards(chara.transform.position, targetPos, speed * Time.deltaTime);
            Debug.Log("Current = " + chara.transform.position + " Target = " + targetPos);
            yield return null;
        }
    }

In what way is it not working? I can’t tell what’s wrong by just looking at the code alone.

You are correct that you can wait another coroutine in that manner. In fact you don’t even have to call StartCoroutine() you can just yield return the IEnumerator method directly.

Yeah i managed to fix it, there were some problems with the code that made the character not move to each tile propely, on top of that my previous movement code (did the same thing but i was making the code better & more applicable) wasnt fully disabled. It’s all fixed now tho.