【Unity】面向初学者的单例和静态变量使用

1,417 阅读5分钟

Unity Singleton 或 Singleton 通常是一个全局可访问的类,其所有对象在任何给定时间都引用单个实例。现在,一个单一实例可以有多个对象,但不同脚本在给定点对类的任何引用都将指向一个实例。

混乱?

阅读下一段后,您不会感到困惑。

创建[类的对象]时,将分配内存来存储其属性。对于单例,无论创建多少个对象,它们都指向相同的内存位置或以其他方式称为实例。

Unity singleton block diagram

Unity 单例框图

因此,为了避免混淆,我们只创建一个 Singleton 类的一个对象,并检查是否存在任何重复项并销毁它们。我们将在本教程的后面部分看到如何操作。

在本教程中,我们将了解如何使用 static 属性创建单例类,以及如何以正确的方式实现它们的用法。

可以通过声明类的公共静态对象将类创建为单例。使用此对象,您可以使用类名访问类中的任何变量或方法,而无需先引用它。

让我们看一些例子

public class example : MonoBehaviour 
{
    public static example my_object;

    float my_player_health=100;
}

在上面的代码中,示例类是一个单例,并且具有一个名为 my_object 的静态对象。现在,如果我想从其他脚本访问变量my_player_health,我可以这样做

public class test: MonoBehaviour
{
   
   void Start()
   {
      float player_health=example.my_object.my_player_health;
   }

}

可以读取和写入任何单一实例类的属性。但是,如果要更改 Unity Singleton 类的读取和写入属性,可以在声明静态对象时执行此操作。

下面是如何将单一实例设置为只读的示例。

public class example : MonoBehaviour 
{
    public static example my_object { get; private set; } ;

    float my_player_health=100;
}

如何查找和删除 Unity 单例的重复实例?

正如我们在介绍中所讨论的,单例类可以有多个对象,但它们都引用同一个实例。这增加了更多的混乱,并使实现单例变得困难。

为了避免这种混淆,我们可以添加一个代码来检查创建的实例是否是类的唯一活动实例。否则,实例可以自行销毁。

在一般的 C# 代码中,您可以使用类的构造函数来执行此操作,但 Unity 确实允许构造函数,因此我们可以将代码添加到 Awake 函数中。

public class example : MonoBehaviour 
{
    public static example my_object { get; private set; } ;

    float my_player_health=100;

     private void Awake()
    {
        if (my_object != null && my_object != this)
        {
            Destroy(this);
        } else {
            my_object = this;
        }
    }
}

在 Unity 中的何处使用单例?

避免同一类的多个实例

单例通常用于不同脚本可能需要的数据。例如,玩家数据。玩家数据由多个脚本出于不同目的访问。

UI 访问数据以显示[健康栏],场景管理器要求它加载场景,音频[管理器]要求它[播放音频]等等。

所有这些脚本都可以为播放器类创建一个实例并访问数据,但这将浪费内存。取而代之的是,您可以为玩家创建一个单一实例类,其中包含所有数据,如玩家健康、位置、弹药等。

所有其他脚本都可以使用玩家数据,而无需为类创建实例。

提高性能

部分程序员在Unity中习惯于广泛使用“[GameObject.Find]”。这非常耗费性能,如果您在不同的地方需要相同的游戏对象,则可以使用单例来避免它们。

例如,如果您需要根据分数更改游戏对象的颜色,则需要找到具有分数的游戏对象以读取值。

如果已将分数类设置为单例,则只需使用名称即可访问它,而无需查找游戏对象。

这是示例代码

public class example : MonoBehaviour 
{
    public static example my_object;

    float my_score;
}
//In a different script
public class test: MonoBehaviour
{
   
   void Start()
   {
      if(example.my_object.my_score>50)
     {
        myobject.material.color=color.red;
      }
   }

}

如果启用了写入访问权限,则可以从所需的任何其他脚本中设置分数值。

像这样

void update_score()
   {
      if(hit)
     {
        example.my_object.my_score-=10;
      }
   }

如何避免单例在场景加载时被破坏?

尽管单例非常方便,但重要的是要注意它在场景加载时会被销毁。因此,如果您在介绍场景中设置玩家生命值并切换到游戏场景,则所有数据都将消失。

Unity 提供了一种避免这种情况的方法,即在场景更改时不破坏单例。

为此,您只需在代码中添加一行。它被称为“[DontdestroyOnLoad]”。

public class example : MonoBehaviour 
{
    public static example my_object { get; private set; } ;

    float my_player_health=100;

     private void Awake()
    {
        if (my_object != null && my_object != this)
        {
            Destroy(this);
        } else {
            my_object = this;
        }
       DontDestroyOnLoad(this.gameObject);
    }
}

Unity 中的静态变量和方法

单例实际上是类的静态实例。与类类似,您还可以创建静态变量和静态方法。可以使用类名从任何地方或任何其他脚本访问静态变量或方法。

不需要对象引用即可访问静态属性。

要声明静态变量或方法,您需要使用关键字 static 类似于我们声明类的静态对象的方式。

以下是示例

静态变量

public static int level_count;

静态方法

public static void my_method()
{

}

假设它们是类示例的一部分。现在要访问数据,您只需使用类名即可引用它。

int my_value=example.level_count
example.my_method()

Unity 中的单例与静态变量用法

在数据存储方式方面,两者非常相似。根据您的要求,您可以决定是必须使用 Singleton 类还是静态变量。

让我们考虑一个例子来更好地理解。

如果您只需要所有脚本中的玩家健康变量,那么创建单例类是没有意义的,您只能有一个静态玩家健康变量。

如果需要播放器类中的所有方法和变量,则最好创建单例,而不是创建多个静态属性。

希望这可以清除有关 Unity 中单例的所有疑惑。