关于 Unity Workflows
Unity Workflows 是面向 Unity 开发者的参考技能,涵盖编辑器脚本、输入、UI、资源和构建。内容结合实现示例与流程建议,帮助自动化编辑器任务和开发项目。
主要功能
- 01编辑器脚本与自定义工具
- 02新旧输入系统
- 03uGUI 与 UI Toolkit 指引
- 04Addressables 资源管理
- 05构建流程脚本
适用场景
- 01自动化重复的编辑器任务
- 02为项目选择输入系统
- 03构建运行时或编辑器 UI
- 04加载和整理游戏资源
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ cryptorabea/ claude_unity_dev_plugin --skill unity-workflows安装后在 Claude Code 中输入/unity-workflows确认已加载。
详细介绍
Unity 工作流和编辑器工具
Unity 编辑器脚本、输入系统、UI 开发和资源管理的核心工作流。
概述
高效的 Unity 工作流能够加快开发进度并减少错误。本技能涵盖编辑器自定义、现代输入处理、UI 系统和资源管线优化。
核心工作流领域:
- 编辑器脚本和自定义工具
- Input System(新版和旧版)
- UI 开发(UI Toolkit、uGUI)
- 资源管理和构建管线
编辑器脚本基础
菜单项
创建自定义菜单命令:
using UnityEditor;
public static class CustomMenu
{
[MenuItem("Tools/My Tool")]
private static void ExecuteTool()
{
Debug.Log("Custom tool executed");
}
[MenuItem("Tools/My Tool", true)] // Validation
private static bool ValidateTool()
{
return Selection.activeGameObject != null;
}
// Keyboard shortcut: Ctrl+Shift+T (Windows), Cmd+Shift+T (Mac)
[MenuItem("Tools/Quick Action %#t")]
private static void QuickAction()
{
// Action
}
}
快捷键:
%= Ctrl(Mac 上为 Cmd)#= Shift&= Alt_g= G 键
上下文菜单
为 GameObject/Component 添加右键上下文菜单:
[MenuItem("GameObject/My Custom Action", false, 10)]
private static void CustomAction()
{
GameObject selected = Selection.activeGameObject;
// Perform action
}
// Component context menu
public class MyComponent : MonoBehaviour
{
[ContextMenu("Do Something")]
private void DoSomething()
{
Debug.Log("Context menu action");
}
[ContextMenuItem("Reset Value", "ResetValue")]
[SerializeField] private int value;
private void ResetValue()
{
value = 0;
}
}
自定义检查器
为自定义类型重写 Inspector:
[CustomEditor(typeof(MyScript))]
public class MyScriptEditor : Editor
{
public override void OnInspectorGUI()
{
// Draw default inspector
DrawDefaultInspector();
MyScript script = (MyScript)target;
// Custom button
if (GUILayout.Button("Execute Action"))
{
script.ExecuteAction();
}
// Custom fields
EditorGUILayout.LabelField("Custom Section", EditorStyles.boldLabel);
script.customValue = EditorGUILayout.IntSlider("Custom Value", script.customValue, 0, 100);
// Apply changes
if (GUI.changed)
{
EditorUtility.SetDirty(target);
}
}
}
属性绘制器
在 Inspector 中自定义属性显示:
[System.Serializable]
public class Range
{
public float min;
public float max;
}
[CustomPropertyDrawer(typeof(Range))]
public class RangeDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
var minProp = property.FindPropertyRelative("min");
var maxProp = property.FindPropertyRelative("max");
float halfWidth = position.width / 2;
Rect minRect = new Rect(position.x, position.y, halfWidth - 5, position.height);
Rect maxRect = new Rect(position.x + halfWidth, position.y, halfWidth - 5, position.height);
EditorGUI.PropertyField(minRect, minProp, GUIContent.none);
EditorGUI.PropertyField(maxRect, maxProp, GUIContent.none);
EditorGUI.EndProperty();
}
}
EditorWindow
创建自定义编辑器窗口:
public class MyEditorWindow : EditorWindow
{
private string textField = "";
private int intField = 0;
[MenuItem("Window/My Editor Window")]
private static void ShowWindow()
{
var window = GetWindow<MyEditorWindow>();
window.titleContent = new GUIContent("My Tool");
window.minSize = new Vector2(300, 200);
window.Show();
}
private void OnGUI()
{
GUILayout.Label("My Custom Tool", EditorStyles.boldLabel);
textField = EditorGUILayout.TextField("Text Field", textField);
intField = EditorGUILayout.IntField("Int Field", intField);
if (GUILayout.Button("Execute"))
{
Execute();
}
}
private void Execute()
{
Debug.Log($"Executed with: {textField}, {intField}");
}
}
AssetDatabase 操作
以编程方式操作资源:
using UnityEditor;
public static class AssetUtilities
{
[MenuItem("Assets/Create Prefab From Selection")]
private static void CreatePrefab()
{
GameObject selected = Selection.activeGameObject;
if (selected == null)
return;
string path = $"Assets/Prefabs/{selected.name}.prefab";
// Create prefab
PrefabUtility.SaveAsPrefabAsset(selected, path);
AssetDatabase.Refresh();
}
public static T LoadAsset<T>(string path) where T : UnityEngine.Object
{
return AssetDatabase.LoadAssetAtPath<T>(path);
}
public static void CreateFolder(string path)
{
if (!AssetDatabase.IsValidFolder(path))
{
AssetDatabase.CreateFolder(System.IO.Path.GetDirectoryName(path), System.IO.Path.GetFileName(path));
}
}
}
Unity Input System
新 Input System 设置
安装:Window > Package Manager > Input System
创建 Input Actions:
- 选择 Create > Input Actions
- 添加 Action Maps(Player、UI 等)
- 添加 Actions(Move、Jump、Fire 等)
- 绑定输入(Keyboard、Gamepad 等)
- 生成 C# 类(Inspector > Generate C# Class)
使用 Input Actions
using UnityEngine.InputSystem;
public class PlayerController : MonoBehaviour
{
private PlayerInput playerInput;
private InputAction moveAction;
private InputAction jumpAction;
private void Awake()
{
playerInput = GetComponent<PlayerInput>();
moveAction = playerInput.actions["Move"];
jumpAction = playerInput.actions["Jump"];
}
private void OnEnable()
{
jumpAction.performed += OnJump;
}
private void OnDisable()
{
jumpAction.performed -= OnJump;
}
private void Update()
{
Vector2 moveInput = moveAction.ReadValue<Vector2>();
Move(moveInput);
}
private void OnJump(InputAction.CallbackContext context)
{
Jump();
}
private void Move(Vector2 input) { }
private void Jump() { }
}
Input Action 资源(生成的 C#)
// After generating C# class from Input Actions asset
private PlayerInputActions inputActions;
private void Awake()
{
inputActions = new PlayerInputActions();
}
private void OnEnable()
{
inputActions.Player.Enable();
inputActions.Player.Jump.performed += OnJump;
}
private void OnDisable()
{
inputActions.Player.Disable();
inputActions.Player.Jump.performed -= OnJump;
}
private void Update()
{
Vector2 move = inputActions.Player.Move.ReadValue<Vector2>();
}
优点:
- 类型安全的动作访问
- 自动补全
- 编译时检查
旧版 Input System
// Still functional, simpler for basic games
private void Update()
{
// Keyboard
if (Input.GetKeyDown(KeyCode.Space))
Jump();
// Mouse
if (Input.GetMouseButtonDown(0))
Fire();
// Axis (configured in Edit > Project Settings > Input Manager)
float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");
Move(new Vector2(horizontal, vertical));
}
适用于:
- 简单原型
- 移动端触摸输入
- 旧版项目
以下情况优先使用新版 Input System:
- 多平台支持
- 可重新绑定的控制
- 复杂的输入处理
UI 系统
uGUI(基于 Canvas 的 UI)
Unity 标准 UI 系统:
using UnityEngine.UI;
using TMPro; // TextMeshPro
public class UIManager : MonoBehaviour
{
[SerializeField] private TextMeshProUGUI scoreText;
[SerializeField] private Button playButton;
[SerializeField] private Slider healthSlider;
private void Start()
{
playButton.onClick.AddListener(OnPlayClicked);
}
private void OnDestroy()
{
playButton.onClick.RemoveListener(OnPlayClicked);
}
public void UpdateScore(int score)
{
scoreText.text = $"Score: {score}";
}
public void UpdateHealth(float health, float maxHealth)
{
healthSlider.value = health / maxHealth;
}
private void OnPlayClicked()
{
Debug.Log("Play button clicked");
}
}
Canvas 渲染模式:
- Screen Space - Overlay:UI 始终显示在最上层,无需摄像机(速度最快)
- Screen Space - Camera:由摄像机渲染 UI,支持后处理
- World Space:游戏世界中的 3D UI
UI 优化:
- 将静态和动态 UI 分别放到不同的 Canvas 中
- 对非交互元素禁用射线检测目标
- 使用精灵图集
- 尽量减少 Layout Groups 的使用(开销较大)
UI Toolkit(现代 UI)
运行时和编辑器 UI(Unity 2021+):
UXML(UI 结构):
<ui:UXML>
<ui:VisualElement name="root">
<ui:Label text="Score: 0" name="scoreLabel"/>
<ui:Button text="Play" name="playButton"/>
</ui:VisualElement>
</ui:UXML>
USS(样式):
.root {
flex-grow: 1;
background-color: rgb(50, 50, 50);
}
#scoreLabel {
font-size: 24px;
color: white;
}
C#(逻辑):
using UnityEngine.UIElements;
public class UIController : MonoBehaviour
{
private UIDocument uiDocument;
private Label scoreLabel;
private Button playButton;
private void Awake()
{
uiDocument = GetComponent<UIDocument>();
var root = uiDocument.rootVisualElement;
scoreLabel = root.Q<Label>("scoreLabel");
playButton = root.Q<Button>("playButton");
playButton.clicked += OnPlayClicked;
}
public void UpdateScore(int score)
{
scoreLabel.text = $"Score: {score}";
}
private void OnPlayClicked()
{
Debug.Log("Play clicked");
}
}
优点:
- 性能更快(保留模式)
- 更适合复杂 UI
- 现代化的类 Web 工作流(相当于 HTML/CSS)
- 与编辑器 UI 共用
适用于:
- 新项目(Unity 2021+)
- 复杂 UI
- 编辑器工具
资源管理
Addressables
异步资源加载和内存管理:
设置:
- Window > Asset Management > Addressables > Groups
- 将资源标记为 Addressable
- 将资源整理到组中
加载资源:
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
public class AssetLoader : MonoBehaviour
{
private async void Start()
{
// Load asset asynchronously
var handle = Addressables.LoadAssetAsync<GameObject>("Enemy");
await handle.Task;
if (handle.Status == AsyncOperationStatus.Succeeded)
{
GameObject prefab = handle.Result;
Instantiate(prefab);
}
// Release when done
Addressables.Release(handle);
}
// Instantiate directly
private async void SpawnEnemy()
{
var handle = Addressables.InstantiateAsync("Enemy");
await handle.Task;
GameObject enemy = handle.Result;
// Use enemy
}
}
优点:
- 异步加载(不会造成帧卡顿)
- 内存管理
- 远程内容更新
- 构建优化
适用于:
- 大型游戏
- DLC/在线更新
- 移动游戏(缩减安装包大小)
Resources 文件夹(旧版)
简单的资源加载:
// Load from Resources folder
GameObject prefab = Resources.Load<GameObject>("Prefabs/Enemy");
// All assets include in build - inefficient
避免使用: 新项目请改用 Addressables。
构建管线
构建设置
using UnityEditor;
using UnityEditor.Build.Reporting;
public static class BuildUtility
{
[MenuItem("Build/Build Windows")]
private static void BuildWindows()
{
BuildPlayerOptions options = new BuildPlayerOptions
{
scenes = new[] { "Assets/Scenes/MainMenu.unity", "Assets/Scenes/Game.unity" },
locationPathName = "Builds/Windows/Game.exe",
target = BuildTarget.StandaloneWindows64,
options = BuildOptions.None
};
BuildReport report = BuildPipeline.BuildPlayer(options);
if (report.summary.result == BuildResult.Succeeded)
{
Debug.Log($"Build succeeded: {report.summary.totalSize} bytes");
}
else
{
Debug.LogError($"Build failed: {report.summary.result}");
}
}
}
构建预处理
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
public class BuildPreprocessor : IPreprocessBuildWithReport
{
public int callbackOrder => 0;
public void OnPreprocessBuild(BuildReport report)
{
Debug.Log("Preprocessing build...");
// Validate assets
// Update version number
// Generate build info
}
}
最佳实践
✅ 应该:
- 使用 Editor 脚本自动执行重复性任务
- 为复杂组件实现自定义检视面板
- 在多平台项目中使用新的 Input System
- 使用独立的画布显示静态和动态内容,优化 UI
- 在大型游戏和移动项目中使用 Addressables
- 为设计师和美术人员创建编辑器工具
- 在构建前验证资源
❌ 不应该:
- 将编辑器路径硬编码(使用 AssetDatabase)
- 忘记取消订阅 Input System 操作
- 过度混用 Layout Groups(会严重影响 UI 性能)
- 在新项目中使用 Resources 文件夹(改用 Addressables)
- 在 Update 循环中创建 UI
- 跳过对关键脚本的编辑器验证
黄金法则:使用编辑器工具自动化工作流。花时间创建工具,能在迭代过程中节省更多时间,效果呈指数级增长。
采用这些工作流优化和现代系统,高效开发可扩展的 Unity 项目。
评论
支持 Markdown