Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question Scriptable objects does not save boolean lists between scenes

Discussion in 'Scripting' started by LoyDtv, Dec 1, 2023.

  1. LoyDtv

    LoyDtv

    Joined:
    Nov 1, 2021
    Posts:
    7
    hi guys, let me start by saying that I am a new developer, I have a script of a scriptable object that manages two boolean lists, in practice it is needed because, in my game there are different levels and each one has 3 coins, my idea was to use a scriptable object to manage if the player had taken them and subsequently if he had reached the end of the level, everything works well except when I change scenes, in a scene where there is no specific scriptable object as a reference for some script, it resets the variables for me, I hope I have explained myself and I accept any solution.
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,243
    Sounds like you have a bug!!!

    Time to start debugging!

    By debugging you can find out exactly what your program is doing so you can fix it.

    Here is how you can begin your exciting new debugging adventures:

    You must find a way to get the information you need in order to reason about what the problem is.

    Once you understand what the problem is, you may begin to reason about a solution to the problem.

    What is often happening in these cases is one of the following:

    - the code you think is executing is not actually executing at all
    - the code is executing far EARLIER or LATER than you think
    - the code is executing far LESS OFTEN than you think
    - the code is executing far MORE OFTEN than you think
    - the code is executing on another GameObject than you think it is
    - you're getting an error or warning and you haven't noticed it in the console window

    To help gain more insight into your problem, I recommend liberally sprinkling
    Debug.Log()
    statements through your code to display information in realtime.

    Doing this should help you answer these types of questions:

    - is this code even running? which parts are running? how often does it run? what order does it run in?
    - what are the names of the GameObjects or Components involved?
    - what are the values of the variables involved? Are they initialized? Are the values reasonable?
    - are you meeting ALL the requirements to receive callbacks such as triggers / colliders (review the documentation)

    Knowing this information will help you reason about the behavior you are seeing.

    You can also supply a second argument to Debug.Log() and when you click the message, it will highlight the object in scene, such as
    Debug.Log("Problem!",this);


    If your problem would benefit from in-scene or in-game visualization, Debug.DrawRay() or Debug.DrawLine() can help you visualize things like rays (used in raycasting) or distances.

    You can also call Debug.Break() to pause the Editor when certain interesting pieces of code run, and then study the scene manually, looking for all the parts, where they are, what scripts are on them, etc.

    You can also call GameObject.CreatePrimitive() to emplace debug-marker-ish objects in the scene at runtime.

    You could also just display various important quantities in UI Text elements to watch them change as you play the game.

    Visit Google for how to see console output from builds. If you are running a mobile device you can also view the console output. Google for how on your particular mobile target, such as this answer for iOS: https://forum.unity.com/threads/how-to-capturing-device-logs-on-ios.529920/ or this answer for Android: https://forum.unity.com/threads/how-to-capturing-device-logs-on-android.528680/

    If you are working in VR, it might be useful to make your on onscreen log output, or integrate one from the asset store, so you can see what is happening as you operate your software.

    Another useful approach is to temporarily strip out everything besides what is necessary to prove your issue. This can simplify and isolate compounding effects of other items in your scene or prefab.

    If your problem is with OnCollision-type functions, print the name of what is passed in!

    Here's an example of putting in a laser-focused Debug.Log() and how that can save you a TON of time wallowing around speculating what might be going wrong:

    https://forum.unity.com/threads/coroutine-missing-hint-and-error.1103197/#post-7100494

    If you are looking for how to attach an actual debugger to Unity: https://docs.unity3d.com/2021.1/Documentation/Manual/ManagedCodeDebugging.html

    "When in doubt, print it out!(tm)" - Kurt Dekker (and many others)

    Note: the
    print()
    function is an alias for Debug.Log() provided by the MonoBehaviour class.



    EDIT: as Melv points out, a GameManager is an alternate solution to this. Here's more:

    Simple Singleton (UnitySingleton):

    Some super-simple Singleton examples to take and modify:

    Simple Unity3D Singleton (no predefined data):

    https://gist.github.com/kurtdekker/775bb97614047072f7004d6fb9ccce30

    Unity3D Singleton with a Prefab (or a ScriptableObject) used for predefined data:

    https://gist.github.com/kurtdekker/2f07be6f6a844cf82110fc42a774a625

    These are pure-code solutions, DO NOT put anything into any scene, just access it via .Instance

    Alternately you could start one up with a
    RuntimeInitializeOnLoad
    attribute.

    The above solutions can be modified to additively load a scene instead, BUT scenes do not load until end of frame, which means your static factory cannot return the instance that will be in the to-be-loaded scene. This is a minor limitation that is simple to work around.

    If it is a GameManager, when the game is over, make a function in that singleton that Destroys itself so the next time you access it you get a fresh one, something like:

    Code (csharp):
    1. public void DestroyThyself()
    2. {
    3.    Destroy(gameObject);
    4.    Instance = null;    // because destroy doesn't happen until end of frame
    5. }
    There are also lots of Youtube tutorials on the concepts involved in making a suitable GameManager, which obviously depends a lot on what your game might need.

    OR just make a custom ScriptableObject that has the shared fields you want for the duration of many scenes, and drag references to that one ScriptableObject instance into everything that needs it. It scales up to a certain point.

    And finally there's always just a simple "static locator" pattern you can use on MonoBehaviour-derived classes, just to give global access to them during their lifecycle.

    WARNING: this does NOT control their uniqueness.

    WARNING: this does NOT control their lifecycle.

    Code (csharp):
    1. public static MyClass Instance { get; private set; }
    2.  
    3. void OnEnable()
    4. {
    5.   Instance = this;
    6. }
    7. void OnDisable()
    8. {
    9.   Instance = null;     // keep everybody honest when we're not around
    10. }
    Anyone can get at it via
    MyClass.Instance.
    , but only while it exists.
     
  3. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,090
  4. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    2,274
    Also, ScriptableObjects are not meant as read-write storage during runtime. They DO behave like it in the Editor, but the Build will not work the same way.
     
    MaskedMouse, Bunny83 and MelvMay like this.
  5. LoyDtv

    LoyDtv

    Joined:
    Nov 1, 2021
    Posts:
    7
    I read the answers, after a bit of testing and debugging I realized that my idea of using scriptable objects to save data is not good, so I opted for this solution:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class TestScript : MonoBehaviour
    6. {
    7.     [SerializeField] SaveCoinsTakedSO[] saveCoinsTakeds;
    8.     private static bool[,] testBool = new bool[2,3];
    9.  
    10.     public void SaveTestMethod(int index)
    11.     {
    12.         testBool[index,0] = saveCoinsTakeds[index].GetCoinsTakedStatesReal(0);
    13.         testBool[index,1] = saveCoinsTakeds[index].GetCoinsTakedStatesReal(1);
    14.         testBool[index,2] = saveCoinsTakeds[index].GetCoinsTakedStatesReal(2);
    15.     }
    16.    
    17.     public void SetTestMetod(int index)
    18.     {
    19.         saveCoinsTakeds[index].SetCoinsTakedStates(0, testBool[index,0]);
    20.         saveCoinsTakeds[index].SetCoinsTakedStates(1, testBool[index,1]);
    21.         saveCoinsTakeds[index].SetCoinsTakedStates(2, testBool[index,2]);
    22.     }
    23.  
    24. }
     
  6. bugfinders

    bugfinders

    Joined:
    Jul 5, 2018
    Posts:
    1,375
    this is a common issue, so you either end up writing a reset function for when in the editor to revert to previous values, or, wondering why it doesnt save.. Inconsistencies between editor and build are hard - especially lighting ones!!