在Unity中是否可能创建一个“全局”类实例?

4

抱歉问一个愚蠢的问题,我是Unity脚本的新手。

我需要创建一个类实例,它需要被多个场景中的组件共享和访问。

我可以轻松地创建一个静态类并在组件脚本中使用它,但我的类具有重网络和处理负载,并且由于我只需要处理一次,因此仅创建单个实例会更好。

是否可能做类似的事情? 例如:

// The class which I need to create and share
public class DemoClass {
     MyExampleClass example;
     public DemoClass (){
            example = new MyExampleClass ();
      }
     public MyExampleClass GetClassInstance(){
            return example;
      }
 }

// The "Global Script" which I'll instantiate the class above
public class GlobalScript{
    MyExampleClass example;
    public void Start(){
         DemoClass demoClass = new DemoClass();
         example = demoClass.GetClassInstance();
     }

     public MyExampleClass getExample(){
         return example;
      }
  }

  // Script to insert in any component
  public class LoadMyClass : MonoBehaviour {
      public void Start(){
          // this is my main issue. How can I get the class Instance in the GlobalScript?
          // the command below works for Static classes.
          var a = transform.GetComponent<GlobalScript>().getExample(); 
       }
    }

任何提示都将不胜感激。
谢谢。

我猜你在用C#编程? - krillgar
@krillgar 抱歉,我在使用C#进行编码。刚刚添加了标签。 - catastrophic error
所以,如果您正在使用C#编码-这绝对不是一个Unity的问题。您提出了太低层次的问题。就像这是一个完全的C#问题。 - TomTom
2
@TomTom,实际上,这与Unity有关。当代码非常涉及检索组件的变换时,它不能被描述为“纯C#”。 - ketchupisred
2
此外,Unity3D支持C#作为其主要编程语言之一。如果OP说他/她正在使用Unity,并且在他/她的场景中使用了一个设计模式,那么这肯定是一个Unity问题。 - ketchupisred
3个回答

7

从简单的层面上看,单例模式似乎是可行的方案(如上所述)。虽然这不是来自Unity官方资源,但以下链接提供了一个教程和一个很好记录的代码示例。

http://clearcutgames.net/home/?p=437

单例示例:

using UnityEngine;

public class Singleton : MonoBehaviour
{
    // This field can be accesed through our singleton instance,
    // but it can't be set in the inspector, because we use lazy instantiation
    public int number;

    // Static singleton instance
    private static Singleton instance;

    // Static singleton property
    public static Singleton Instance
    {
        // Here we use the ?? operator, to return 'instance' if 'instance' does not equal null
        // otherwise we assign instance to a new component and return that
        get { return instance ?? (instance = new GameObject("Singleton").AddComponent<Singleton>()); }
    }

    // Instance method, this method can be accesed through the singleton instance
    public void DoSomeAwesomeStuff()
    {
        Debug.Log("I'm doing awesome stuff");
    }
}

如何使用单例模式:

// Do awesome stuff through our singleton instance
Singleton.Instance.DoSomeAwesomeStuff();

通常,您希望将 Singleton 类的构造函数设为“私有”,以便仅通过 public static Singleton Instance 获取器方法才能创建新实例。 - Scott Chamberlain
@ScottChamberlain 我同意。关键词是“通常”。然而,当类继承自MonoBehaviour时使用构造函数会导致构造函数在不需要的时候被调用,在许多情况下可能会导致Unity崩溃。 - ketchupisred
我的错,我应该说使用protected而不是private,你是正确的,私有可能会导致它崩溃。 - Scott Chamberlain

2

阅读方便命名的单例。它们是一种预先设计的Unity软件设计模式,用于满足只有一个这样的实例的需求。网上有许多Unity特定的通用示例。

从链接中包含的“为什么使用Singleton?”您可能会问。首先,什么是单例?它是一种设计模式,将类的实例化限制为一个对象。如果您在这里,您可能想要基本上使用它来实现全局变量。对于任何其他用途,只需将其作为起点即可。 在Unity中使用单例的优点而不是静态参数和方法主要有:

  1. 静态类在首次引用时进行惰性加载,但必须具有空的静态构造函数(或为您生成一个)。这意味着如果您不小心并且不知道自己在做什么,很容易搞砸并破坏代码。至于使用Singleton模式,您已经自动执行了许多不错的操作,例如使用静态初始化方法创建它们并使它们不可变。
  2. 单例可以实现接口(静态不行)。这允许您构建合同,您可以将其用于其他单例对象或任何其他您想要抛出的类。换句话说,您可以有一个游戏对象,其中包含其他组件以进行更好的组织!
  3. 您还可以从基类继承,这是无法使用静态类完成的。

对于Unity特别来说,还有一种额外的概念,即所述脚本将被分配给通常会在新级别加载时被销毁的游戏对象。这就是为什么通常会有一种Unity-centric方法来防止在加载新级别时破坏游戏对象的原因。

DontDestroyOnLoad(singleton);

我强烈建议您熟悉设计模式,特别是与游戏开发相关的模式。当应用在正确的位置时,它们非常有趣且极其强大。


3
如果链接失效,仅提供链接作为答案通常会被视为不好的行为。您可以将相关文本与链接一起添加到答案中,以便更好地解释。原文如下:Link only answers are generally frowned upon in the event the link becomes dead. Could you please add the relevant text into the answer along with the link as well? - tddmonkey
是的,这解决了我想要实现的内容。我会深入研究这个。 我只是想知道在Unity中是否有一个“非黑客”解决方案。谢谢。 - catastrophic error
1
如果你想让你的进程承担这个责任,我认为有类似的方法可以做到这一点,但我不想建议比这里包含的更未经测试的东西。例如,你可以在开场景中创建该对象,并仅调用: DontDestroyOnLoad(this) 但是,另一个开发人员(包括你自己)可能会将另一个相同的预制件投入场景,那么那意味着什么?我坚信,在这种情况下,最好提供一个通用的标准,而不是自己动手。 - Al Baker

0

与其使用单例模式,我更倾向于考虑只使用ScriptableObjects,它们通常用于全局数据存储。

一个class的例子:

using UnityEngine;
[CreateAssetMenu(menuName = "Float Variable")]
public class FloatVariable : ScriptableObject
{
    public float value;
}

您可以这样实现:
using UnityEngine;
public class PlayerHealth : MonoBehaviour

{
    public FloatVariable playerHealth;
    void Start()
    {
        Debug.Log("The player's health is: " + playerHealth.value);
    }
    void TakeDamage(float damageTaken)
    {
        playerHealth.value -= damageTaken;
    }
}

通过检查器,您可以更改ScriptableObject上的值,然后可以将其拖放到游戏对象中的ScriptComponent中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接