简体   繁体   中英

Inspector value cannot access from another class in Unity3d

I have two classes. One called GameManager and another one Enemies . I have two variables in GameManager which I have changed from inspector currentLevel=1 and totalEnemy=10.

// GameManager.cs
    private static GameManager instance = new GameManager();
    public static GameManager get(){ return instance; }

    public int currentLevel;
    public int curLevel { get; set; }
    public int totalEnemy;
    public int totLevel { get; set; }


    void Start () {
        curLevel = currentLevel;
        totLevel = totalEnemy;
    }

I'm trying to access these two variable from Eneimes class like this; but everytime it gives me curLevel = 0 , but I'm expecting to get curLevel = 1 . What I'm doing wrong?

// Enemies.cs

    void Start () {
        Debug.Log (GameManager.get().curLevel); // always output = 0
    }

The line private static GameManager instance = new GameManager(); is the issue.

When a script is attached to a GameObject , an instance of the type of the script is referenced as this inside the script. In other words, there can be multiple instances of same type if the same script is attached to multiple GameObject s.

Therefore, the specific instance that have curLevel = 1 as you set in the Inspector is an instance of the type attached to the specific GameObject . This means the one should be referred to as this inside the script.

If you declare a new instance of GameManager as in your code, you are basically ignoring all values in the Inspector because the static GameManager instance is pointing to a different instance than the instance you set values for in the Inspector.

In order to use the specific instance that you declared using the Inspector, you should do the following.

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

public class GameManager : MonoBehaviour
{
    private static GameManager instance;
    public static GameManager get() { return instance; }

    public int currentLevel;
    public int curLevel { get; set; }
    public int totalEnemy;
    public int totLevel { get; set; }

    void Awake()
    {
       if (instance == null) 
       {
          instance = this;
       }
       else 
       {
          Debug.LogError(string.Format("GameManager.Awake(): More than one instances of this type {0} is being initialised but it's meant to be Singleton and should not be initialised twice. It is currently being initialised under the GameObject {1}.", this.GetType(), this.gameObject.name));
          Destroy(gameObject);
       }

        curLevel = currentLevel;
        totLevel = totalEnemy;
    }
}

Note that I changed Start() to Awake() . This is because you are referring to values initiliased in this method from other scripts, and you cannot guarantee which Start() is called first between different MonoBehaviours in the runtime. However, Unity guarantees that Awake() is always called earlier than Start() . Further, it is Unity's best practice to initialise self-initialisable variables in Awake() , and initialise variables dependent on other scripts in Start() because of this execution order.

Lastly, there will be problems when there are multiple GameObject that has GameManager as its component in your scene. Consider a case where you have two such objects. when the scene is loaded, each of the script will call Awake() , and both of them will set private static GameManager instance; to each of the two this . The result would be one is overriden by another.

You could say that you will be careful to use this script and make sure only one GameObject has this script as its component. However, you should always write your code as if someone who do not know about your code can use it without thinking, and stupid mistakes of other people new to the project could be easily detected .

EDIT:

To respond to the OP's comment, I added code to handle when this type is initialised more than once in the project. In addition to @Kardux 's suggestion, I added Debug.LogError() because I do not want the project to silently solve things. If a problem happens, I want to get notified of it.

If you are using Singleton s frequently in your project, you might want to have a parent abstract class Singleton that handles this instance checking process for all child Singleton s, and have GameManager inherit from Singleton .

However, use Singleton with care as it is considered a bad design pattern if misused. (And I don't know how to use it properly so I avoid using it.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM