为了提高Unity开发的效率,Unity提供了一些编辑器的扩展,使用好这些拓展,有助于开发效率的提高.

🚗一、OnDrawGizmos

在Unity开发里,OnDrawGizmos 是一个特别的方法,主要用于在场景视图中绘制可视化辅助工具,这些可视化元素有助于开发者在编辑场景时更好地理解和调试游戏对象的行为和属性。下面详细介绍其常见用途:

1. 显示游戏对象的边界

你可以借助 OnDrawGizmos 方法绘制游戏对象的边界框,以此来查看碰撞体或者对象的实际范围。例如,绘制一个简单的立方体边界框:

using UnityEngine;

public class BoundsVisualizer : MonoBehaviour
{
    void OnDrawGizmos()
    {
        // 设置绘制的颜色为黄色
        Gizmos.color = Color.yellow;
        // 获取碰撞体组件
        Collider collider = GetComponent<Collider>();
        if (collider != null)
        {
            // 绘制碰撞体的边界框
            Gizmos.DrawWireCube(transform.position + collider.center, collider.size);
        }
    }
}

2. 显示路径点

当设计游戏中的移动路径时,你可以使用 OnDrawGizmos 方法绘制路径点,从而直观地查看路径的走向。示例如下:

using UnityEngine;

public class PathVisualizer : MonoBehaviour
{
    public Transform[] waypoints;

    void OnDrawGizmos()
    {
        // 设置绘制的颜色为绿色
        Gizmos.color = Color.green;
        if (waypoints != null && waypoints.Length > 0)
        {
            for (int i = 0; i < waypoints.Length; i++)
            {
                if (waypoints[i] != null)
                {
                    // 绘制路径点
                    Gizmos.DrawSphere(waypoints[i].position, 0.2f);
                    if (i < waypoints.Length - 1 && waypoints[i + 1] != null)
                    {
                        // 绘制路径点之间的连线
                        Gizmos.DrawLine(waypoints[i].position, waypoints[i + 1].position);
                    }
                }
            }
        }
    }
}

3. 显示射线方向

在调试射线检测时,你可以使用 OnDrawGizmos 方法绘制射线的方向,从而确认射线是否按照预期发射。示例代码如下:

using UnityEngine;

public class RayVisualizer : MonoBehaviour
{
    public float rayLength = 10f;

    void OnDrawGizmos()
    {
        // 设置绘制的颜色为红色
        Gizmos.color = Color.red;
        // 绘制射线
        Gizmos.DrawRay(transform.position, transform.forward * rayLength);
    }
}

4. 显示目标点

当游戏对象有特定的目标点时,你可以使用 OnDrawGizmos 方法绘制目标点,以便在场景中直观地查看。示例如下:

using UnityEngine;

public class TargetVisualizer : MonoBehaviour
{
    public Transform target;

    void OnDrawGizmos()
    {
        // 设置绘制的颜色为蓝色
        Gizmos.color = Color.blue;
        if (target != null)
        {
            // 绘制目标点
            Gizmos.DrawSphere(target.position, 0.2f);
            // 绘制从当前对象到目标点的连线
            Gizmos.DrawLine(transform.position, target.position);
        }
    }
}

总之,OnDrawGizmos 方法在Unity开发里是一个非常实用的调试工具,它能帮助开发者更直观地理解游戏对象的状态和行为,提升开发效率。

🚕二、OnInspectorGUI

在Unity开发中,OnInspectorGUI 是一个自定义编辑器脚本里的方法,它的作用是自定义检视面板(Inspector)的显示内容和交互逻辑。下面为你详细介绍其常见用途:

1. 自定义属性显示

一般而言,Unity默认会在检视面板展示脚本里的公共属性。不过,借助 OnInspectorGUI 方法,你能够以独特的方式呈现这些属性,比如对属性分组、添加描述信息等。以下是一个简单的示例:

using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(MyScript))]
public class MyScriptEditor : Editor
{
    public override void OnInspectorGUI()
    {
        // 获取目标脚本
        MyScript myScript = (MyScript)target;

        // 绘制标题
        EditorGUILayout.LabelField("自定义属性显示", EditorStyles.boldLabel);

        // 绘制一个整数属性
        myScript.intValue = EditorGUILayout.IntField("整数属性", myScript.intValue);

        // 绘制一个浮点数属性
        myScript.floatValue = EditorGUILayout.FloatField("浮点数属性", myScript.floatValue);

        // 应用修改
        if (GUI.changed)
        {
            EditorUtility.SetDirty(myScript);
        }
    }
}

public class MyScript : MonoBehaviour
{
    public int intValue;
    public float floatValue;
}

在上述代码中,MyScriptEditor 类继承自 Editor,并且重写了 OnInspectorGUI 方法。通过这种方式,我们可以按照自己的需求绘制 MyScript 类的属性。

2. 添加自定义按钮和交互元素

你可以在检视面板添加自定义按钮、滑块等交互元素,从而执行特定的操作。例如,添加一个按钮用于重置属性:

using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(MyScript))]
public class MyScriptEditor : Editor
{
    public override void OnInspectorGUI()
    {
        MyScript myScript = (MyScript)target;

        // 绘制默认的检视面板
        DrawDefaultInspector();

        // 添加一个按钮
        if (GUILayout.Button("重置属性"))
        {
            myScript.intValue = 0;
            myScript.floatValue = 0f;
            EditorUtility.SetDirty(myScript);
        }

        if (GUI.changed)
        {
            EditorUtility.SetDirty(myScript);
        }
    }
}

public class MyScript : MonoBehaviour
{
    public int intValue;
    public float floatValue;
}

在这个例子中,我们添加了一个名为“重置属性”的按钮,点击该按钮就会把 MyScript 类的属性重置为默认值。

3. 动态显示和隐藏属性

依据特定条件,你可以动态显示或者隐藏某些属性。例如,根据一个布尔值来决定是否显示某个属性:

using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(MyScript))]
public class MyScriptEditor : Editor
{
    public override void OnInspectorGUI()
    {
        MyScript myScript = (MyScript)target;

        // 绘制布尔属性
        myScript.showExtraProperty = EditorGUILayout.Toggle("显示额外属性", myScript.showExtraProperty);

        // 根据布尔值决定是否显示额外属性
        if (myScript.showExtraProperty)
        {
            myScript.extraValue = EditorGUILayout.IntField("额外属性", myScript.extraValue);
        }

        if (GUI.changed)
        {
            EditorUtility.SetDirty(myScript);
        }
    }
}

public class MyScript : MonoBehaviour
{
    public bool showExtraProperty;
    public int extraValue;
}

在这个示例中,只有当 showExtraPropertytrue 时,才会显示 extraValue 属性。

4. 验证和限制属性值

你可以在 OnInspectorGUI 方法里对属性值进行验证和限制,防止用户输入无效的值。例如,限制一个整数属性的取值范围:

using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(MyScript))]
public class MyScriptEditor : Editor
{
    public override void OnInspectorGUI()
    {
        MyScript myScript = (MyScript)target;

        // 绘制整数属性并限制取值范围
        myScript.intValue = Mathf.Clamp(EditorGUILayout.IntField("整数属性", myScript.intValue), 0, 100);

        if (GUI.changed)
        {
            EditorUtility.SetDirty(myScript);
        }
    }
}

public class MyScript : MonoBehaviour
{
    public int intValue;
}

在这个例子中,intValue 属性的取值范围被限制在 0 到 100 之间。

总之,OnInspectorGUI 方法为开发者提供了强大的自定义检视面板的能力,能够让开发者依据具体需求灵活地展示和操作脚本属性,提高开发效率和用户体验。

🚙三、OnSceneGUI

在Unity开发里,OnSceneGUI 是一个在自定义编辑器脚本中使用的方法,主要用于在场景视图(Scene View)中创建交互界面和进行可视化操作。下面详细介绍其常见用途:

1. 场景视图中的交互操作

你可以在场景视图里添加交互元素,像手柄、按钮等,让开发者能直接在场景中操作游戏对象。例如,创建一个可以在场景中拖动的手柄来改变游戏对象的位置:

using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(MyScript))]
public class MyScriptEditor : Editor
{
    private Vector3 handlePosition;

    void OnSceneGUI()
    {
        MyScript myScript = (MyScript)target;

        // 获取手柄的位置
        handlePosition = myScript.transform.position;

        // 开启手柄交互
        Handles.color = Color.red;
        handlePosition = Handles.PositionHandle(handlePosition, Quaternion.identity);

        // 如果手柄位置改变,更新游戏对象的位置
        if (GUI.changed)
        {
            Undo.RecordObject(myScript.transform, "Move Object");
            myScript.transform.position = handlePosition;
            EditorUtility.SetDirty(myScript);
        }
    }
}

public class MyScript : MonoBehaviour
{
    // 这里可以添加其他属性
}

在上述代码中,Handles.PositionHandle 方法创建了一个可拖动的手柄,开发者能直接在场景中拖动该手柄来改变游戏对象的位置。

2. 绘制自定义图形和标记

在场景视图中绘制自定义的图形和标记,辅助开发者更好地理解游戏对象的属性和关系。例如,绘制一条从当前对象到目标对象的连线:

using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(MyScript))]
public class MyScriptEditor : Editor
{
    public override void OnSceneGUI()
    {
        MyScript myScript = (MyScript)target;

        if (myScript.target != null)
        {
            // 设置绘制颜色为蓝色
            Handles.color = Color.blue;
            // 绘制从当前对象到目标对象的连线
            Handles.DrawLine(myScript.transform.position, myScript.target.position);
        }
    }
}

public class MyScript : MonoBehaviour
{
    public Transform target;
}

在这个例子中,Handles.DrawLine 方法在场景视图中绘制了一条从当前对象到目标对象的连线。

3. 场景视图中的交互菜单

在场景视图中添加交互菜单,让开发者可以通过右键点击等操作执行特定的功能。例如,添加一个右键菜单来重置游戏对象的位置:

using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(MyScript))]
public class MyScriptEditor : Editor
{
    void OnSceneGUI()
    {
        MyScript myScript = (MyScript)target;

        Event e = Event.current;
        if (e.type == EventType.MouseDown && e.button == 1)
        {
            // 创建一个右键菜单
            GenericMenu menu = new GenericMenu();
            menu.AddItem(new GUIContent("重置位置"), false, () =>
            {
                Undo.RecordObject(myScript.transform, "Reset Position");
                myScript.transform.position = Vector3.zero;
                EditorUtility.SetDirty(myScript);
            });
            menu.ShowAsContext();
            e.Use();
        }
    }
}

public class MyScript : MonoBehaviour
{
    // 这里可以添加其他属性
}

在这个示例中,当开发者在场景视图中右键点击时,会弹出一个包含“重置位置”选项的菜单,点击该选项可将游戏对象的位置重置为原点。

4. 场景视图中的提示信息

在场景视图中显示提示信息,帮助开发者了解游戏对象的状态和操作说明。例如,在游戏对象上方显示其名称:

using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(MyScript))]
public class MyScriptEditor : Editor
{
    public override void OnSceneGUI()
    {
        MyScript myScript = (MyScript)target;

        // 获取游戏对象在场景中的位置
        Vector3 position = myScript.transform.position;
        // 将位置转换为屏幕坐标
        Vector3 screenPosition = HandleUtility.WorldToGUIPoint(position);

        // 在屏幕坐标位置绘制标签
        Handles.BeginGUI();
        GUI.Label(new Rect(screenPosition.x - 20, Screen.height - screenPosition.y - 20, 100, 20), myScript.name);
        Handles.EndGUI();
    }
}

public class MyScript : MonoBehaviour
{
    // 这里可以添加其他属性
}

在这个例子中,Handles.BeginGUIHandles.EndGUI 方法之间的代码用于在场景视图中绘制一个标签,显示游戏对象的名称。

总之,OnSceneGUI 方法为开发者在场景视图中提供了丰富的交互和可视化功能,有助于提高开发效率和调试体验。

🚌四、EditorWindow

在Unity开发中,EditorWindow 是一个非常有用的类,它允许开发者创建自定义的编辑器窗口。这些窗口可以在Unity编辑器内部运行,为开发者提供额外的功能和工具,以提高开发效率。以下是关于 EditorWindow 的详细介绍和常见用途:

1. 创建自定义编辑器窗口

首先,你需要创建一个继承自 EditorWindow 的类,并实现必要的方法。以下是一个简单的示例:

using UnityEditor;
using UnityEngine;

public class MyCustomWindow : EditorWindow
{
    [MenuItem("Window/My Custom Window")]
    public static void ShowWindow()
    {
        // 获取或创建窗口实例
        MyCustomWindow window = GetWindow<MyCustomWindow>("My Custom Window");
    }

    private void OnGUI()
    {
        // 在这里绘制窗口的UI
        GUILayout.Label("这是一个自定义编辑器窗口", EditorStyles.boldLabel);
    }
}

在这个示例中:

  • [MenuItem("Window/My Custom Window")] 是一个特性,它会在Unity编辑器的“Window”菜单下添加一个名为“My Custom Window”的菜单项。
  • ShowWindow 方法使用 GetWindow 函数来获取或创建 MyCustomWindow 的实例。
  • OnGUI 方法用于绘制窗口的用户界面,这里只是简单地显示了一个标签。

2. 与游戏对象交互

自定义编辑器窗口可以与场景中的游戏对象进行交互。例如,你可以选择一个游戏对象,并在窗口中显示其信息或对其进行修改:

using UnityEditor;
using UnityEngine;

public class GameObjectInfoWindow : EditorWindow
{
    private GameObject selectedGameObject;

    [MenuItem("Window/GameObject Info")]
    public static void ShowWindow()
    {
        GameObjectInfoWindow window = GetWindow<GameObjectInfoWindow>("GameObject Info");
    }

    private void OnGUI()
    {
        // 显示选择游戏对象的按钮
        selectedGameObject = (GameObject)EditorGUILayout.ObjectField("选择游戏对象", selectedGameObject, typeof(GameObject), true);

        if (selectedGameObject != null)
        {
            // 显示游戏对象的名称
            GUILayout.Label("名称: " + selectedGameObject.name);
            // 显示游戏对象的位置
            GUILayout.Label("位置: " + selectedGameObject.transform.position);
        }
    }
}

在这个示例中,EditorGUILayout.ObjectField 用于让用户选择一个游戏对象,然后在窗口中显示该游戏对象的名称和位置。

3. 执行批量操作

自定义编辑器窗口可以用于执行批量操作,例如批量修改游戏对象的属性。以下是一个批量修改游戏对象标签的示例:

using UnityEditor;
using UnityEngine;

public class BatchTagEditor : EditorWindow
{
    private string newTag;

    [MenuItem("Window/Batch Tag Editor")]
    public static void ShowWindow()
    {
        BatchTagEditor window = GetWindow<BatchTagEditor>("Batch Tag Editor");
    }

    private void OnGUI()
    {
        // 输入新标签的文本框
        newTag = EditorGUILayout.TextField("新标签", newTag);

        if (GUILayout.Button("批量修改标签"))
        {
            // 获取所有选中的游戏对象
            GameObject[] selectedObjects = Selection.gameObjects;
            foreach (GameObject obj in selectedObjects)
            {
                // 修改游戏对象的标签
                obj.tag = newTag;
            }
            EditorUtility.DisplayDialog("完成", "标签已批量修改", "确定");
        }
    }
}

在这个示例中,用户可以输入一个新的标签,然后点击“批量修改标签”按钮,所有选中的游戏对象的标签都会被修改为新的标签。

4. 数据管理和工具

自定义编辑器窗口还可以用于数据管理和工具开发。例如,创建一个窗口来管理游戏中的配置数据:

using UnityEditor;
using UnityEngine;

[System.Serializable]
public class GameConfig
{
    public int playerHealth;
    public float enemySpeed;
}

public class GameConfigEditor : EditorWindow
{
    private GameConfig config;

    [MenuItem("Window/Game Config Editor")]
    public static void ShowWindow()
    {
        GameConfigEditor window = GetWindow<GameConfigEditor>("Game Config Editor");
    }

    private void OnEnable()
    {
        // 加载配置数据
        config = new GameConfig();
        // 这里可以添加从文件或其他数据源加载数据的逻辑
    }

    private void OnGUI()
    {
        // 显示玩家生命值输入框
        config.playerHealth = EditorGUILayout.IntField("玩家生命值", config.playerHealth);
        // 显示敌人速度输入框
        config.enemySpeed = EditorGUILayout.FloatField("敌人速度", config.enemySpeed);

        if (GUILayout.Button("保存配置"))
        {
            // 这里可以添加保存配置数据到文件或其他数据源的逻辑
            EditorUtility.DisplayDialog("保存成功", "配置数据已保存", "确定");
        }
    }
}

在这个示例中,创建了一个 GameConfig 类来存储游戏的配置数据,然后在编辑器窗口中显示和修改这些数据。点击“保存配置”按钮时,可以将数据保存到文件或其他数据源。

综上所述,EditorWindow 为Unity开发者提供了强大的自定义编辑器功能,能够创建各种实用的工具和窗口,以满足不同的开发需求。

🚎五、ScriptableWizard

ScriptableWizard 是 Unity 引擎提供的一个类,它可以让开发者创建自定义的向导窗口。这些向导窗口能够辅助开发者完成特定的任务,例如生成脚本、配置参数、执行批量操作等。下面为你详细介绍其使用方法、常见用途以及一个示例代码。

使用方法

要使用 ScriptableWizard,你需要创建一个继承自 ScriptableWizard 的类,并且实现以下几个关键部分:

  1. 菜单选项:借助 [MenuItem] 特性在 Unity 编辑器的菜单里添加一个选项,点击该选项就会显示向导窗口。
  2. 属性配置:在类中定义一些公共属性,这些属性会以可编辑的字段形式显示在向导窗口中。
  3. 生成或执行逻辑:实现 OnWizardCreate 方法,当用户点击向导窗口中的 “Create” 按钮时,该方法会被调用,你可以在这个方法里编写生成文件、执行操作等逻辑。
  4. 验证逻辑(可选):实现 OnWizardUpdate 方法,用于验证用户输入的属性是否合法。若不合法,可以禁用 “Create” 按钮并显示错误信息。

常见用途

  • 快速生成代码或资源:创建自定义的脚本模板、材质、预制体等。
  • 批量操作:对多个游戏对象进行批量修改、排序、删除等操作。
  • 配置参数:集中配置一些复杂的参数,例如游戏的难度设置、角色的属性等。

