How to "rest" before continuing?

This is a follow up question to a question I had earlier today: Can't figure out Lerp! Woodcutter not moving to detected tree. - Unity Engine - Unity Discussions

In this script, a worker leaves his home, looks for a tree to cut, cuts the tree and returns home. It all happens very fast at the moment, he sprints from home, tree, home, tree. and so on. I would like him to rest for a while before he goes out looking for new work. How would I get around this? I’ve read about coroutines, timers and several different options, but I think that many of them seem a bit overkill. Hope someone here has a good solution.

Right now the script looks like this:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class WoodCutterDetector : MonoBehaviour
{
    public GameObject home;

    private GameObject tree;

    bool working = false;
    bool carryingTree = false;

    // Use this for initialization
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        if (working)
        {
            if(!carryingTree) {
                MoveToTree(tree);
            } else {
                MoveHome ();
            }
        } else {       
            GetTreeInRange();
        }
    }

    void MoveHome() {
        Vector3 homePosition = home.transform.position;
        Vector3 workerPosition = transform.position;

        transform.position = Vector3.Lerp(workerPosition, homePosition, 0.1f);

        if (Vector3.Distance(homePosition, workerPosition) <= 0.1f)
        {
            working = false;
            carryingTree = false;
        }
    }

    void MoveToTree(GameObject tree)
    {
        Vector3 treePosition = tree.transform.position;
        Vector3 workerPosition = transform.position;

        transform.position = Vector3.Lerp(workerPosition, treePosition, 0.1f);

        if (Vector3.Distance(treePosition, workerPosition) <= 0.1f)
        {
            Destroy(tree);
            tree = null;
            carryingTree = true;
        }
    }



    void GetTreeInRange()
    {
        GameObject[] trees = GameObject.FindGameObjectsWithTag("Tree");

        foreach (GameObject t in trees)
        {
            float distance = Vector3.Distance(t.transform.position, transform.position);

            if (distance <= 5f)
            {
                tree = t;
                working = true;
                return;
            }
        }
    }
}

Try making a variable when he is back home and set it to “1”.
Then to “MoveToTree” use invoke to set a timer to make that variable to “0”.

1 Like

Coroutines are what you want to use for modular behaviour like this. Here’s a simple example with your script. Notice there isn’t even a need for the Update() method. Instead you have states that transition to where they need to go and process their behavior.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class WoodCutterDetector : MonoBehaviour
{
    public GameObject home;

    bool working = false;
    bool carryingTree = false;
    float characterSpeed = 1f;

    void Start()
    {
        StartCoroutine(BeginWorkerFSM());
    }

    IEnumerator BeginWorkerFSM()
    {
        while(this.enabled) //Keep running while this script is active
        {
            yield return StartCoroutine(StartWorking());
            //if we return, then we've finished working and could execute something else, like
            //yield return StartCoroutine(GoToBed());
            yield return null;
        }
    }

    IEnumerator StartWorking()
    {
        while(working)
        {
            yield return StartCoroutine(MoveToTree(GetTreeInRange())); //Find a tree and walk to it
            yield return StartCoroutine(MoveHome()); //Take the tree home.
            //Do some checks here, should we stop working and do something else now? if so, set working = false
            //like:  if "all out of wood to collecting", working = false;
        }
    }

    IEnumerator MoveHome() {
        yield return StartCoroutine(MoveTo(home.transform));
        carryingTree = false;
    }

    IEnumerator MoveToTree(GameObject tree)
    {
        if(tree != null)
        {
            yield return StartCoroutine(MoveTo(tree.transform)); //Return to this function once our MoveTo() routine finishes
            StartCoroutine(ChopWood(tree)); //Start chopping our tree
        } else { 
            working = false; //out of wood, stop working
        }
    }

    //Routine to reuse for moving from place to place
    IEnumerator MoveTo(Transform target)
    {
        while(transform.position != target.position)
        {
            transform.position = Vector3.MoveTowards(transform.position, target.position, Time.deltaTime * characterSpeed); //Move to target by our speed each frame
            yield return null; //Wait for next frame
        }
    }

    IEnumerator ChopWood(GameObject tree)
    {
        //Start a tree cutting animation here and do a "while chopping animation plying { yield return null;}"
        //Or just a yield return new WaitForSeconds(chopTime);
        //Then this stuff
        Destroy(tree);
        carryingTree = true;
        yield return null;
    }

    GameObject GetTreeInRange()
    {
        GameObject[] trees = GameObject.FindGameObjectsWithTag("Tree"); //Not something you should be doing frequently...

        foreach (GameObject t in trees)
        {
            if (Vector3.Distance(t.transform.position, transform.position) <= 5f)
            {
                return t;
            }
        }
        return null;
    }
}
1 Like

This makes really good sense. I will put it to use once I get back to Unity tomorrow. Thanks!

@Invertex , on like 58 you forgot a "transform.position = " - Otherwise it seems to work! :slight_smile:

Whoops, you’re right haha, I just wrote it straight without testing the code, I’m surprised that’s the only issue :stuck_out_tongue:

1 Like