如何通过单击不拖动对象在场景视图中的鼠标点上创建对象?

时间:2018-11-01 15:42:38

标签: unity3d unity-editor

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

更新

经过深思熟虑,我意识到使用MenuItem对我来说非常合适。这是我的代码如下:

SLMenuItems:

BRL

SLMenuItemsEditor:

currency = 'BRL'
options = '
  <option selected>Select Currency</option>
  <option value="AUD">(AUD) Australian Dollar</option>
  <option value="BRL">(BRL) Brazilian Real</option>
  <option value="CAD">(CAD) Canadian Dollar</option>
  <option value="CZK">(CZK) Czech Koruna</option>
  <option value="DKK">(DKK) Danish Krone</option>
  <option value="EUR">(EUR) Euro</option>
  <option value="HKD">(HKD) Hong Kong Dollar</option>
  <option value="HUF">(HUF) Hungarian Forint</option>
  <option value="ILS">(ILS) Israeli New Sheqel</option>
  <option value="JPY">(JPY) Japanese Yen</option>
  <option value="MYR">(MYR) Malaysian Ringgit</option>
  <option value="MXN">(MXN) Mexican Peso</option>
  <option value="NOK">(NOK) Norwegian Krone</option>
  <option value="NZD">(NZD) New Zealand Dollar</option>
  <option value="PHP">(PHP) Philippine Peso</option>
  <option value="PLN">(PLN) Polish Zloty</option>
  <option value="GBP">(GBP) Pound Sterling</option>
  <option value="SGD">(SGD) Singapore Dollar</option>
  <option value="SEK">(SEK) Swedish Krona</option>
  <option value="CHF">(CHF) Swiss Franc</option>
  <option value="TWD">(TWD) Taiwan New Dollar</option>
  <option value="THB">(THB) Thai Baht</option>
  <option value="TRY">(TRY) Turkish Lira</option>
  <option value="USD">(USD) U.S. Dollar</option>'

options.sub(' selected', '').sub(%r{("#{currency}")}, '\1 selected').html_safe

我一直收到以下错误:

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

指向该行:

    [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' 中。

1 个答案:

答案 0 :(得分:2)

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

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

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

那么,应该如何使用静态方法-所有实例都相同-“知道”,应该访问哪个实例值?

因此,要么使方法为非静态方法,要么使变量为静态方法。取决于您的进一步需求。

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


我建议您使该类完全不继承自MonoBehaviour,因为它对GameObject没有任何行为。它仅带来一些编辑器功能,因此使其成为可以在Assets中“存在”而无需实例化的静态类。

比起您可以使用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;
        }      
    }
}

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