【问题标题】:How can I create an object at the mouse point in scene view by clicking not dragging the object?如何通过单击不拖动对象在场景视图中的鼠标点处创建对象?
【发布时间】:2019-04-05 20:58:03
【问题描述】:

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

更新

经过一番思考,我意识到使用 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.

【问题讨论】:

  • 1.检测鼠标点击。 2. 实例化游戏对象。我已经为你简化了。如果您迷路了,请使用您当前的代码编辑问题。
  • @Programmer 好的,我想我明白了。这是我想要实现的一小部分。几天前我问过gamedev.stackexchange.com/questions/164911/…,但没有得到任何帮助。如果你能帮我看看,我会很高兴的。
  • 我建议您在 SO 上重新创建该问题。有许多 Unity 用户可以为您提供帮助
  • 查看GetMouseButtonDownScreenToWorldPointScreenPointToRay,具体取决于您的需求
  • @Programmer 我刚刚更新了问题

标签: unity3d unity-editor


【解决方案1】:

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

它与编辑器脚本无关,而是与您的班级SlMenuItems 本身有关。

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

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

所以要么将方法设为非静态,要么将变量设为静态。取决于您的进一步需求。

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


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

您可以使用SceneView.onSceneGUIDelegateOnSceneGUI 注册回调,而无需为此实现编辑器脚本:

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;
        }      
    }
}

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

【讨论】:

  • 使方法非静态意味着我不能将它用作menuitem。使变量static a 后出现以下错误。 error CS0176: Static member 'SLMenuItems.canClickSceneViewToCreatePath' cannot be accessed with an instance reference, qualify it with a type name insteadif (slMenuItems.canClickSceneViewToCreatePath) {slMenuItems.canClickSceneViewToCreatePath = false;OnSceneGUI() 行中
  • 如果您将变量设为静态,您将不再通过实例化目标 (sLMenuItems) 访问它,而是通过类 SLMenuItems 访问它(注意 majuscule S
  • 谢谢,这正是我需要的。还有一件事,鼠标点击后如何保持新创建的对象被选中?
  • 如果您能帮我看看stackoverflow.com/questions/53107167/…,我将不胜感激
  • @hutfelt 你可以使用selection.activeGameObject = <your created object> 来保持专注。请看我的更新。当您说它有效时,您是使用 cmets 的快速修复还是使用我更新的答案中没有编辑器脚本的版本?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多