2

Generally, most objects are placed in the scene view by dragging or something. I want to right click the mouse (without dragging an object) to create an object in the scene view. I know this would require some editor coding but I’m not sure how to go about it.

UPDATE

After giving it some thought I realised that using a MenuItem would be quite appropriate for me. Here is my code below:

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

        }
    }

I keep getting the following error:

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

pointing to the line:

canClickSceneViewToCreatePath = true;

in SLMenuItems.

hutfelt
  • 321
  • 1
  • 8
  • 1
    1.Detect mouse click. 2. Instantiate GameObject. I have simplified this for you. If you're lost, edit the question with your current code. – Programmer Nov 01 '18 at 15:54
  • @Programmer Okay, I think I get it. This is a small part of what I'm trying to achieve. I asked https://gamedev.stackexchange.com/questions/164911/how-to-create-an-illustrator-photoshop-like-pentool-for-creating-bezier-curves-i a few days ago but haven't got any assistance. I would be glad if you could take a look at it for me. – hutfelt Nov 01 '18 at 16:41
  • 1
    I suggest you re-create that question here on SO. There are many Unity users able to help you – Programmer Nov 01 '18 at 17:55
  • Have a look at [`GetMouseButtonDown`](https://docs.unity3d.com/ScriptReference/Input.GetMouseButtonDown.html), [`ScreenToWorldPoint`](https://docs.unity3d.com/ScriptReference/Camera.ScreenToWorldPoint.html) and [`ScreenPointToRay`](https://docs.unity3d.com/ScriptReference/Camera.ScreenPointToRay.html) depending what fits your needs – derHugo Nov 02 '18 at 07:14
  • @Programmer I just updated the question – hutfelt Nov 03 '18 at 14:14
  • @derHugo I just updated the question – hutfelt Nov 03 '18 at 14:14
  • Change `public bool canClickSceneViewToCreatePath = false;` to `public static bool canClickSceneViewToCreatePath = false;` – Programmer Nov 03 '18 at 15:06
  • @Programmer I made the change you recommended. Now I'm getting the error `error CS0176: Static member 'SLMenuItems.canClickSceneViewToCreatePath' cannot be accessed with an instance reference, qualify it with a type name instead` at the lines `if (slMenuItems.canClickSceneViewToCreatePath) {` and `slMenuItems.canClickSceneViewToCreatePath = false;` in `OnSceneGUI()` – hutfelt Nov 03 '18 at 22:54
  • Because it's a `static`, you have to access it directly without instance with just `SLMenuItemsEditor.canClickSceneViewToCreatePath` **instead of** `slMenuItems.canClickSceneViewToCreatePath` – Programmer Nov 03 '18 at 23:17
  • @Programmer thanks. Now the issue I'm having is the game object with `SLMenuItems` attached has to be selected before code in `OnSceneGUI()` in `SLMenuItemsEditor` is recognised. Is there a way I can get this code to be recognised without selecting the game object? – hutfelt Nov 05 '18 at 14:58
  • It's hard to help you if I don't even know what you're trying to do. What is this plugin doing? **What's** the code in OnSceneGUI doing and **when** should it do it? – Programmer Nov 05 '18 at 15:04
  • @Programmer The MenuItem `CreateObject` makes `SLMenuItemsEditor.canClickSceneViewToCreatePath` true. `OnSceneGUI` is suppose to create a game object at `pointsPos` when `SLMenuItemsEditor.canClickSceneViewToCreatePath` is true and the scene view is left-clicked hence solving the my original problem. But the need to keep the game object selected defeats the whole purpose. – hutfelt Nov 05 '18 at 15:15
  • Subscribe to the event `EditorApplication.update` event. The function you subscribed to is what you should use instead of OnSceneGUI. – Programmer Nov 05 '18 at 15:19

1 Answers1

2

Your CreateObject method is static but your canClickSceneViewToCreatePath value is not.

It has nothing to do with the editor script but with your class SlMenuItems itself.

A static method is not instanced or with other words it is kind of shared between all instances of that component type while the non-static value might be different for each component.

So how should a static method - which is the same for all instances - "know", which of the instances values it should access?

So either make the method non-static or the variable static. Depending on what your further need is.

Since the MenuItem needs a static method make the variable static as well.


I would suggest you make that class not inherit from MonoBehaviour at all since it doesn't have any behaviour for a GameObject. It only brings some editor features so rather make it a static class that can "live" in the Assets without needing to be instanced.

Than you can use SceneView.onSceneGUIDelegate to register a callback for OnSceneGUI without implementing an editor script for that:

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

But I suggest you change your questions title since this is actually not the solution to the "task" you describe before.

derHugo
  • 83,094
  • 9
  • 75
  • 115
  • Making the method non-static would mean I can't use it as a `menuitem`. After making the variable `static` a got the following error. `error CS0176: Static member 'SLMenuItems.canClickSceneViewToCreatePath' cannot be accessed with an instance reference, qualify it with a type name instead` at the lines `if (slMenuItems.canClickSceneViewToCreatePath) {` and `slMenuItems.canClickSceneViewToCreatePath = false;` in `OnSceneGUI()` – hutfelt Nov 03 '18 at 23:08
  • If you make the variable static you don't access it via the instanced target (`sLMenuItems`) anymore but via the class `SLMenuItems` (note the majuscule `S`) – derHugo Nov 04 '18 at 14:10
  • Thanks, this is precisely what I need. One more thing, how can I keep the newly created object selected after the mouse click? – hutfelt Nov 05 '18 at 16:26
  • I would also be grateful if you could take a look at https://stackoverflow.com/questions/53107167/how-to-create-an-illustrator-photoshop-like-pentool-for-creating-bezier-curves-i for me – hutfelt Nov 05 '18 at 16:33
  • @hutfelt you can use `selection.activeGameObject = ` in order to keep it focused. Please see my update. When you said it worked did you use the quick fix from the comments or already the version without an editor script from my updated answer? – derHugo Nov 05 '18 at 17:25
  • I used your updated answer when I said it worked. I just used the new update, it is selected for a split second then it deselects. – hutfelt Nov 05 '18 at 17:57
  • I did something very similar shortly ([see here](https://stackoverflow.com/questions/53120463/how-to-get-screen-click-input-from-anywhere-with-custom-editor-attribute/53125954#53125954)). I think the problem is that right after the mouseclick Unity fires a Layout and Repaint event. Actually it is not the mouseclick deselecting the object but one of those. I updated my answer. So I introduced a bool again and keep catching the events until its not Layout or Repaint again. I'll test it as soon as possible (currently on smartphone) – derHugo Nov 06 '18 at 06:09
  • Sorry for not replying earlier. I just tried the latest update. Since `lastCreated` is a gameObject `lastCreated = false;` gives an error. I commented that line and followed the rest of your update but it doesn't keep the object selected. – hutfelt Nov 13 '18 at 14:43
  • Hey I just noticed that instead of `obj` it should be `lastCreated` and ofcourse `lastCreated=null`. Updated it. – derHugo Nov 13 '18 at 17:26
  • its still doesn't keep selected. As I said earlier, this is just a portion of the larger picture I am trying to achieve which is [How to create an illustrator/photoshop-like pentool for creating bezier curves in Unity](https://stackoverflow.com/questions/53107167/how-to-create-an-illustrator-photoshop-like-pentool-for-creating-bezier-curves-i?noredirect=1&lq=1). I found [this solution](https://github.com/setchi/BezierCanvas) which is a bezier tool on a canvas. I need a solution without a canvas, just directly in the scene view. Not too sure how to restructure this solution. – hutfelt Nov 14 '18 at 11:53
  • @hutfelt were you able to get the continued selection working? – TenOutOfTen Dec 04 '18 at 05:36
  • @derHugo I'm very much interested in the continued selection. I tried it a while ago, but just put it on hold since I couldn't get it working. I just tried the code you provided here and it's not keeping the object selected. – TenOutOfTen Dec 04 '18 at 05:38