Home > Back-end >  Why do my variables keep going back to 0 and why do my variables look different on the screen then i
Why do my variables keep going back to 0 and why do my variables look different on the screen then i

Time:06-04

So for some reason when I try to access variables from another script the text is showing what I put but when I change things in the editor the accrual values don't change. even if in the code I put playerPoints to 9 when I run it its 0 during a collision. I have all the scripts and objects connected up, and when I try to flip where the variables are, make them in the destroy script, for some reason, it doesn't work. it wont let me use public BallsText bt; it will return an error even though it works fine for the other. I'm sorry if all of these are really basic questions but I've looked as far as I can on the internet and I cant find anything, so any help would be appreciated.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class BallsText : MonoBehaviour
{
    public Destroy destroy;
   // public int playerPoints = 0;
    //public int enemyPoints = 0;

    //int playerPoints = 0;
    //int enemyPoints = 0;

    public Text playerPointsText;
    public Text enemyPointsText;

    void Update()
    {
        playerPointsText.text = destroy.playerPoints.ToString();
        enemyPointsText.text = destroy.enemyPoints.ToString();
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Destroy : MonoBehaviour
{    
    public int playerPoints = 9;
    public int enemyPoints = 0;

    void OnCollisionEnter2D(Collision2D col)
    {
        if (this.name == "Destroy.cs")
        {
            Debug.Log("i have a doughnut");
        }

        if (col.gameObject.name == "Player")
        {
            Debug.Log(playerPoints);
            playerPoints  ;
            Debug.Log(playerPoints);
            Debug.Log("at least im a dounut");
            Destroy(this.gameObject);
            
        }
        else if (col.gameObject.name == "Enemy Zone")
        {

            enemyPoints  ;
            Destroy(this.gameObject);
        }
    }
}

CodePudding user response:

You can hide them from the inspector's view with HideInInspector while still being public.

[HideInInspector] public int playerPoints = 9;

You can also use the reset script to return the setting of public numbers to the first state.

enter image description here

CodePudding user response:

Like Everts said, you had the value at zero and created an instance of the script. Now when you change the script Unity will serialize (save) all the public/serialized fields, then reload the script, then deserialize (load) all the public/serialized fields it previously saved. This means your values are locked into the instance that you previously used. If you make a new instance, you get the current values, and if you reset the script you'll get the current values.

This has also bitten me enough times that I don't set default values when variables are declared anymore. If you have a value that you want to use, set those values in Start() or Awake().

The advantage here is that whatever is in the script will get overwritten when play mode starts. The disadvantage is that you can't customize those values on a per-instance basis anymore, because all instances of the script will all load the same default values when play mode starts. If this matters to you, and you want to be able to customize those values, then unfortunately you'll need to go to each script and change those values manually.

If you use a property with an automatic backing field then you won't be able to see it in the editor

public class Destroy : MonoBehaviour
{
    
    public int playerPoints{get; set;} = 9; // Can't see this in the editor
    public int enemyPoints{get; set;} = 0; // Can't see this in the editor

If you use a property with an explicit backing field then you can expose the backing field to the editor with the [SerializeField] tag, but then you've got the same problem you've got now - the editor will serialize that field and subsequent changes to the script won't affect instances:

public class Destroy : MonoBehaviour
{
    [SerializeField]
    private int playerPoints_ = 9; // Instances will "lock in" values and later changes to the script here won't take effect
    [SerializeField]
    private int enemyPoints_ = 0; // Instances will "lock in" values and later changes to the script here won't take effect
    public int playerPoints
    {
        get=>playerPoints_; 
        set{playerPoints_ = value;}
    }
    public int enemyPoints
    {
        get=>enemyPoints_; 
        set{enemyPoints_ = value;}
    }

If you keep the fields public (and thus exposed to the editor) but set the values at runtime in Awake() or Start() then you can see the values in the editor but the editor values for all instances will be overridden when play mode starts:

public class Destroy : MonoBehaviour
{
    
    public int playerPoints; // Doesn't matter what the value is here on instantiation becuase you'll override it on Awake()
    public int enemyPoints; // Doesn't matter what the value is here on instantiation becuase you'll override it on Awake()
    
    public void Awake()
    {
        playerPoints = 9; // Will override *every* instance's values with this.
        enemyPoints = 0; // Will override *every* instance's values with this.
    }

:EDIT:

I'll add too that repeatedly polling is wasteful. If you use events then you can subscribe and get notifications when there's something to see. Consider instead:

public class Destroy : MonoBehaviour
{
    private int playerPoints = 9;
    private int enemyPoints = 0;

    public System.EventHandler<int> OnPlayerPointsChanged;
    public System.EventHandler<int> OnEnemyPointsChanged;

    
void OnCollisionEnter2D(Collision2D col)
{
    
    if (this.name == "Destroy.cs")
    {
        Debug.Log("i have a doughnut");
    }

    if (col.gameObject.name == "Player")
    {
        Debug.Log(playerPoints);
        playerPoints  ;
        OnPlayerPointsChanged?.Invoke(this, playerPoints);
        Debug.Log(playerPoints);
        Debug.Log("at least im a dounut");
        Destroy(this.gameObject);
        
    }
    else if (col.gameObject.name == "Enemy Zone")
    {

        enemyPoints  ;
        OnEnemyPointsChanged?.Invoke(this, enemyPoints);
        Destroy(this.gameObject);
    }
}
}

Now Destroy has two public events that fire when the public or enemy points change. Anyone subscribing to those events will get notified when the points change, and part of the event notification is the current point value.

Then your other script subscribes to the events and does whatever they need to when they receive that event. Here they'll convert the points .ToString() and update the Text values:

public class BallsText : MonoBehaviour
{
    public Destroy destroy;
    public Text playerPointsText;
    public Text enemyPointsText;

    private void Start()
    {
        destroy.OnPlayerPointsChanged  = PlayerPointsChanged;
        destroy.OnEnemyPointsChanged  = EnemyPointsChanged;
    }

    public void PlayerPointsChanged(object sender, int points)
    {
        playerPointsText.text = points.ToString;
    }

    public void EnemyPointsChanged(object sender, int points)
    {
        enemyPointsText.text = points.ToString();
    }
}

Last note here is that your Destroy script increments enemyPoints but then also immediately destroys the gameObject, so I don't see the point in incrementing enemyPoints unless there's something else accumulating enemy points. With the subscription model that's totally doable - you could have something else subscribing to the Destroy script models and they'll get notifications before the script self-destructs.

  • Related