You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中如何同时启用自定义编辑器工具与地形Paint texture工具?

问题描述

我做了一个简单的自定义编辑器工具,能让鼠标位置保持水平直线,用来在地形上绘制直线纹理。但开启Inspector里地形编辑器的「Paint Texture」工具时,自定义工具就会被禁用;反过来选中自定义工具时,「Paint Texture」工具也会失效。请问怎么才能让这两个工具同时启用?

现象截图

  • 自定义工具选中但Paint Texture已停用:
    Custom tool selected but paint texture is deactivated
  • 选中Paint Texture时自定义工具被取消选中:
    Custom tool got deselected on paint texture selection

自定义工具的OnToolGUI方法代码

public override void OnToolGUI(EditorWindow window)
{
    HandleUtility.AddDefaultControl(GUIUtility.GetControlID(FocusType.Passive));
    Event e = Event.current;
    if (!(window is SceneView))
        return;

    if (!ToolManager.IsActiveTool(this))
        return;

    if (e.shift)
    {
        if (e.type == EventType.MouseDown)
        {
            if (e.button == 0)
            {
                downY = e.mousePosition.y;
            }
        }
        if (e.type == EventType.MouseDrag)
        {
            if (e.button == 0)
            {
                e.mousePosition = new Vector2(e.mousePosition.x, downY);
                Debug.Log("Mouse Position: " + e.mousePosition);
            }
        }
    }
}
解决方法

Unity默认工具系统是互斥的,选中一个就会取消另一个。要实现两者同时生效,得换个思路——要么把自定义逻辑做成全局辅助工具,要么修改自定义工具的判断逻辑,兼容地形工具的激活状态。

方案1:监听SceneView全局事件(推荐)

放弃继承EditorTool,改用SceneView.duringSceneGui全局事件注入鼠标逻辑,这样不管当前激活的是哪个地形工具,只要在SceneView里就能生效。修改后的代码示例:

[InitializeOnLoad]
public class StraightLinePainterHelper
{
    private static float downY;

    static StraightLinePainterHelper()
    {
        SceneView.duringSceneGui += OnSceneGUI;
    }

    private static void OnSceneGUI(SceneView sceneView)
    {
        // 只在地形Paint Texture工具激活且按住Shift时生效,可按需调整判断条件
        if (!Terrain.activeTerrain || !IsTerrainPaintToolActive())
            return;

        Event e = Event.current;
        if (e.shift)
        {
            if (e.type == EventType.MouseDown && e.button == 0)
            {
                downY = e.mousePosition.y;
            }
            if (e.type == EventType.MouseDrag && e.button == 0)
            {
                e.mousePosition = new Vector2(e.mousePosition.x, downY);
                e.Use(); // 关键:标记事件已处理,让地形工具使用修改后的鼠标位置
            }
        }
    }

    // 判断当前是否激活了地形的Paint Texture工具
    private static bool IsTerrainPaintToolActive()
    {
        TerrainEditor terrainEditor = TerrainEditor.activeTerrainEditor;
        if (terrainEditor == null) return false;
        return terrainEditor.selectedTool is TerrainToolPaintTexture;
    }
}

方案2:修改自定义工具的激活判断逻辑

如果一定要保留EditorTool的形式,需要去掉自定义工具的激活判断,转而监听地形工具的状态,同时避免抢占地形工具的输入焦点:

public override void OnToolGUI(EditorWindow window)
{
    // 移除默认控件设置,避免抢占地形工具的输入焦点
    // HandleUtility.AddDefaultControl(GUIUtility.GetControlID(FocusType.Passive));
    Event e = Event.current;
    if (!(window is SceneView))
        return;

    // 判断地形Paint Texture工具是否激活,而非自定义工具本身
    TerrainEditor terrainEditor = TerrainEditor.activeTerrainEditor;
    if (terrainEditor == null || !(terrainEditor.selectedTool is TerrainToolPaintTexture))
        return;

    if (e.shift)
    {
        if (e.type == EventType.MouseDown && e.button == 0)
        {
            downY = e.mousePosition.y;
        }
        if (e.type == EventType.MouseDrag && e.button == 0)
        {
            e.mousePosition = new Vector2(e.mousePosition.x, downY);
            e.Use(); // 确保地形工具接收修改后的鼠标位置
        }
    }
}

关键注意点

  • e.Use()是核心:它会告诉Unity这个事件已被处理,确保修改后的鼠标位置能被地形工具正确接收,同时避免事件冲突。
  • 方案1的体验更流畅:不需要手动切换工具,只要打开地形Paint工具并按住Shift,就自动启用直线绘制逻辑。

内容的提问来源于stack exchange,提问作者Tushar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 15:15:59