一个强大的Unity编辑器扩展,提供圆形按钮界面来快速访问自定义操作。使用Ctrl+Q热键在鼠标位置显示径向菜单。
- 圆形界面: 以鼠标位置为中心的直观径向按钮布局
- 热键激活: 按
Ctrl+Q立即打开操作菜单 - 分层组织: 将操作组织到类别和子类别中
- 动态分页: 自动处理大量操作的分页显示
- 条件操作: 根据当前上下文启用/禁用操作
- 状态管理: 可视化状态指示器(选中/未选中,可见/隐藏)
- 优先级系统: 通过优先级值控制操作显示顺序
- 简易集成: 基于特性的简单操作注册
- 动态指令: 在运行时以编程方式注册指令,实现上下文感知功能
您可以通过以下方法之一将Quick Action包导入到Unity项目中:
对于VRChat开发者:通过VPM Listing添加到VCC
- 打开Unity,转到 Window > Package Manager
- 点击左上角的 + 按钮
- 选择 Add package from git URL...
- 输入以下URL:
https://github.com/Yueby/QuickAction.git - 点击 Add 并等待包导入完成
- 从GitHub仓库下载包
- 将文件解压到项目的
Packages文件夹中 - Unity会自动检测并导入包
创建一个新脚本并添加简单操作:
using UnityEngine;
using UnityEditor;
using Yueby.QuickActions; // 不要忘记导入命名空间
public class MyActions
{
[QuickAction("Tools/Hello World", "显示问候消息")]
public static void HelloWorld()
{
Debug.Log("来自Quick Action的问候!");
}
[QuickAction("Tools/Create Cube", "在场景中创建立方体", Priority = -100)]
public static void CreateCube()
{
var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.name = "Quick Action立方体";
Selection.activeGameObject = cube;
}
}- 打开菜单: 在Unity编辑器中按
Ctrl+Q - 选择操作: 鼠标移动到对应角度会自动选择选项
- 执行操作:
- 松开
Ctrl+Q键自动执行选中的操作 - 或者鼠标左键点击执行操作
- 松开
- 取消操作: 鼠标右键点击关闭窗口不执行任何操作
- 按住
Ctrl+Q打开菜单 - 鼠标移动到不同角度选择不同选项
- 松开按键或左键点击执行
- 右键点击取消操作
QuickActionAttribute用于标记方法为快速操作:
[QuickAction(path, description, Priority = priority, ValidateFunction = nameof(ValidationMethod))]参数:
path(必需):使用正斜杠的操作路径(例如:"Tools/My Action")description(可选):操作描述,用于工具提示Priority(可选):显示优先级(数字越小越靠前)ValidateFunction(可选):用于条件启用的方法名
操作方法必须是:
staticpublic或private- 返回
void - 无参数
验证函数必须是:
staticpublic或private- 返回
bool - 无参数
验证函数还可以使用以下方法控制操作的可见性和选中状态:
QuickAction.SetVisible(path, bool): 显示/隐藏操作QuickAction.SetChecked(path, bool): 设置选中状态(显示勾选标记)QuickAction.GetVisible(path): 获取可见性状态QuickAction.GetChecked(path): 获取选中状态
动态指令允许您在运行时以编程方式注册指令,非常适合上下文感知功能:
// 注册动态指令
QuickAction.RegisterDynamicAction(
"Selection/Component/Copy",
() => CopyComponent(),
"复制选中的组件",
-100
);动态指令特性:
- 运行时注册: 在面板打开时添加指令
- 自动清理: 面板关闭时自动移除指令
- 上下文感知: 非常适合依赖于当前选择的指令
使用方式:
// 注册事件(在类初始化时)
[InitializeOnLoadMethod]
private static void RegisterDynamicActions()
{
QuickAction.OnBeforeOpen += OnQuickActionOpen;
}
// 在事件中注册动态指令
private static void OnQuickActionOpen()
{
QuickAction.RegisterDynamicAction(
"路径/指令名称",
() => { /* 指令逻辑 */ },
"指令描述",
优先级
);
}
// 带验证的动态指令
QuickAction.RegisterDynamicAction(
"路径/指令名称",
() => { /* 指令逻辑 */ },
"指令描述",
优先级,
() => { /* 验证逻辑,返回bool */ }
);using UnityEngine;
using UnityEditor;
using Yueby.QuickActions;
public class BasicActions
{
[QuickAction("Debug/Hello World", "显示问候消息")]
public static void HelloWorld()
{
Debug.Log("Hello from Quick Action!");
}
[QuickAction("GameObject/Create Cube", "在场景中创建立方体")]
public static void CreateCube()
{
var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.name = "Quick Action Cube";
Selection.activeGameObject = cube;
}
}using UnityEngine;
using UnityEditor;
using Yueby.QuickActions;
public class ConditionalActions
{
[QuickAction("Selection/Delete Selected", "删除选中的GameObject", ValidateFunction = "HasSelection")]
public static void DeleteSelected()
{
if (Selection.gameObjects.Length > 0)
{
foreach (var go in Selection.gameObjects)
{
Undo.DestroyObjectImmediate(go);
}
}
}
private static bool HasSelection()
{
bool hasSelection = Selection.gameObjects.Length > 0;
// 只有在选中对象时才显示此操作
QuickAction.SetVisible("Selection/Delete Selected", hasSelection);
return hasSelection;
}
[QuickAction("Play Mode/Stop Play", "停止播放模式", ValidateFunction = "IsPlaying")]
public static void StopPlay()
{
EditorApplication.isPlaying = false;
}
private static bool IsPlaying()
{
return EditorApplication.isPlaying;
}
}using UnityEngine;
using UnityEditor;
using Yueby.QuickActions;
public class StateActions
{
private static bool _featureEnabled = false;
[QuickAction("Settings/Toggle Feature", "启用/禁用功能", ValidateFunction = "ValidateFeature")]
public static void ToggleFeature()
{
_featureEnabled = !_featureEnabled;
Debug.Log($"功能{(_featureEnabled ? "已启用" : "已禁用")}");
}
private static bool ValidateFeature()
{
// 功能启用时显示勾选标记
QuickAction.SetChecked("Settings/Toggle Feature", _featureEnabled);
return true;
}
[QuickAction("Tools/Debug Mode", "切换调试模式", ValidateFunction = "ValidateDebugMode")]
public static void ToggleDebugMode()
{
Debug.unityLogger.logEnabled = !Debug.unityLogger.logEnabled;
}
private static bool ValidateDebugMode()
{
// 显示当前调试模式状态
QuickAction.SetChecked("Tools/Debug Mode", Debug.unityLogger.logEnabled);
return true;
}
}using UnityEngine;
using UnityEditor;
using Yueby.QuickActions;
public class HierarchicalActions
{
[QuickAction("Tools/Utilities/Screenshot", "截取屏幕截图")]
public static void TakeScreenshot()
{
ScreenCapture.CaptureScreenshot("screenshot.png");
Debug.Log("截图已保存为screenshot.png");
}
[QuickAction("Tools/Utilities/Open Persistent Data", "打开持久数据路径")]
public static void OpenPersistentData()
{
EditorUtility.RevealInFinder(Application.persistentDataPath);
}
[QuickAction("Tools/Scene/Save Scene", "保存当前场景")]
public static void SaveScene()
{
EditorSceneManager.SaveScene(EditorSceneManager.GetActiveScene());
}
}动态指令非常适合上下文感知指令,如组件管理:
using UnityEngine;
using UnityEditor;
using Yueby.QuickActions;
public class ComponentActions
{
[InitializeOnLoadMethod]
private static void RegisterDynamicActions()
{
QuickAction.OnBeforeOpen += OnQuickActionOpen;
}
private static void OnQuickActionOpen()
{
if (Selection.activeGameObject != null)
{
var components = Selection.activeGameObject.GetComponents<Component>();
foreach (var component in components)
{
if (component == null) continue;
var componentName = component.GetType().Name;
var componentKey = $"{componentName}_{component.GetInstanceID()}";
// 为每个组件注册复制和移除操作
QuickAction.RegisterDynamicAction(
$"Selection/Component/{componentName}/Copy",
() => CopyComponent(componentKey),
$"复制{componentName}组件",
-850
);
QuickAction.RegisterDynamicAction(
$"Selection/Component/{componentName}/Remove",
() => RemoveComponent(componentKey),
$"移除{componentName}组件",
-849
);
}
}
}
private static void CopyComponent(string componentKey)
{
// 复制组件的实现
Debug.Log($"已复制组件: {componentKey}");
}
private static void RemoveComponent(string componentKey)
{
// 移除组件的实现
Debug.Log($"已移除组件: {componentKey}");
}
}主要优势:
- 自动上下文检测: 根据当前选择创建指令
- 临时指令: 面板关闭时自动清理指令
- 可扩展: 适用于任意数量的组件或对象
- 性能优化: 仅在需要时创建指令
Quick Action提供了专门的SceneView集成功能,包括:
- 视图切换: 快速切换到前/后/左/右/顶/底视图
- 正交/透视模式: 切换SceneView的投影模式
- 上下文感知: 只有在SceneView窗口活跃时才显示相关操作
- 中心圆圈: 将鼠标移离中心以激活选择
- 操作按钮: 悬停选择,释放热键或点击执行
- 返回按钮(↑): 返回上一级类别或第一页
- 下一页按钮(←): 当有更多页面时导航到下一页
- 每页最多8个按钮
- 对具有大量操作的类别自动分页
- 动态按钮分配(返回按钮、操作、下一页按钮)
- 选中状态: 显示彩色左边框表示"开启"状态
- 未选中状态: 显示灰色左边框并降低透明度表示"关闭"状态
- 无状态: 没有状态管理的操作不显示边框指示器
- 隐藏操作: 操作可以根据上下文动态隐藏
- 圆形界面会捕获其后面的背景内容,创造无缝的视觉效果
- 背景不是透明的,而是使用界面位置处编辑器内容的截图
- 这创造了透明的错觉,同时保持适当的UI渲染
- 使用描述性路径:
"Tools/Build/Build Player"而不是"Build" - 分组相关操作:
"GameObject/Primitives/Create Cube" - 保持操作名称简洁但清晰
- 避免在验证函数中进行重操作
- 使用验证函数防止错误
- 考虑为可逆操作使用
Undo操作
- 在处理前验证输入
- 提供有意义的错误消息
- 对风险操作使用try-catch块
[QuickAction(string path, string description = null)]Path: 操作路径(必需)Description: 操作描述(可选)Priority: 显示优先级(可选,默认:0)ValidateFunction: 验证方法名(可选)
开发说明: 本项目使用 Cursor 辅助开发。




