Unity NavMesh and Resource Gathering

I have not used the building NavMesh and NavMesh Agent for Unity.  After watching a few tutorials on youtube about path finding and navigation, i figured I should start with something more simple.  I realize using these tools is not optimal, but for the sake of learning they worked out fine.

I had a few goals in mind when I started this scene:

  1. AI navigation
  2. AI states
  3. Resource collection
  4. Resource delivery

Thankfully, Unity handles most of the AI navigation using the NavMesh.  The only coding involved with it is setting destinations based off of the state the AI is in.  I made five states that the AI could be in:

private void CheckState() {
        switch (state) {
            case State.Idle:
                // check energy level >>  goto: rest
                // goto: job
                // no jobs, empty inventory
                break;
            case State.Rest:
                // arrived?
                // rest
                // goto: last state
                break;
            case State.MovingToGatherNode:
                // arrived?
                // goto: rest
                break;
            case State.Gathering:
                // check inventory
                // full >> goto: target
                // finish
                     // full >> goto: target
                     // not full >> goto: job
                break;
            case State.MovingToTarget:
                // arrived?
                // empty inventory
                break;
        }
    }

Below is a video of the AI going through the different states.  The green lines are the AI’s path to its destination and the white boxes are just NavMesh obstacles.  After all the resources are collected, it will return home and wait for a new job to appear.  I made a hot key that spawns new resources to get collected to test the event system that I used for the jobs.

Unity NavMesh Agent and Resource Gathering
Scene Capture.

The AI will broadcast an event that it has reached its destination.  This signals the resource node that it is now workable.  There are two phases to working a resource.  The first is preparing it, like chopping down a tree.  The second is gather the resource.  All of these actions are controlled in the resource as long as the AI is working it.  If the AI’s inventory is filled before the resource is complete, it will broadcast the working event again with the appropriate bool.  Once the resource is depleted, it will broadcast a completion event that the AI subscribes to when it is assigned the node.

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

public enum ResourceType {
    Wood,
    Stone,
}

public class Resource : MonoBehaviour {
    /* Event Actions
     */
    public event Action<int> OnGather;
    public event Action<bool> OnGatherComplete;

    /* Resource Properties
     */
    public float resourceCount = 10;
    public float gatherInterval = 2f;
    public float timeToPrep = 5f;

    /* Agent Information
     */
    [SerializeField] GatherAI agent;
    private float gatherSpeedMultiplier = 1;
    public bool gather = false;
    public bool isGatherComplete = false;
    public bool working = false;

    public void Initialized(GatherAI script) {
        agent = script;
        agent.OnGatheringComplete += HandleResourceDestroy;
        agent.OnWorking += HandleOnWorking;
    }

    private void Update() {
        // gather from resource
        if (working) {
            if (gather) {
                // check remaining count
                if (resourceCount > 0) {
                    gatherInterval -= Time.deltaTime * gatherSpeedMultiplier;
                    if (gatherInterval <= 0f) {
                        gatherInterval = 2f;
                        OnGather?.Invoke(1);
                        resourceCount--;
                    }
                } else {
                    // gathering complete
                    OnGatherComplete?.Invoke(true);
                }
            } else {
                // work
                if (timeToPrep <= 0f) {
                    // time to prep met
                    gather = true;
                } else {
                    // prep to gather
                    timeToPrep -= Time.deltaTime * gatherSpeedMultiplier;
                }
            }
        }
    }

    public void HandleOnWorking(bool b, float workMulitplier = 1f) {
        working = b;
        gatherSpeedMultiplier = workMulitplier;
    }

    public void HandleResourceDestroy() {
        agent.OnWorking -= HandleOnWorking;
        agent.OnGatheringComplete -= HandleResourceDestroy;
        Debug.Log("Resource complete: " + transform.name);
        Destroy(transform.gameObject);
    }

}

Future Implementation

Next up is abstraction and refactoring.  I want to make the resource class a base or interface for all resource types.  I think how I have it implemented it would support multiple AIs working a resource, but that will be something I test in the future as well.

Right now, the resource node just contains a queue of workable nodes.  I plan to implement growth for resources and different algorithms for selecting a new node.  Right now it grabs the first in the queue but I might explore priority queues or getting the closest node to the AI.  There is a lot of opportunity.

Unity NavMesh and Resource Gathering