示例代码

以下是一个简单的示例,展示了如何使用 ScriptableWizard 创建一个向导窗口,用于生成一个简单的 MonoBehaviour 脚本:

using UnityEditor;
using UnityEngine;
using System.IO;

public class ScriptGeneratorWizard : ScriptableWizard
{
    // 脚本名称
    public string scriptName = "NewScript";
    // 是否为 MonoBehaviour 脚本
    public bool isMonoBehaviour = true;

    // 在 Unity 菜单中添加选项
    [MenuItem("Tools/Script Generator Wizard")]
    static void CreateWizard()
    {
        // 显示向导窗口
        ScriptableWizard.DisplayWizard<ScriptGeneratorWizard>("Script Generator Wizard", "Create");
    }

    // 当用户点击 "Create" 按钮时调用
    void OnWizardCreate()
    {
        // 生成脚本内容
        string scriptContent = GenerateScriptContent();
        // 获取保存路径
        string path = AssetDatabase.GetAssetPath(Selection.activeObject);
        if (string.IsNullOrEmpty(path))
        {
            path = "Assets";
        }
        else if (Path.GetExtension(path) != "")
        {
            path = path.Replace(Path.GetFileName(AssetDatabase.GetAssetPath(Selection.activeObject)), "");
        }

        // 拼接完整的文件路径
        string fullPath = Path.Combine(path, scriptName + ".cs");
        // 将脚本内容写入文件
        File.WriteAllText(fullPath, scriptContent);
        // 刷新 AssetDatabase
        AssetDatabase.Refresh();
    }

    // 生成脚本内容的方法
    string GenerateScriptContent()
    {
        string content = "";
        if (isMonoBehaviour)
        {
            content = @"using UnityEngine;

public class " + scriptName + " : MonoBehaviour {

    void Start() {
        // Start is called before the first frame update
    }

    void Update() {
        // Update is called once per frame
    }
}";
        }
        else
        {
            content = @"public class " + scriptName + " {

    public " + scriptName + "() {
        // Constructor
    }
}";
        }
        return content;
    }

    // 当向导窗口更新时调用,可用于验证输入
    void OnWizardUpdate()
    {
        // 检查脚本名称是否为空
        isValid = !string.IsNullOrEmpty(scriptName);
        // 设置错误信息
        errorString = isValid ? "" : "脚本名称不能为空";
    }
}

代码解释

  1. 菜单选项[MenuItem("Tools/Script Generator Wizard")] 在 Unity 的 “Tools” 菜单下添加了一个名为 “Script Generator Wizard” 的选项。
  2. 属性配置scriptName 用于输入脚本名称,isMonoBehaviour 用于选择是否创建 MonoBehaviour 脚本。
  3. 生成脚本内容GenerateScriptContent 方法依据 isMonoBehaviour 的值生成不同类型的脚本内容。
  4. 创建脚本文件OnWizardCreate 方法在用户点击 “Create” 按钮时被调用,它把生成的脚本内容写入指定路径的文件中,并刷新 AssetDatabase。
  5. 验证逻辑OnWizardUpdate 方法检查 scriptName 是否为空,若为空则禁用 “Create” 按钮并显示错误信息。

通过上述步骤,你可以利用 ScriptableWizard 创建自定义的向导窗口,提高开发效率。

🚓六、ScriptableObject

ScriptableObject 是 Unity 引擎中一个非常有用的类,它继承自 UnityEngine.Object,允许开发者创建可序列化的数据容器,这些容器可以独立于游戏对象存在,并且可以在不同的场景、脚本和资源中共享数据。以下从几个方面详细介绍 ScriptableObject

特点

  • 数据独立存储ScriptableObject 可以将数据存储在资产文件(.asset)中,这些文件可以独立于场景和游戏对象存在。这使得数据的管理和维护更加方便,尤其是在处理大量配置数据时。
  • 数据共享:多个脚本和游戏对象可以引用同一个 ScriptableObject 实例,从而实现数据的共享。这样可以避免数据的重复存储和不一致性问题。
  • 序列化支持ScriptableObject 支持 Unity 的序列化系统,可以将其数据保存到磁盘并在需要时重新加载。这使得数据可以在不同的会话和平台之间持久化。
  • 可编辑性:在 Unity 编辑器中,可以直接编辑 ScriptableObject 的属性,就像编辑游戏对象的组件一样。这为开发者提供了直观的配置界面。

常见用途

  • 游戏配置数据:例如游戏的关卡配置、角色属性、物品信息等。将这些数据存储在 ScriptableObject 中,可以方便地在编辑器中进行修改和管理。
  • 事件系统:可以创建 ScriptableObject 来表示游戏中的事件,不同的游戏对象可以订阅这些事件,实现松耦合的通信机制。
  • AI 行为树:将 AI 的行为树逻辑存储在 ScriptableObject 中,方便设计和调整 AI 的行为。

