Scripts saving data between playmode sessions.

I messed up the title… it should be “Scripts saving data between playmodes activations”
That title is about as accurate as I can make it since I don’t have a good idea of what is going on.


Image Descriptions:
The first image is the scene view. I intend to be able to click on boxes that appear in either the left or the right inventory and have an image of the object show up in a confirmation window in the center. While it looks like it is working, I had not clicked anything during that ‘run’ and what is currently showing up is the information from the last item I clicked the previous time. When the scene begins a script sets up all of the data and one instantiate call creates a single game object on the canvas, containing all three displays. Those three displays themselves are prefabs.
8497709--1131239--Screenshot 2022-10-07 193907.png
The second image is the hierarchy as it appears during the first image.
8497709--1131242--Screenshot 2022-10-07 194110.png
The third image is the hierarchy as it appears in the prefab editor of what I put on the background canvas, mostly to emphasize that almost all of these are prefabs attached to prefabs.

Intended behavior:
The user should be able to click on any of the red boxes in the right side group. The selection should be represented in the middle window.

Current behavior:
Clicking any of the red boxes (buttons) on the right registers the correct information (verified with print statements), and the data gets all the way to the system that updates the red box on the confirmation window, but the text nor image update. When the scene playmode is ended and re-started, the confirmation window will show the data of the last clicked on item from the right inventory.

Notes:
I have had problems with prefabs before, it would not be unusual if I was somehow referencing the original prefab while trying to change the visible version of it.
Whether I have clicked boxes on the right or not, this code:

public void OnPointerEnter(PointerEventData eventData){
if(_item == null) {Debug.Log("No item found"); return;}
}

evaluates to true and exits.

I can post the code, but its a bit spread across a few files. I didn’t want to make this original post absurdly long.

Sounds like a promising thread to pull on. Some things are guaranteed to persist between play mode sessions, and some things sometimes persist:

Always persist:

  • Changes made to assets in code, including Materials, Textures, Prefabs, ScriptableObjects, etc… (as long as you made the changes to the actual asset and not an ephemeral in-memory version or copy of these things)
  • PlayerPrefs
  • Save files

As a rule I don’t recommend changing any assets in code, since that behavior differs between the editor (where the changes persist) and builds (where they do not).

Sometimes persist (can be reset due to domain reloads etc.)

  • Static variables
  • Static event subscriptions
  • Variables in editor scripts

Trust me, its not my objective to be modifying prefabs with scripts during runtime. As far as I can tell, any reference I try to make to the objects are just going wrong.

I just wrote this to ensure that I was getting the right references to things instead of using the editor…

    private GameObject ParentPanel;
    private InventoryBox ThisItemBox;
    private GameObject SliderManager;
    private SliderManager _sliderScript;

    private InventoryTransferManager _iTM;

    void Awake(){
        ParentPanel = this.gameObject.transform.parent.gameObject;
        GameObject BoxObject = this.gameObject.transform.GetChild(1).GetChild(0).gameObject;
        Debug.Log("Box? Name: " + BoxObject.name);
        ThisItemBox = BoxObject.transform.GetComponent<InventoryBox>();
        SliderManager = this.gameObject.transform.GetChild(1).GetChild(1).gameObject;
        _sliderScript = SliderManager.transform.GetComponent<SliderManager>();
        Debug.Log("End of transfer Awake");
        if(BoxObject == null) { Debug.Log("BoxObject is null");}
        if(ThisItemBox == null) { Debug.Log("ThisItemBox is null");}
        if(SliderManager == null) { Debug.Log("SliderManager is null");}
    }

and yet by the next time I try to use one the game says there is no valid reference
" The variable SliderManager of InventoryConfirmPanel has not been assigned. "

I am at a loss because the following code does nothing, every time it is accessed it prints false.

public void Show(){
ParentPanel = this.gameObject.transform.parent.gameObject;
Debug.Log("Confirm window is " + ParentPanel.activeInHierarchy);
Debug.Log("State: " + this.gameObject.activeInHierarchy);
if(this.gameObject.activeInHierarchy){
this.gameObject.SetActive(false);
}
else{
this.gameObject.SetActive(true);
}
}

You are always going to have trouble writing code like the above.

If you have more than one or two dots (.) in a single statement, you’re just being mean to yourself.

How to break down hairy lines of code:

http://plbm.com/?p=248

Break it up, practice social distancing in your code, one thing per line please.

“Programming is hard enough without making it harder for ourselves.” - angrypenguin on Unity3D forums

“Combining a bunch of stuff into one line always feels satisfying, but it’s always a PITA to debug.” - StarManta on the Unity3D forums

Beyond that, to find the specific issue,

When in doubt, print it out!™

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

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 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.

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 or iOS: https://discussions.unity.com/t/700551 or this answer for Android: https://discussions.unity.com/t/699654

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.

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://discussions.unity.com/t/839300/3

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

Thank you for your reply, though I literally just figured out what the issue was. Though your message does contain some important bits of information, and my code is definitely not finalized, especially after hours of trying to pinpoint a problem.
Also, this is the unity page for how to get a parent: Unity - Scripting API: Transform.parent

Anyways, the problem:

private void LoadMerchantBuySell(){
var display = Instantiate(MerchantBuySell, new Vector3(0,0,0), Quaternion.identity);
display.transform.SetParent(baseCanvas.transform,false);
InventoryTransferManager ITF = MerchantBuySell.GetComponent<InventoryTransferManager>();
ITF.Initialize(UniverseData.PlayerCharacter,UniverseData.PotionMerchant);
}

MerchantBuySell being a prefab containing all the prefabs and systems that I want to display upon the screen.

For those who don’t see it, I save the output of the instantiate, use that gameObject to set its position in the hierarchy.
Then, like someone with a lesson coming to them, I save the script which I want to reference as it is in the prefab.
Initializing based off of that meant that from all points forward I was referencing the version of everything that appeared in the prefab. Therefore all the relative parenting and child-ing, and public values set during the editor were possible, but were still being done to the versions of themselves which were in the original prefabs. Information that I initialized in an Awake() wasn’t being found because it was in a different ‘instance’ of the script.

What really helped:

int Returns the instance ID of the object. When used to call the origin object, this method returns a positive value. When used to call the instance object, this method returns a negative value.”
If that hadn’t existed I would have no idea what I was accessing.

Basically, when unsure if you are accessing a prefab or not, run:
Debug.Log(": " + this.gameObject.GetInstanceID());