Search Unity

Best way to implement 'jobs'

Discussion in 'Scripting' started by Emolk, Jul 16, 2019.

  1. Emolk

    Emolk

    Joined:
    Feb 11, 2014
    Posts:
    241
    So i'm attempting to make a village management type game with its own spin. I have a 'worker' entity that moves around and collects essence, and deposits that essence into the base.

    Now i want to implement different job types for the workers. So some would be farmers, some fighters, etc. I was wondering the base way to do this? Iv'e thought of a couple of ways i could possibly to this.

    The first would be to have different prefab variants for each worker, with a base script attached to the parent and different job scripts attached to each variant. So the farmer would inherit the base script and will have the farmer script attached as well.

    Another way i thought of was to have all the job scripts attached to each worker, and just enable/disable them when the workers change jobs.

    Is there another easier way i'm missing? Which method would be easier to work with and implement? Pros and cons of both?

    Thanks.
     
  2. Emolk

    Emolk

    Joined:
    Feb 11, 2014
    Posts:
    241
    Bumping this.
     
  3. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,770
    Maybe if you starting new project, consider ECS / DOTS as Data Oriented Design?
     
  4. SparrowGS

    SparrowGS

    Joined:
    Apr 6, 2017
    Posts:
    2,536
    Don't have all the scripts on the unit and do the enable/disable thing, use a delegate/callback/event/we to switch out the logic for each job.

    Could have a Job master class / Interface (not a monobehaviour) and have inheriting classes from it called like Job_Farmer Job_Fighter / IFarmer IFighter, and on the master unit script just have a variable for the master Job class / interface and use that to call stuff like LookForJob() DoJob() or whatever.
     
    GeorgeCH and Munchy2007 like this.
  5. GeorgeCH

    GeorgeCH

    Joined:
    Oct 5, 2016
    Posts:
    222
    Very much what @SparrowsNest said - my instinct would be to have a Job master class and sub-classes that set up and handle all logic associated with that specific job.

    Also, take a look at GOAP - while a bit advanced and performance-intensive (it generates a crazy amount of garbage every frame), I understand it is often used to manage decision-making / activity simulations (I think Crusader Kings 2 uses a form of it to drive the behaviors of all in-game agents). There are some paid tutorials you can find online that go into more information and use cases.
     
  6. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,694
    When deciding on a solution, I recommend using something that's:
    • Fairly easy to implement
    • Easy to understand when you dig into it months later
    • Expandable without having to go back into old code
    • Relatively efficient
    GOAP is cool, but it has a steep learning curve. Here's an idea:

    1. Add a "blackboard" script to your worker. This script holds information that your worker's jobs can use. As a first pass, for simplicity just use explicit variables. Example:
    Code (csharp):
    1. [Serializable] // (So we can see it in the inspector)
    2. public class Blackboard
    3. {
    4.     public GameObject currentTarget;
    5.     public GameObject currentThreat; // etc.
    6. }
    2. Create jobs as ScriptableObject assets. ScriptableObjects aren't anything magical; they're just plain old script instances that Unity can serialize. A ScriptableObject asset is a ScriptableObject that's been saved as an asset file in your project. Example base class:
    Code (csharp):
    1. public class Job : ScriptableObject
    2. {
    3.     public virtual Job Process(Worker worker)
    4.     {
    5.         // This function should update the job and return the job that the worker...
    6.         // ...should work on next, usually the same job unless something has changed.
    7.         return this;
    8.     }
    9. }
    Example job for defending oneself:
    Code (csharp):
    1. [CreateAssetMenu]
    2. public class DefendSelf : Job
    3. {
    4.     public override void Process(Worker worker)
    5.     {
    6.         // Code here to attack worker.blackboard.currentThreat.
    7.         // If currentThreat is gone, return a new job.
    8.     }
    9. }
    3. Add a class that ties the blackboard and jobs together. Example:
    Code (csharp):
    1. public class Worker : MonoBehaviour
    2. {
    3.     public Blackboard blackboard;
    4.     public Job currentJob;
    5.    
    6.     void Update()
    7.     {
    8.         currentJob = currentJob.Process(this);
    9.     }
    10. }
     
    Joe-Censored likes this.