创建和使用 ScriptableObject

1. 创建 ScriptableObject

首先,创建一个继承自 ScriptableObject 的类,并定义需要存储的数据:

using UnityEngine;

[CreateAssetMenu(fileName = "NewItem", menuName = "Inventory/Item")]
public class Item : ScriptableObject
{
    public string itemName;
    public int itemID;
    public Sprite itemIcon;
    public int itemValue;
}

在这个例子中,[CreateAssetMenu] 属性允许在 Unity 编辑器的“Assets/Create”菜单中创建 Item 类型的 ScriptableObject 资产。

2. 创建 ScriptableObject 资产

在 Unity 编辑器中,右键点击“Assets”面板,选择“Inventory/Item”,即可创建一个新的 Item 资产。然后可以在 Inspector 面板中编辑该资产的属性。

3. 在脚本中引用 ScriptableObject

在其他脚本中,可以通过公共字段引用 ScriptableObject 资产,并使用其中的数据:

using UnityEngine;

public class ItemManager : MonoBehaviour
{
    public Item item;

    void Start()
    {
        Debug.Log("Item Name: " + item.itemName);
        Debug.Log("Item ID: " + item.itemID);
    }
}

在这个例子中,ItemManager 脚本通过公共字段 item 引用了一个 Item 类型的 ScriptableObject 资产,并在 Start 方法中打印了该资产的名称和 ID。

总结

ScriptableObject 是 Unity 中一个强大的工具,它提供了一种灵活、可维护的方式来管理和共享游戏数据。通过合理使用 ScriptableObject,可以提高游戏开发的效率和可扩展性。

🚑七、Attributes

在 Unity 开发里,Attributes(特性)是一种非常重要的编程元素,它可以给类、方法、字段等代码元素添加额外的元数据信息,从而影响 Unity 编辑器对这些代码元素的处理方式,或者在运行时改变它们的行为。下面为你介绍一些常见的 Unity 特性及其用途:

1. SerializeField

这个特性的作用是让私有字段在 Unity 编辑器的检视面板中可被序列化和显示。通常情况下,私有字段不会在检视面板中显示,不过使用 SerializeField 特性就能打破这个限制。

using UnityEngine;

public class MyScript : MonoBehaviour
{
    [SerializeField]
    private int myPrivateVariable;
}

2. HideInInspector

SerializeField 相反,HideInInspector 特性用于隐藏公共字段,使其不在检视面板中显示。这在你希望某些公共字段仅在代码中使用,而不在编辑器中暴露时非常有用。

using UnityEngine;

public class MyScript : MonoBehaviour
{
    [HideInInspector]
    public int myPublicVariable;
}

3. Range

Range 特性可以限制一个浮点数或整数类型的字段在指定的范围内取值,同时会在检视面板中显示一个滑动条,方便调整数值。

using UnityEngine;

public class MyScript : MonoBehaviour
{
    [Range(0f, 100f)]
    public float myFloatValue;
}

4. Tooltip

Tooltip 特性能够为字段添加提示信息,当鼠标悬停在检视面板中的字段上时,会显示该提示信息。

using UnityEngine;

public class MyScript : MonoBehaviour
{
    [Tooltip("这是一个用于存储玩家生命值的变量")]
    public int playerHealth;
}

5. Header

Header 特性用于在检视面板中为字段分组添加标题,让检视面板的布局更加清晰。

using UnityEngine;

public class MyScript : MonoBehaviour
{
    [Header("玩家属性")]
    public int playerHealth;
    public int playerSpeed;

    [Header("敌人属性")]
    public int enemyHealth;
    public int enemySpeed;
}

6. Space

Space 特性可以在检视面板中为字段之间添加空白间隔,增强检视面板的可读性。

using UnityEngine;

public class MyScript : MonoBehaviour
{
    public int firstValue;

    [Space(10)]
    public int secondValue;
}

7. RequireComponent

RequireComponent 特性会强制要求一个游戏对象在添加某个脚本时,同时添加指定的组件。如果该组件不存在,Unity 会自动添加它。

using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
public class MyScript : MonoBehaviour
{
    // 该脚本所在的游戏对象必须有 Rigidbody 组件
}

8. ExecuteInEditMode

ExecuteInEditMode 特性允许脚本在编辑模式下也能执行,这对于创建编辑器工具或实时预览效果非常有用。

using UnityEngine;

[ExecuteInEditMode]
public class MyScript : MonoBehaviour
{
    void Update()
    {
        // 该脚本在编辑模式和运行模式下都会执行 Update 方法
    }
}

