How do I make the TaskListSO save more than a list of strings?

Gary,
I was excited to get to this lesson, as from the beginning I wanted the “task” to be a title, and to have a text field to store details. Actually, I working towards a task category (e.g. Convert to TaskItem), and then subtasks displayed in a separate ScrollView under that TaskItem - bascially what we have now (e.g. Create TaskItem, Modify TaskListEditor, Modify TaskListSO, etc). However, every attempt I’ve made to store anything more complex than a simple list fails. I tried using a set of Arrays, but synchronizing them during adds/removals just got too messy. Is there a way to have a TaskListSO store a List (or other object) of items that each contain a string (title), bool (completed), and another string (details)? Using a class doesn’t work.

Thanks

If I’m understanding your quesiton, yeah you can.

Do you have specifics of the issue you’re running into? Maybe you need to post your code.

Try changing tasks object in the TaskListSO from a list of string to a list of TaskData which is a serializable struct with more fields. Something like:

using System.Collections.Generic;
using UnityEngine;

namespace GameDevTV.Tasks
{

    [CreateAssetMenu(menuName = "Task List", fileName = "New Task List")]
    public class TaskListSO : ScriptableObject
    {

        [SerializeField] List<TaskData> tasks = new List<TaskData>();

        public List<TaskData> GetTasks() { return tasks; }

        public void AddTasks(List<TaskData> savedTasks)
        {
            tasks.Clear();
            tasks = savedTasks;
        }

        public void AddTask(string task, bool complete)
        {
            tasks.Add(new TaskData() { name = task, complete = false });
        }
    }

    [System.Serializable]
    public struct TaskData
    {
        public string name;
        public bool complete;
    }

}

Anything added to TaskData must be a serializable datatype. It’s not pretty in the Inspector but I guess that’s another use for Visual Elements, maybe? It’s then an arduous task of going over the TaskListEditor code making sure it all works.

Opening up the “New Task List.asset” file shows it’s saving something.

Thanks,

The editor won’t be hard to implement, because I tried doing this with a class, and kept everything just in case I figured out how to make it work. Changing the tasks/subtasks in a struct will have to be modified, since they’re value types., and I’ve not worked with structs. I’ll have to work on that Monday.

Well. After a day of learning about and converting everything to a list of structs, and getting my display working great with structs, I decided to implement saving the SO to finalize the deal, only to realize I never unimplemented it. It just wasn’t working. It saves, but not between sessions. It saves List<string> between sessions, but not List<TaskItem>. So bummed.
The struct portion of my code is:

public struct SubTask{
        //public string TaskName {get; set;}
        //public bool Completed {get; set;}
        //public string Details {get; set;}
        string taskName;
        bool completed;
        string details;
        public string TaskName {get {return taskName;} set{taskName = value;}}
        public bool Completed {get {return completed;} set{completed = value;}}
        public string Details {get {return details;} set{details = value;}}

        public override string ToString(){
            return string.Format("{0}, {1}, {2}",TaskName, Completed, Details);
        }
        //public static readonly SubTask Empty = new SubTask();
    }

I tried several different ways, but no luck. In my SO, my list is simply:

        [SerializeField] List<SubTask> tasks = new List<SubTask>();

Assigning my SO in my editor script is done when loading (I force that - can’t create tasks till a SO is loaded)

       void LoadTasksFromSO(){
            if (loadTasksSOField.value == null) return;
            //taskListSO = loadTasksSOField.value as TaskListSO;
            taskListSO = loadTasksSOField.value as TaskListSOSubtask;
            activeTasksDisplay.Clear();
            //foreach (string task in tasks){
            //foreach (string task in taskListSO.GetTasks()){
            foreach (SubTask task in taskListSO.GetTasks()){
                if (task.Completed){
                    AddTaskToList(task, completedTasksDisplay);
                }else{
                    AddTaskToList(task, activeTasksDisplay);
                }
            }
        }

And, lastly, my code to save my SO is:

       void SaveTasksSO(){
            if (taskListSO == null) return;
            Debug.Log("saving taskListSO");
            EditorUtility.SetDirty(taskListSO);
            AssetDatabase.SaveAssetIfDirty(taskListSO);
            //AssetDatabase.Refresh(); // course uses this, but it's for updating the database after importing new items
        }

Any ideas?

Is your struct marked with [System.Serializable]?

Try replacing your SubTask struct with this:

    [System.Serializable]
    public class SubTask
    {
        [SerializeField] string _taskname;
        [SerializeField] bool _completed;
        [SerializeField] bool _details;

        public string TaskName { get { return _taskname; } set { _taskname = value; } }
        public bool Completed { get { return _completed; } set { _completed = value; } }
        public bool Details { get { return _details; } set { _details = value; } }

        public override string ToString()
        {
            return string.Format("{0}, {1}, {2}", TaskName, Completed, Details);
        }
    }

As Brian mentioned above, the class/struct needs to a have [System.Serializable] attribute. Next mark your private variables with [SerializeField]. I’ve changed it back to a class. It doesn’t really matter which is used.

Currently viewing your TaskListSO object in the Unity Editor, there will be nothing. With the above changes you should see the Tasks list and be able to edit children, similar to the right window of my first post screenshot.

Brian -yes, the struct is [System.Serializable] (should have pasted that line, too)
vyyc - Thanks, you’re on the right track. Last night I thought of serializing each field in addition to the struct (as you did), and that was the key. It works now. I left it as a struct since I already changed everything. However, I don’t think a class will work, as I tried that first, but perhaps I didn’t serialize each field. Maybe I’ll try that today, just to check. We’re here to learn, after all.

Final update. Classes do work, so I guess my whole issue was I did not mark each field with [SerializeField]. Oddly, you can’t change a class to a struct and expect all your methods to work, due to structs being value types. However, you CAN change a struct to class (literally just changing the word struct to class), and everything still works (i.e. classes can have their members changed using values, but structs can’t have their members changed using references). Big learning experience.

2 Likes

I’ve also found I generally have better luck with classes over structs when dealing with serializing. I had to make a lot of similar adjustments when I wrote the InventoryItem Editor (and am making still more now that I’m rewriting it for UIElements.

Cool. I’m halfway through the inventory course and took this course for a change of pace. I’ll be getting back to the other course soon.

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.

Privacy & Terms