首页 > 解决方案 > 如何通过单击不拖动对象在场景视图中的鼠标点处创建对象?

问题描述

通常,大多数对象通过拖动或其他方式放置在场景视图中。我想右键单击鼠标(不拖动对象)以在场景视图中创建对象。我知道这需要一些编辑器编码,但我不知道该怎么做。

更新

经过一番思考,我意识到使用 MenuItem 对我来说非常合适。下面是我的代码:

SLMenuItems:

public class SLMenuItems : MonoBehaviour {

public bool canClickSceneViewToCreatePath = false;

void Start()
{

}

[MenuItem("Component/Create Custom Object")]
static void CreateObject()  {
   Debug.Log("menu item selected");
    canClickSceneViewToCreatePath = true;
} 
}

SLMenuItemsEditor:

    [CustomEditor(typeof(SLMenuItems))]
public class SLMenuItemsEditor : Editor {
    SLMenuItems slMenuItems;


    void OnEnable()
    {
        slMenuItems = (SLMenuItems)target;

    }


    void OnSceneGUI()
        {
            if (slMenuItems.canClickSceneViewToCreatePath)  {
                Vector3 pointsPos = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition).origin;

                if (Event.current.type == EventType.MouseDown && Event.current.button == 0)
                {

                    // create object here at pointsPos

                    slMenuItems.canClickSceneViewToCreatePath = false;
                }
            }

        }
    }

我不断收到以下错误:

Assets/SLMenuItems.cs(23,9): error CS0120: An object reference is required to access non-static member `SLMenuItems.canClickSceneViewToCreatePath'

指向线:

canClickSceneViewToCreatePath = true;

SLMenuItems.

标签: unity3dunity-editor

解决方案


你的CreateObject方法是static,但你的canClickSceneViewToCreatePath价值不是。

它与编辑器脚本无关,而与您的类SlMenuItems本身无关。

方法static没有实例化,或者换句话说,它在该组件类型的所有实例之间共享,而每个组件的非静态值可能不同。

那么静态方法(所有实例都相同)应该如何“知道”它应该访问哪些实例值?

所以要么使方法非静态或变量静态。取决于您的进一步需求。

由于MenuItem需要静态方法,因此也需要将变量设为静态。


我建议你让那个类根本不继承,MonoBehaviour因为它对 GameObject 没有任何行为。它只带来了一些编辑器功能,所以宁愿让它成为一个静态类,可以“活”在资产中而无需实例化。

比您可以SceneView.onSceneGUIDelegate用来注册回调而OnSceneGUI无需为此实现编辑器脚本:

private static GameObject lastCreated;

private static bool isCreating;

public static class SLMenuItems
{   
    [MenuItem("Component/Create Custom Object")]
    private static void CreateObject() 
    {
        Debug.Log("menu item selected");

        isCreating = true;
        lastCreated = null;

        // Add a callback for SceneView update
        SceneView.onSceneGUIDelegate -= UpdateSceneView;
        SceneView.onSceneGUIDelegate += UpdateSceneView;
    }

    private static void UpdateSceneView(SceneView sceneView)
    {
        if(lastCreated)
        {
            // Keep lastCreated focused
            Selection.activeGameObject = lastCreated;
        }

        if(isCreating)
        {  
            if (Event.current.type == EventType.MouseDown && Event.current.button == 0)
            {
                Vector3 pointsPos = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition).origin;

                //Todo create object here at pointsPos
                lastCreated = Instantiate(somePrefab);

                // Avoid the current event being propagated
                // I'm not sure which of both works better here
                Event.current.Use();
                Event.current = null;

                // Keep the created object in focus
                Selection.activeGameObject = lastCreated;

                // exit creation mode
                isCreating = false;
            }
        } 
        else
        {
            // Skip if event is Layout or Repaint
            if(e.type == EventType.Layout || e.type == EventType.Repaint)
            {
                Selection.activeGameObject = lastCreated;
                return;
            }

            // Prevent Propagation
            Event.current.Use();
            Event.current = null;
            Selection.activeGameObject = lastCreated;
            lastCreated = null;

            // Remove the callback
            SceneView.onSceneGUIDelegate -= UpdateSceneView;
        }      
    }
}

但我建议您更改您的问题标题,因为这实际上不是您之前描述的“任务”的解决方案。


推荐阅读