9. ContextMenuItem

ContextMenuItem 特性可以为字段添加上下文菜单项,当在检视面板中右键点击该字段时,会显示指定的菜单项。

using UnityEngine;

public class MyScript : MonoBehaviour
{
    [ContextMenuItem("重置为零", "ResetToZero")]
    public int myValue;

    private void ResetToZero()
    {
        myValue = 0;
    }
}

10. CreateAssetMenu

CreateAssetMenu 特性用于在 Unity 编辑器的 “Assets/Create” 菜单中添加一个选项,用于创建自定义的 ScriptableObject 资产。

using UnityEngine;

[CreateAssetMenu(fileName = "NewItem", menuName = "Inventory/Item")]
public class Item : ScriptableObject
{
    public string itemName;
    public int itemValue;
}

这些特性能够显著提升代码的可读性和可维护性,同时优化 Unity 编辑器的使用体验。

🚒八、AssetPostprocessor

AssetPostprocessor 是 Unity 提供的一个非常实用的类,它允许开发者在资产导入过程中插入自定义逻辑。借助继承 AssetPostprocessor 类并重写特定的方法,你能够在资产导入前后执行自定义操作,例如修改纹理设置、处理模型导入、转换音频格式等。下面为你详细介绍 AssetPostprocessor 的使用方法和常见应用场景。

基本使用方法

要使用 AssetPostprocessor,你需要创建一个继承自 AssetPostprocessor 的类,并重写相应的方法。这些方法会在资产导入的不同阶段被调用。以下是一个简单的示例:

using UnityEditor;
using UnityEngine;

public class MyAssetPostprocessor : AssetPostprocessor
{
    // 在资产导入前调用
    void OnPreprocessAsset()
    {
        Debug.Log("正在预处理资产: " + assetPath);
    }

    // 在资产导入后调用
    void OnPostprocessAsset()
    {
        Debug.Log("资产导入完成: " + assetPath);
    }
}

在上述代码中,OnPreprocessAsset 方法会在资产开始导入之前被调用,OnPostprocessAsset 方法会在资产导入完成后被调用。assetPath 是一个继承自 AssetPostprocessor 的属性,它表示当前正在处理的资产的路径。

常见应用场景

1. 纹理导入设置

在纹理导入时,你可以使用 AssetPostprocessor 来自动设置纹理的格式、压缩方式、过滤模式等。以下是一个示例:

using UnityEditor;
using UnityEngine;

public class TexturePostprocessor : AssetPostprocessor
{
    void OnPreprocessTexture()
    {
        // 检查导入的是否为纹理资产
        TextureImporter textureImporter = (TextureImporter)assetImporter;
        if (textureImporter != null)
        {
            // 设置纹理格式
            textureImporter.textureFormat = TextureImporterFormat.RGBA32;
            // 设置过滤模式
            textureImporter.filterMode = FilterMode.Bilinear;
            // 设置压缩质量
            textureImporter.compressionQuality = 50;
        }
    }
}

在这个示例中,OnPreprocessTexture 方法会在纹理导入前被调用,通过 TextureImporter 对象可以修改纹理的各种导入设置。

2. 模型导入设置

对于模型导入,你可以使用 AssetPostprocessor 来调整模型的导入选项,如缩放因子、动画导入设置等。以下是一个示例:

using UnityEditor;
using UnityEngine;

public class ModelPostprocessor : AssetPostprocessor
{
    void OnPreprocessModel()
    {
        // 检查导入的是否为模型资产
        ModelImporter modelImporter = (ModelImporter)assetImporter;
        if (modelImporter != null)
        {
            // 设置模型的缩放因子
            modelImporter.globalScale = 1.0f;
            // 禁用动画导入
            modelImporter.animationType = ModelImporterAnimationType.None;
        }
    }
}

在这个示例中,OnPreprocessModel 方法会在模型导入前被调用,通过 ModelImporter 对象可以修改模型的导入设置。

3. 音频导入设置

对于音频导入,你可以使用 AssetPostprocessor 来调整音频的压缩格式、音量等。以下是一个示例:

using UnityEditor;
using UnityEngine;

public class AudioPostprocessor : AssetPostprocessor
{
    void OnPreprocessAudio()
    {
        // 检查导入的是否为音频资产
        AudioImporter audioImporter = (AudioImporter)assetImporter;
        if (audioImporter != null)
        {
            // 设置音频压缩格式
            audioImporter.format = AudioImporterFormat.Compressed;
            // 设置音频音量
            audioImporter.defaultSampleSettings.volume = 0.8f;
        }
    }
}

在这个示例中,OnPreprocessAudio 方法会在音频导入前被调用,通过 AudioImporter 对象可以修改音频的导入设置。

