无法从 Unity3d 中的另一个类访问检查器值

jqu*_*404 2 c# unity-game-engine

我有两节课。一个叫做GameManager,另一个叫做Enemies。我在GameManager 中有两个变量,我已经从检查器currentLevel=1totalEnemy=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;
    }
Run Code Online (Sandbox Code Playgroud)

我试图从这样的Enimes类中访问这两个变量;但每次它给我curLevel = 0,但我期待得到curLevel = 1。我做错了什么?

// Enemies.cs

    void Start () {
        Debug.Log (GameManager.get().curLevel); // always output = 0
    }
Run Code Online (Sandbox Code Playgroud)

Mat*_*att 5

线private static GameManager instance = new GameManager();是问题。

当脚本附加到 a 时GameObject,脚本类型的实例被引用为this脚本内部。换句话说,如果同一个脚本附加到多个GameObjects 上,则可以有多个相同类型的实例。

因此,具体的实例curLevel = 1,你在检查器中设置为一个实例连接到类型的具体 GameObject。这意味着应该将其称为this在脚本内部。

如果您GameManager在代码中声明了一个新的as实例,您基本上会忽略 Inspector 中的所有值,因为 thestatic GameManager instance指向的实例与您在 Inspector 中为其设置值的实例不同。

为了使用您使用 Inspector 声明的特定实例,您应该执行以下操作。

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,我更改Start()Awake(). 这是因为您是从其他脚本中引用此方法中初始化的值,并且您不能保证在运行时中的Start()不同之间首先调用哪个MonoBehaviours。但是,Unity 保证Awake()始终早于Start(). 此外,Unity 的最佳实践是在 中初始化自初始化变量Awake(),并在 中初始化依赖于其他脚本的变量Start()根据此执行顺序。

最后,就会出现问题,当有多个GameObject具有GameManager作为其在场景中的组成部分。考虑一种情况,您有两个这样的对象。当场景加载时,每个脚本都会调用Awake(),并且它们都将设置private static GameManager instance;为两个中的每一个this。结果将是一个被另一个覆盖。

你可以说你会小心使用这个脚本并确保只有一个GameObject脚本作为它的组件。但是,您应该始终编写您的代码,就好像不了解您的代码的人可以不假思索地使用它一样,并且可以很容易地检测到其他新项目的人的愚蠢错误。

编辑:

为了回应 OP 的评论,我添加了代码来处理在项目中多次初始化此类型的情况。除了@Kardux的建议,我补充Debug.LogError()是因为我不想让项目默默解决事情。如果出现问题,我希望得到通知。

如果您Singleton在项目中经常使用s,您可能希望有一个父级abstract class Singleton来处理所有子级Singleton的实例检查过程,并GameManager继承自Singleton.

但是,请Singleton谨慎使用,因为如果误用它会被认为是一种糟糕的设计模式。(而且我不知道如何正确使用它,所以我避免使用它。)

  • 使用实例时的一个好做法是检查实例是否已经存在(以防您只想要一个作为单例方法):`if(instance == null) { instance = this; } else { DestroyImmediate(gameobject); }`。如果您忘记从游戏的其他场景中删除组件,这可以为您省去麻烦:) (2认同)