Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question ScriptableObject holding specific Class reference

Discussion in 'Scripting' started by wechat_os_Qy06hIF5DiUpguLJT5P_Qi-Wk, Nov 11, 2022.

  1. wechat_os_Qy06hIF5DiUpguLJT5P_Qi-Wk

    wechat_os_Qy06hIF5DiUpguLJT5P_Qi-Wk

    Joined:
    Jan 24, 2020
    Posts:
    9
    Hey guys, i'm a newbie and i might need a little advice here
    So i'm starting a quest system game and so far i'm creating "Tasks" as ScriptableObjects and storing them in a TaskManager List<ScriptableTasks>

    And a class for the Task logic, So each Task has a ScriptableObject reference and a logic class.

    Each Task class derives from ITask interface that stores the task states, and from TaskBase that holds the shared logic between tasks.

    How can i reference the "Task logic/class" script in the Task ScriptableObject on the editor

    I tried setting the field for the Taskscript type as a gameObject, create a gameObject prefab and slap the Task class/logic on it and then Instantiate it when the task is ready, it does work, but i'm quite sure that's not the right approach.

    Setting the ITask type won't show on the ScriptableObject, and when set to TaskBase, won't let me drag and drop the Task class.
    I hope i knew how to explain myself, basically the question is, how can i get to reference different specific C# classes under one single field.

    Below are the classes

    The scriptableObject for the tasks "Scriptable Tasks"
    ---------------------------------------------------------------------
    Code (CSharp):
    1.  
    2.  
    3.     [CreateAssetMenu(fileName = "Tasks", menuName = "ScriptableObjects/NewTask", order = 1)]
    4.     public class ScriptableTasks : ScriptableObject
    5.     {
    6.      
    7.         public int taskOrder; // Task index
    8.  
    9. //PROBLEM HERE BELOW!!
    10.         public  GameObject taskScript; //prefab holding the class logic
    11.  
    12.         public string taskName; // Title for the task
    13.  
    14.         public string taskDescription; //Task details
    15.  
    16.         public ScriptableCharacter taskGiver;  //Return the task to who
    17.        
    18.         public bool isCollectables;// Whether to ask demands collecting stuff
    19.  
    20.         [ShowIf("isCollectables")]
    21.         public int amountToCollect; // amount to collect
    22.  
    23.         public ScriptableTasks nextTask;// Getting the next task Ready
    24.  
    25.     }

    ITask holding task states
    ---------------------------------------
    Code (CSharp):
    1.  
    2. public interface ITask
    3. {
    4.     void SetTaskInitialized();
    5.     void SetTaskReady();
    6.     void SetTaskActive();
    7.     void SetTaskComplete();
    8.     void SetTaskFinished();
    9.     bool CheckTaskParameters();
    10. }
    TaskBase contains shared logic
    -------------------------------------------
    Code (CSharp):
    1.  
    2. public class TaskBase : MonoBehaviour
    3. {
    4.    protected  Transform player;
    5.    protected Animator playerAnimator;
    6.    protected  PlayerInputs inputs;
    7.    protected bool canInteract = true;
    8.    protected bool interactPressed;
    9.    protected IsPlayerInRange playerInRange;
    10.     public enum TaskState {Initialized,Ready, Active, Complete}
    11.     protected TaskState taskState;
    12.  
    13.     private void Awake()
    14.     {
    15.         player = GameObject.FindWithTag("Player").transform;
    16.         playerAnimator = player.GetComponentInChildren<Animator>();
    17.    
    18.         inputs = PlayerController.PlayerInputs;
    19.         inputs.Inputs.Interact.started += OnInteract;
    20.         inputs.Inputs.Interact.canceled += OnInteract;
    21.     }
    22.  
    23.     //Input action interaction
    24.     void OnInteract(InputAction.CallbackContext context) {}
    25.    
    26.     //Disable the player movements
    27.     protected void DisablePlayerMovements(){}
    28.  
    29.     //Set player active again
    30.     protected void EnablePlayerMovements(){}
    31.  
    32.     //Player can interact again coroutine
    33.    protected IEnumerator ResetInteraction(){}
    34. }
    35.  

    Example of a task
    ---------------------------------------------

    Code (CSharp):
    1.  
    2. public class TalkToNeighbours : TaskBase, ITask
    3. {
    4.     //Mom Transform
    5.     Transform mom;
    6.     //Neighbour transform
    7.     Transform neighbour;
    8.     //Mom Animator
    9.     Animator momAnimator;
    10.     //Neighbour Animator
    11.     Animator neighbourAnimator;
    12.     //Mom Check if player in range script
    13.     IsPlayerInRange momInRange;
    14.     //Neighbour checks if player is in range script
    15.     IsPlayerInRange neighbourInRange;
    16.  
    17.     //mom Cinemachine first animation
    18.     [SerializeField] CinemachineVirtualCamera momCamera;
    19.     //Mom cinemachine conversation focus
    20.     [SerializeField] CinemachineVirtualCamera momFocusCamera;
    21.  
    22.     //Mom conversation Text
    23.      [SerializeField]  TextAsset momConversationText;
    24.     //Neighbnrou conversation Text
    25.     [SerializeField] TextAsset neighbourConversationText;
    26.  
    27.     //Player can start interacting with the task characters
    28.     bool startInteracting = false;
    29.  
    30.     bool spokeToNeighbour = false;
    31.  
    32.     //Immedietly when the task is presented to the player
    33.     public void SetTaskInitialized()
    34.     {
    35.         //Set the task state to initialized
    36.         taskState = TaskState.Initialized;
    37.  
    38.         //Get the mom infos
    39.         mom = GameObject.FindGameObjectWithTag("Mom").transform;
    40.         momInRange = mom.GetComponent<IsPlayerInRange>();
    41.         momInRange.checkForPlayer = true;
    42.         //Set mom gameobject visible by size
    43.         mom.localScale = new Vector3(1, 1, 1);
    44.         momAnimator = mom.GetComponentInChildren<Animator>();
    45.  
    46.         //Same with the neighbour
    47.         neighbour = GameObject.FindGameObjectWithTag("Neighbour").transform;
    48.         neighbourInRange = neighbour.GetComponent<IsPlayerInRange>();
    49.         neighbourInRange.checkForPlayer = true;
    50.  
    51.         //Initialize the first animation conversation
    52.         DialogueSystem.instance.InitializeConversation(this, momConversationText, 0, taskState);
    53.         //Start displaying the conversation
    54.         DialogueSystem.instance.StartConversation();
    55.     }
    56.  
    57.  
    58.     //Right after telling the player that a task is ready to be presented
    59.     public void SetTaskReady()
    60.     {
    61.         //Set the task to active
    62.         taskState = TaskState.Ready;
    63.         //Player can start interacting with the characters
    64.         startInteracting = true;
    65.         //Display the first animation cinemachine Camera
    66.         momCamera.enabled = false;
    67.  
    68.    
    69.     }
    70.  
    71.     //Task active happens right after the player gets to know the details of the task,
    72.     //after the conversation with the task giver
    73.     public void SetTaskActive()
    74.     {
    75.         //Set taskstate to active
    76.         taskState = TaskState.Active;
    77.         //Mom conversation focus camera disable
    78.         momFocusCamera.enabled = false;
    79.         //Set the animattion for mom conversation to false
    80.         momAnimator.SetBool("isArguing", false);
    81.         //Set the animatio for the player conversation to false
    82.         playerAnimator.SetBool("isSadIdle", false);
    83.         //Reenabke the player movements
    84.         EnablePlayerMovements();
    85.  
    86.         StartCoroutine(ResetInteraction());
    87.         //Initialize the conversation for during the task text
    88.         //  DialogueSystem.instance.InitializeConversation(this, neighbourConversationText, 0, taskState);
    89.     }
    90.  
    91.  
    92.     public bool CheckTaskParameters()
    93.     {
    94.         return spokeToNeighbour;
    95.     }
    96.  
    97.     public void SetTaskComplete()
    98.     {
    99.         taskState = TaskState.Complete;
    100.         EnablePlayerMovements();
    101.         StartCoroutine(ResetInteraction());
    102.  
    103.         momAnimator.SetBool("isArguing", false);
    104.         //Set the animatio for the player conversation to false
    105.         playerAnimator.SetBool("isSadIdle", false);
    106.  
    107.         momFocusCamera.enabled = false;
    108.     }
    109.  
    110.     public void SetTaskFinished()
    111.     {
    112.         EnablePlayerMovements();
    113.  
    114.         StartCoroutine(ResetInteraction());
    115.  
    116.         momAnimator.SetBool("isArguing", false);
    117.  
    118.         playerAnimator.SetBool("isSadIdle", false);
    119.  
    120.         momFocusCamera.enabled = false;
    121.  
    122.         TaskManager.instance.EndTask(this.gameObject);
    123.        
    124.     }
    125.  
    126.  
    127.  
    128.     void Update()
    129.     {
    130.         if (startInteracting)
    131.        HandleInteraction();
    132.     }
    133.  
    134.  
    135.     void HandleInteraction()
    136.     {
    137.         //if interact is pressed and the player can interact and the player is in range
    138.         if (interactPressed && canInteract && (momInRange.playerInRange || neighbourInRange.playerInRange))
    139.         {
    140.  
    141.  
    142.             //Set a delay for interaction
    143.             canInteract = false;
    144.  
    145.             if (momInRange.playerInRange)
    146.             {
    147.  
    148.                 DisablePlayerMovements();
    149.                 //Set the player rotation
    150.                 HandleRotation(mom);
    151.  
    152.                 switch (taskState)
    153.                 {
    154.                     case TaskState.Ready:
    155.                         DialogueSystem.instance.InitializeConversation(this, momConversationText, 1, TaskState.Ready);
    156.                         break;
    157.  
    158.                     case TaskState.Active:
    159.                         DialogueSystem.instance.InitializeConversation(this, momConversationText, 2, TaskState.Active);
    160.                         break;
    161.  
    162.                     case TaskState.Complete:
    163.                         DialogueSystem.instance.InitializeConversation(this, momConversationText, 3, TaskState.Complete);
    164.                         break;
    165.                 }
    166.  
    167.  
    168.                 DialogueSystem.instance.StartConversation(true);
    169.  
    170.  
    171.                 momAnimator.SetBool("isArguing", true);
    172.                 playerAnimator.SetBool("isSadIdle", true);
    173.  
    174.                 momFocusCamera.enabled = true;
    175.  
    176.    
    177.  
    178.             }
    179.             else
    180.             {
    181.                 DisablePlayerMovements();
    182.                 HandleRotation(neighbour);
    183.  
    184.                 switch (taskState)
    185.                 {
    186.                     case TaskState.Ready:
    187.                         DialogueSystem.instance.InitializeConversation(this, neighbourConversationText, 0, TaskState.Ready);
    188.                         break;
    189.  
    190.                     case TaskState.Active:
    191.                         DialogueSystem.instance.InitializeConversation(this, neighbourConversationText,1, TaskState.Active);
    192.                         spokeToNeighbour = true;
    193.                         break;
    194.  
    195.                     case TaskState.Complete:
    196.                         DialogueSystem.instance.InitializeConversation(this, neighbourConversationText, 2, TaskState.Complete);
    197.                         break;
    198.                 }
    199.  
    200.                 DialogueSystem.instance.StartConversation();
    201.              
    202.             }
    203.         }
    204.  
    205.  
    206.     }
    207.  
    208.  
    209.  
    210.  


    Lastly, the Task manager
    -----------------------------------------------------------
    Code (CSharp):
    1. public class TaskManager : MonoBehaviour
    2. {
    3.     public static TaskManager instance;
    4.  
    5.     public List<ScriptableTasks> listTasks = new List<ScriptableTasks>();
    6.     public List<ScriptableTasks> listActiveTasks = new List<ScriptableTasks>();
    7.     public List<ScriptableTasks> listFinishedTasks = new List<ScriptableTasks>();
    8.  
    9.  
    10.     [SerializeField] GameObject canvasNewTaskPrefab;
    11.  
    12.     public GameObject canvasTaskProgress;
    13.  
    14.     private void Awake()
    15.     {
    16.         if (instance != this)
    17.         {
    18.             instance = this;
    19.         }
    20.         else { Destroy(gameObject); }
    21.  
    22.  
    23.  
    24.         GameObject firstTask = Instantiate(listTasks[0].taskScript);
    25.  
    26.         firstTask.name = listTasks[0].taskScript.name;
    27.         listActiveTasks.Add(listTasks[0]);
    28.     }
    29.  
    30.  
    31.     public void SetNextTaskReady(GameObject obj)
    32.     {
    33.  
    34.         ScriptableTasks task = listTasks.Find(x => x.taskScript.name == obj.name);
    35.  
    36.         ScriptableTasks newTask = task.nextTask;
    37.  
    38.         GameObject scriptObject = Instantiate(newTask.taskScript);
    39.  
    40.         scriptObject.GetComponent<ITask>().SetTaskInitialized();
    41.  
    42.         listActiveTasks.Add(newTask);
    43.  
    44.     }
    45.  
    46.     public void StartTask(GameObject obj)
    47.     {
    48.         //Get the current Task
    49.         ScriptableTasks currentTask = listActiveTasks.Find(x => x.taskScript.name == obj.name);
    50.  
    51.         //Get the next task
    52.         ScriptableTasks newTask = currentTask.nextTask;
    53.  
    54.         //Instantiate the task GameObject script
    55.         GameObject newTaskOject = Instantiate(newTask.nextTask.taskScript);
    56.  
    57.         Instantiate(canvasNewTaskPrefab);
    58.  
    59.         //Add the task to the list of active tasks
    60.         listActiveTasks.Add(newTask.nextTask);
    61.  
    62.     }
    63.  
    64.     public void TaskProgress(int taskIndex, int total, int amount)
    65.     {
    66.  
    67.         GameObject progress = Instantiate(canvasTaskProgress);
    68.         Transform parent = progress.transform.GetChild(0);
    69.         parent.GetChild(0).GetComponent<TMP_Text>().text = listTasks[taskIndex].taskName;
    70.  
    71.         Slider progressSlider = parent.GetComponentInChildren<Slider>();
    72.  
    73.         progressSlider.maxValue = total;
    74.  
    75.         LeanTween.value(progressSlider.gameObject, (amount - 1), amount, .3f)
    76.        .setOnUpdate((value) => { progressSlider.value = value; })
    77.        .setOnComplete(() =>
    78.        {
    79.            TMP_Text numericProgressText = progressSlider.GetComponentInChildren<TMP_Text>();
    80.            numericProgressText.text = amount + "/" + total;
    81.        });
    82.  
    83.  
    84.     }
    85.  
    86.     public void EndTask(GameObject obj)
    87.     {
    88.  
    89.         ScriptableTasks task = listActiveTasks.Find(x => x.taskScript.name == obj.name.Remove(obj.name.Length));  
    90.  
    91.         listActiveTasks.Remove(task);
    92.  
    93.  
    94.         // Get Reward
    95.  
    96.     }
    97.  
    98. }
    99.  
     
  2. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,895
    What you're asking for is polymorphic serialisation. Unity supports this via the SerializeReference attribute: https://docs.unity3d.com/ScriptReference/SerializeReference.html

    What it doesn't support is built in inspector tools to view and select the derived/implementing type you want to serialise into the object. Fortunately there's numerous paid and free addons that let you do this. I use Odin Inspector myself.
     
  3. wechat_os_Qy06hIF5DiUpguLJT5P_Qi-Wk

    wechat_os_Qy06hIF5DiUpguLJT5P_Qi-Wk

    Joined:
    Jan 24, 2020
    Posts:
    9
    Oh waw, that sounds great! is there any free Inspector addons you might recommend ?