注意事项

  • 脚本位置:继承自 AssetPostprocessor 的脚本必须放在 Assets/Editor 文件夹下,因为这些脚本只在编辑器模式下运行。
  • 性能考虑:在处理大量资产导入时,要注意自定义逻辑的性能,避免影响导入速度。

通过使用 AssetPostprocessor,你可以自动化资产导入过程,确保所有资产都按照统一的标准进行导入和处理,提高开发效率。

🚐九、Undo

在 Unity 开发里,Undo 类起着重要作用,它为开发者提供了在编辑器中实现撤销和重做操作的功能。借助 Undo 类,你可以记录对游戏对象、组件、脚本属性等所做的更改,这样开发者就能轻松撤销和重做这些操作,极大地提升了开发效率。下面为你详细介绍 Undo 类的使用方法和常见应用场景。

基本使用方法

Undo 类提供了多个静态方法,用于记录不同类型的操作。以下是一些常用的方法:

1. Undo.RecordObject

该方法用于记录对一个或多个 UnityEngine.Object 对象的更改。在修改对象之前调用此方法,Unity 会自动记录对象的初始状态,以便后续可以撤销这些更改。

using UnityEditor;
using UnityEngine;

public class UndoExample : MonoBehaviour
{
    public int myIntValue;

    [MenuItem("Tools/Change Value with Undo")]
    static void ChangeValueWithUndo()
    {
        // 获取选中的游戏对象
        GameObject selectedObject = Selection.activeGameObject;
        if (selectedObject != null)
        {
            // 获取游戏对象上的 UndoExample 组件
            UndoExample example = selectedObject.GetComponent<UndoExample>();
            if (example != null)
            {
                // 记录对组件的更改
                Undo.RecordObject(example, "Change Int Value");
                // 修改组件的属性
                example.myIntValue += 1;
            }
        }
    }
}

在上述代码中,Undo.RecordObject(example, "Change Int Value") 记录了对 UndoExample 组件的更改,并且为这次操作指定了一个描述信息 “Change Int Value”。当用户按下撤销快捷键(通常是 Ctrl + ZCmd + Z)时,myIntValue 的值会恢复到修改前的状态。

2. Undo.RegisterCreatedObjectUndo

此方法用于记录新创建的对象,这样在撤销操作时可以删除这个新创建的对象。

using UnityEditor;
using UnityEngine;

public class CreateObjectWithUndo : MonoBehaviour
{
    [MenuItem("Tools/Create Object with Undo")]
    static void CreateObject()
    {
        // 创建一个新的游戏对象
        GameObject newObject = new GameObject("New Object");
        // 记录新创建的对象
        Undo.RegisterCreatedObjectUndo(newObject, "Create New Object");
    }
}

在这个示例中,Undo.RegisterCreatedObjectUndo(newObject, "Create New Object") 记录了新创建的游戏对象 newObject,当用户撤销操作时,这个新创建的游戏对象会被删除。

3. Undo.DestroyObjectImmediate

该方法用于删除对象,并且会将删除操作记录到撤销系统中,以便可以重做删除操作。

using UnityEditor;
using UnityEngine;

public class DestroyObjectWithUndo : MonoBehaviour
{
    [MenuItem("Tools/Destroy Object with Undo")]
    static void DestroyObject()
    {
        // 获取选中的游戏对象
        GameObject selectedObject = Selection.activeGameObject;
        if (selectedObject != null)
        {
            // 删除对象并记录操作
            Undo.DestroyObjectImmediate(selectedObject, "Destroy Object");
        }
    }
}

在这个示例中,Undo.DestroyObjectImmediate(selectedObject, "Destroy Object") 删除了选中的游戏对象,并且将删除操作记录到撤销系统中。

常见应用场景

  • 自定义编辑器脚本:在编写自定义编辑器脚本时,使用 Undo 类可以让用户在使用这些脚本进行操作时能够撤销和重做。例如,在自定义的场景编辑器中,用户可能会创建、移动或删除游戏对象,使用 Undo 类可以确保这些操作是可撤销的。
  • 属性修改:当在脚本中修改游戏对象的属性时,使用 Undo 类可以让用户撤销这些属性的修改。例如,在自定义的属性面板中,用户可能会修改游戏对象的位置、旋转或缩放,使用 Undo 类可以记录这些更改并支持撤销操作。

注意事项

  • 操作描述:在调用 Undo 类的方法时,建议为操作提供一个有意义的描述信息,这样在撤销和重做操作的历史记录中可以清楚地看到每个操作的具体内容。
  • 线程安全Undo 类的方法只能在主线程中调用,不能在异步操作或多线程环境中使用。

通过合理使用 Undo 类,你可以为 Unity 编辑器中的操作添加撤销和重做功能,提升用户体验和开发效率。