前篇链接:Unity之C#学习笔记(10):接口和多态 Interfaces and Polymorphism
静态类型(Static)用一句话概括就是:在程序运行期间只会被创建一份的通用内容,用关键字static修饰,主要包括静态类、静态变量和静态方法。我们这里主要讨论静态变量和静态方法。这一篇博文给出了比较详细的介绍,可以作为补充:C# 之 static的用法详解。
静态变量和方法是类中的成员。与一般的成员变量和方法不同的是,无论我们创建多少个类的对象,静态成员都只会存在一个。换一种说法,静态成员不依赖于类的具体对象。从内存分配角度来说,静态成员的存储位置和非静态成员不同,当创建新对象时,会在堆中为非静态成员新分配空间。而静态成员从最初就存储在其他位置,只会有一个存储空间。
静态成员有一个特性:可以通过类名.变量名(方法名)直接访问。这个特性对于我们在Unity中编程是非常便利的。对于非静态成员,我们要先通过GetComponent获得类的实例,再访问或调用对应的变量或方法,而静态成员则通过类名即可调用。
对于静态方法,因为它不属于任何类的示例,所以不能调用任何非静态的成员。
静态成员可以通过静态构造函数来初始化,我们通过一个Employee雇员类的例子来说明。
public class Employee
{
public int employeeID;
public string firstName, lastName;
public int salary;
public static string companyName;
public Employee()
{
Debug.Log("Constructor called");
employeeID = 0;
lastName = "张";
firstName = "三";
salary = 8000;
}
static Employee()
{
Debug.Log("Static Constructor called");
companyName = "Tencent";
}
}
雇员有一些各不相同个人的信息,还有一个公共的公司名。public Employee()是Employee类的构造函数,而static Employee()就是它的静态构造函数,可以将所有静态成员在这里初始化。我们知道,构造函数会在每次创建一个新的类的对象时被调用。而静态构造函数不同,它会在创建第一个类的对象,或第一次访问类中的静态成员时被调用。 且对于第一种情况,静态构造函数早于构造函数被调用。
// 在Start中
Employee employee1 = new Employee();
Employee employee2 = new Employee();
// 输出:
// Static Constructor called
// Constructor called
// Constructor called
// 在Start中
Debug.Log(Employee.companyName);
// 输出:
// Static Constructor called
// Tencent
正如定义,静态成员适用于那些不依赖于类的实例,只需要一份通用实例的成员。举个例子,用于计数的变量count和打印提示信息的方法print就可以声明为静态成员。现在就来看这样一个例子。
在这个例子中,我们要通过按键动态地生成Enemy,这些Enemy经过3秒就会自动销毁。我们还要设置一个Text的UI组件,显示当前存活的Enemy数量。
首先搭建结构。创建一个敌人的预制体用于动态生成,并绑一个脚本Enemy。创建一个Text,用于展示敌人数量。创建Spawn Manager和UI Manager空物体,并挂载相应的脚本SpawnManager和UIManager,用于控制生成敌人和UI。
在SpawnManager脚本中,我们规定当按下空格键时,就生成一个Enemy:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnManager : MonoBehaviour
{
public GameObject enemyPrefab; // 面板上引用到预制体EnemyPrefab
public static int activeEnemyCount;
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Instantiate(enemyPrefab);
}
}
}
在UIManager中,定义一个UpdateEnemyCount函数:(涉及UI操作注意using UnityEngine.UI)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; // 注意
public class UIManager : MonoBehaviour
{
public Text enemyCount; // 面板上引用到Enemy Count的Text控件
public void UpdateEnemyCount()
{
enemyCount.text = "Active Enemy: " + SpawnManager.activeEnemyCount;
}
}
在Enemy脚本中,当被创造时(OnEnable),计数加一,刷新UI,并使其3秒后被销毁;销毁时(OnDisable),计数减一,刷新UI:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy : MonoBehaviour
{
private UIManager _ui;
private void OnEnable()
{
SpawnManager.activeEnemyCount++;
_ui = GameObject.Find("UI Manager").GetComponent<UIManager>();
_ui.UpdateEnemyCount();
Die();
}
private void OnDisable()
{
SpawnManager.activeEnemyCount--;
_ui.UpdateEnemyCount();
}
private void Die()
{
Destroy(this.gameObject, 3); // 3秒后销毁物体
}
}
正确设置好所有引用,运行:
可以看到,我们多次按下空格,生成了多个Enemy,它们过3秒消失。计数器也正确显示了。
在下一节,我们来学习属性(Properties)。