(Solved) Invoke vs WaitForSeconds, and reusable waits.

I am working on a resource collection script. Every frame, the game checks if you are hovering over a resource and if you are holding down right mouse button. If so, and here is my problem, there should be a collection wait time(cooldown) and then your resource amount should increase by one. However, neither route i’ve tried to take works. With invoke, it correctly waits for the specified time before actually giving the resource, but than continues to give me resources, without waiting again. Same goes for WaitForSeconds in a coroutine. I need a way to reuse the wait for either of these methods, but can’t seem to find out how. Any help would be very much appreciated. Here is my code:

using System.Collections;
using UnityEngine;

[RequireComponent(typeof(CameraRaycaster))]
public class gatherResource : MonoBehaviour
{
    new Camera camera;
    CameraRaycaster camRay;

    bool onResource = false;
    bool onCooldown = true;

    public float gatherSpeed = 3;
    public int testResource = 0;

    public Coroutine collectResource = null;

    // Use this for initialization
    void Start ()
    {
        camera = FindObjectOfType<Camera>();
        camRay = camera.GetComponent<CameraRaycaster>();
        camRay.onLayerChange += LayerIsResource;
    }

    private void Update()
    {
        while (Input.GetMouseButton(1) && onResource == true)
        {
            print(testResource);
            Invoke("CollectResource", gatherSpeed);
            break;
        }
    }

    void LayerIsResource(Layer currentLayer)
    {
        if (currentLayer == Layer.Resource)
            onResource = true;
        else
            onResource = false;
    }

    float CollectResource()
    {
        return testResource++;
    }
}

Update is executed once every frame. Your strange “while loop” is actually just an if statement since you have a break in it. GetMouseButton returns the state of the button. That means as long as the button is pressed down you will start a new Invoke every frame. It’s not really clear how your script is supposed to work. Even when you use GetMouseButtonDown (which is only true for one frame when the button is pressed down) the user can simply click two or more times and each time it starts a new invoke. If that’s what you want just replace GetMouseButton with GetMouseButtonDown.

However if you want to only allow “collecting” the resource once every “gatherSpeed” you better use a coroutine like this:

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

IEnumerator CollectResource()
{
    while (true)
    {
        if (onResource && Input.GetMouseButtonDown(1))
        {
            testResource++;
            yield return new WaitForSeconds(gatherSpeed);
        }
        yield return null;
    }
}

This allows the user to click when onResource is true but restrict the clicking to once every “gatherSpeed” seconds. If you like to delay the increment to after the wait time, just move it right below the WaitForSeconds.

I’m not sure why my code formatted like that. When it showed me the preview, it wasn’t like that.