0
\$\begingroup\$

I am programming a Jobs Queue in Unity, so that I can send Tasks/Jobs to a central queue where the tasks will be processed.

First, I defined 3 possible job types inside of a Scriptable Object.

public enum jobsType {justPlay, playAndWait, justWait}

Then I defined a class inside of the same Scriptable Object, so that each Jobs could have 3 inputs: a GameObject, a jobs Type, and a float. Here's the whole Scriptable Object.

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
using Unity.VisualScripting;

public enum jobsType {justPlay, playAndWait, justWait}

[CreateAssetMenu(fileName = "New JobsData", menuName = "Jobs Data", order = 51)]

public class JobsData : ScriptableObject {

    [SerializeField]
    public JobsItem thisJobsItem;

    [IncludeInSettings(true)]
    [System.Serializable]
    public class JobsItem {

    [SerializeField]
    public jobsType typeOfJob;

    [SerializeField]
    public GameObject thisGameObject;

    [SerializeField]
    public float jobsNumber;
}
}

Then in my code, I use a Switch to route the jobs processing based on the jobs Type enum. This code is working great - so far, so good!

That said, I don't want to have to modify this JobsData Scriptable Object every time I want to add a new jobs Type. I'm trying to honor the Open-Closed Principle: "software entities (classes, modules, methods, etc.) should be open for extension, but closed for modification."

It would be ideal if I could define jobsType to be a separate Scriptable Object... and then use a Switch to route between various jobsTypes.

At first, I was considering doing the following:

  1. Create Scriptable Object assets using JobsData, where each asset represents a Jobs Type
  2. Create a List of these Scriptable Object assets
  3. Use that List to dynamically generate an Enum (not sure if this is possible)
  4. Then use that Enum to power a Switch. (also don't see how this is possible).

But as I thought it through, I realized that this might not be the best path forward... since as I understand it, Enums must be specified at compile time. Beyond that, I can't figure out how I would even use any dynamically-generated Enums in my Switch.

Right now my code works because I define my Enums in the class, but not sure how to achieve the same thing with a dynamic Enum (if those are even possible).

I'd love to be able to define my JobsTypes as Scriptable Object assets, and then route jobs based on those assets. Given that:

  1. Is there an alternative to a Switch I could use to route JobsTypes to the appropriate processing code?
  2. Or alternatively, is there a way I could use dynamically-generated Enums in a Switch?

Or maybe I'm thinking about this all wrong? Open to any and all ideas!

\$\endgroup\$
6
  • \$\begingroup\$ I think you'll get better answers if you phrase your question in terms of "How can I solve this problem?" rather than "Is it possible to use the solution I already thought of?" \$\endgroup\$
    – DMGregory
    Nov 10, 2022 at 1:44
  • \$\begingroup\$ @DMGregory - I tried to phrase the question more broadly, but struggled to find the right words. Also, I tried to make it clear that I don't know how to use dynamically-generated Enums in a Switch. If anyone has ideas on how I can better word the title question, happy to edit!! It's something I struggled with... \$\endgroup\$
    – kanamekun
    Nov 10, 2022 at 1:54
  • \$\begingroup\$ I don't think a dynamically generated enum is the best solution here. Have you considered a dictionary mapping types or objects to delegates? \$\endgroup\$
    – DMGregory
    Nov 10, 2022 at 1:56
  • \$\begingroup\$ I did consider a dictionary but wasn't sure how to configure it. Would the idea be to create a dictionary at runtime, and then use strings from the dictionary to power a Switch? \$\endgroup\$
    – kanamekun
    Nov 10, 2022 at 2:10
  • 1
    \$\begingroup\$ You don't need a switch. The dictionary can contain delegates that run the code that would have been inside the switch cases. \$\endgroup\$
    – DMGregory
    Nov 10, 2022 at 2:29

1 Answer 1

2
\$\begingroup\$

As I reread your question, I think you can do this with just plain old inheritance. First we'll declare an abstract base class for all our job types, and expose a Process() method. Here I've defined it as an IEnumerator on the assumption that you want these to run as coroutines so they can include delays, based on your "wait" names, but you could also make it just a plain void method if you don't need that.

public abstract class JobType : ScriptableObject {
    public abstract IEnumerator Process(JobData data);
}

Then we can implement derived classes for each distinct processing behaviour you need:

[CreateAssetMenu(fileName="JustPlayJobType.asset", menuName="Job Type/Just Play")]
public class JustPlayJobType : JobType {
    public override IEnumerator Process(JobData data) {
        // "Play" code goes here.
        yield break;
    }
}

[CreateAssetMenu(fileName="PlayAndWaitJobType.asset", menuName="Job Type/Play and Wait")]
public class PlayAndWaitJobType : JobType {
    public override IEnumerator Process(JobData data) {   
        // Play" code goes here. 
        yield return new WaitForSeconds(data.jobItem.number);       
    }
}

Then instead of a switch statement, you can just read the job type scriptable object from your job item and invoke its process method:

IEnumerator ProcessJobs(Queue<JobData> jobs) {
    jobsInProgress = true;
    while (jobs.Count > 0) {
        var job = jobs.Dequeue();
        var jobType = job.jobItem.jobType;
        yield return jobType.Process(job);
    }
    jobsInProgress = false;
}

You can then run ProcessJobs as a coroutine and it will run the jobs sequentially, including waits as specified by the respective job types in the collection.

\$\endgroup\$
1
  • \$\begingroup\$ In the past, this route has always been the easiest to work with. Very extendable to new Jobs, and there's a good localization of control. It's very easy to want to treat the jobs as flags for behavior that is defined elsewhere, but logistically it's a lot better to let the jobs define that behavior themselves. \$\endgroup\$ Nov 10, 2022 at 13:17

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .