Menu
CoddyTech

可序列化类

本课属于 Coddy Unity 学习旅程的“Unity 游戏架构”单元。第 18 课,共 64 课。

设置很多的脚本会在 Inspector 中显示一长串扁平列表。普通的 C# 类可以把这些设置分组:为它标记 [System.Serializable],该类的字段就会显示为一个折叠面板,面板以字段命名,里面包含这个类的各个字段。一个类可以供多个字段和多个脚本使用:

[System.Serializable]
public class Stats
{
    public float speed = 2f;
    public int damage = 10;
}

public class Enemy : MonoBehaviour
{
    public Stats normal;
    public Stats enraged;
    private Stats current;

    void Start()
    {
        current = normal;
    }

    void Update()
    {
        transform.Translate(0f, 0f, current.speed * Time.deltaTime);
    }

    public void Enrage()
    {
        current = enraged;
    }
}

Inspector 会显示两个折叠面板,Normal 和 Enraged,每个面板都有 Speed 和 Damage。每个 Enemy 都有自己的两个 Stats 对象,并在自己的 Inspector 中填写这些对象的值,代码通过 normal.speed 来读取它们。Unity 加载脚本时会创建这些对象,因此即使没有 = new Stats(),normal 也永远不会是 null;字段初始化器(2 和 10)就是新 Enemy 的初始值。结构体也可以用同样的特性,[System.Serializable] public struct Range { public float min; public float max; },并且像 Vector3 一样,在赋值时会被复制。

需要这个特性,是因为 Unity 只会保存它被告知要保存的类型。没有它,这个类在 Inspector 中不可见:没有折叠面板,在 Inspector 中设置的值不会被保存,Unity 也不会创建对象,所以 normal.speed 会抛出 NullReferenceException。在类内部,通常的 Inspector 规则仍然适用:使用 public 字段或带有 [SerializeField] 的 private 字段,不能使用属性或静态字段。

challenge icon

挑战

简单

将每个光束的设置归到一个结构体中。

  1. 在 Flashlight 上方,声明 public struct Beam,其中包含公共的 Color color、float range 和 float angle。添加 [System.Serializable],这样 Inspector 中就会显示它。
  2. 将六个分散的字段替换为 public Beam wide; 和 public Beam focused;。
  3. 在 Apply() 中,根据 focusedOn ? focused : wide 设置灯光的三个值。

按“开始”,然后按 Space:光束就能照到水晶。然后点击“检查”。

亲手试试

using UnityEngine;
using UnityEngine.InputSystem;

public class Flashlight : MonoBehaviour
{
    public Color wideColor = Color.white;
    public float wideRange = 5f;
    public float wideAngle = 60f;
    public Color focusedColor = Color.white;
    public float focusedRange = 10f;
    public float focusedAngle = 30f;
    private bool focusedOn;
    private Light lamp;

    void Start()
    {
        lamp = GetComponent<Light>();
        Apply();
    }

    void Update()
    {
        if (Keyboard.current.spaceKey.wasPressedThisFrame)
        {
            focusedOn = !focusedOn;
            Apply();
        }
    }

    void Apply()
    {
        lamp.color = focusedOn ? focusedColor : wideColor;
        lamp.range = focusedOn ? focusedRange : wideRange;
        lamp.spotAngle = focusedOn ? focusedAngle : wideAngle;
    }
}

Unity 游戏架构中的所有课

自己练习: Unity 在线编辑器