如何通过单击而不拖动对象在场景视图中的鼠标点处创建对象?
How can I create an object at the mouse point in scene view by clicking not dragging the object?
一般情况下,大部分物体都是通过拖动什么的方式放置在场景视图中的。我想右键单击鼠标(不拖动对象)在场景视图中创建一个对象。我知道这需要一些编辑器编码,但我不确定如何去做。
更新
经过深思熟虑后,我意识到使用 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
.
您的 CreateObject
方法是 static
但您的 canClickSceneViewToCreatePath
值不是。
它与编辑器脚本无关,而是与您的 class SlMenuItems
本身有关。
static
方法未实例化,或者换句话说,它在该组件类型的所有实例之间共享,而每个组件的 non-static 值可能不同。
那么静态方法——对所有实例都相同——"know",它应该访问哪些实例值?
所以要么使方法 non-static 要么使变量静态。取决于您的进一步需求。
因为 MenuItem
需要一个静态方法,所以也使变量成为静态的。
我建议您使 class 完全不继承自 MonoBehaviour
,因为它对 GameObject 没有任何行为。它只带来了一些编辑器功能,因此宁愿将其设为静态 class,可以 "live" 在资产中,而无需实例化。
您可以使用 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;
}
}
}
但我建议您更改问题标题,因为这实际上不是您之前描述的 "task" 的解决方案。
一般情况下,大部分物体都是通过拖动什么的方式放置在场景视图中的。我想右键单击鼠标(不拖动对象)在场景视图中创建一个对象。我知道这需要一些编辑器编码,但我不确定如何去做。
更新
经过深思熟虑后,我意识到使用 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
.
您的 CreateObject
方法是 static
但您的 canClickSceneViewToCreatePath
值不是。
它与编辑器脚本无关,而是与您的 class SlMenuItems
本身有关。
static
方法未实例化,或者换句话说,它在该组件类型的所有实例之间共享,而每个组件的 non-static 值可能不同。
那么静态方法——对所有实例都相同——"know",它应该访问哪些实例值?
所以要么使方法 non-static 要么使变量静态。取决于您的进一步需求。
因为 MenuItem
需要一个静态方法,所以也使变量成为静态的。
我建议您使 class 完全不继承自 MonoBehaviour
,因为它对 GameObject 没有任何行为。它只带来了一些编辑器功能,因此宁愿将其设为静态 class,可以 "live" 在资产中,而无需实例化。
您可以使用 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;
}
}
}
但我建议您更改问题标题,因为这实际上不是您之前描述的 "task" 的解决方案。