RuntimeUnityEditor icon indicating copy to clipboard operation
RuntimeUnityEditor copied to clipboard

Select object under cursor/point of aim

Open Paddywaan-zz opened this issue 5 years ago • 10 comments

Runtime editor is really super useful, but often, it can sometimes be difficult to even find the right entity that you are after, especially when you are searching through a few hundred objects. Would it be possible, and have you considered implementing a method to select the current GO under the cursor / point of aim

Would be useful to have some utility functions such as cloning/duplciation & removal.

Credit to Elysium#5804 for the idea

Paddywaan-zz avatar Nov 08 '19 18:11 Paddywaan-zz

My current idea on how to do it is to transform object positions into screen coordinates and select the closest one to the cursor. It will probably be quite inaccurate, but it won't depend on colliders.

ManlyMarco avatar Nov 08 '19 22:11 ManlyMarco

Try this Marco, get the object via protected fields in StandAloneInputModule via reflection. Not raycasting or anything else. Works like a charm in my RuntimeEditor. Just access what Unity already knows. (They just lock you out unless your an internal). I hate the dependency on a Collider, but considering the performance impact, I'll take just adding a empty collider. You can always remove it once you use it. I tried your method of transform conversion, and the impact was horrible for one, and two, yes Very inaccurate, half the time not even finding the object.

EDIT: This will detect both UI element and worldspace objects.

/* NOTE: Each Mesh GameObject must have a Collider.

	Object[] allMeshRenders = GameObject.FindObjectsOfType(typeof(MeshRenderer));
	foreach(var renderer in allMeshRenders)
	{
		MeshRenderer tmp = renderer as MeshRenderer;
		GameObject tmpGO = tmp.gameObject;

		if (!tmpGO.HasComponent<Collider>()) 
		{
			tmpGO.AddComponent<BoxCollider>(); 
		}
	}

*/

using System; using System.Reflection; using UnityEngine; using UnityEngine.EventSystems; using Object = UnityEngine.Object;

public class PointerInteraction : MonoBehaviour
{
    public GameObject ClickedObject = null;
    public GameObject HoverObject = null;

    private Camera activeCam;
    private StandaloneInputModule InputSystem;

    void Awake()
    {
        // Make sure we have an active Camera
        activeCam = Camera.main;
        if(activeCam == null)
        {
            activeCam = Camera.current;
            if(activeCam == null) { Debug.LogError("[UIMouseInteraction] - Main Camera not Found!"); }
        }

        // Make sure we have an EventSystem
        EventSystem eventSystem = GameObject.FindObjectOfType<EventSystem>();
        if (eventSystem == null)
        {
            eventSystem = gameObject.AddComponent<EventSystem>();
        }

        // Make sure we have a StandaloneInputModule
        InputSystem = eventSystem.GetComponent<StandaloneInputModule>();
        if (InputSystem == null) { InputSystem = eventSystem.gameObject.AddComponent<StandaloneInputModule>(); }
        if (!InputSystem.enabled)
        {
            InputSystem.enabled = true;
            InputSystem.ActivateModule();
        }

        // Make sure the current Camera has a Raycaster
        if(activeCam.GetComponent<PhysicsRaycaster>() == null || activeCam.GetComponent<Physics2DRaycaster>() == null)
        {
            activeCam.gameObject.AddComponent<PhysicsRaycaster>();
            activeCam.gameObject.AddComponent<Physics2DRaycaster>();
        }
    }

    void Update()
    {
        Type objType = typeof(StandaloneInputModule);
        FieldInfo focusedObjectField = objType.GetFieldValue("m_CurrentFocusedGameObject");
        if (InputSystem != null) { HoverObject = (GameObject)focusedObjectField.GetValue(InputSystem); }

        FieldInfo eventField = objType.GetFieldValue("m_InputPointerEvent");
        PointerEventData pointerEvent = (PointerEventData)eventField.GetValue(InputSystem);
        if (pointerEvent != null)
        {
            if (pointerEvent.button == PointerEventData.InputButton.Left)
            {
                ClickedObject = pointerEvent.pointerPressRaycast.gameObject;
            }
        }
	}
}

public static class ReflectionHelper
{
    public static T GetFieldValue<T>(this object obj, string name)
    {
        // Set the flags so that private and public fields from instances will be found
        var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
        var field = obj.GetType().GetField(name, bindingFlags);
        return (T)field.GetValue(obj);
    }

    public static FieldInfo GetFieldValue(this Type type, string fieldName)
    {
        FieldInfo field = null;
        field = type.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Default);
        return field;
    }

    public static FieldInfo[] GetAllFields(this Type type)
    {
        FieldInfo[] fields = null;
        fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Default);
        return fields;
    }
}

wh0am15533 avatar Jan 07 '20 08:01 wh0am15533

You want to explicitly enable that StandAloneInput module also, since Assets like ReWired, InControl, etc like to implement their own Event system in games. (i.e Bridge Constructor).

wh0am15533 avatar Jan 07 '20 08:01 wh0am15533

Good idea but I would like to avoid adding colliders or messing with the event system to keep compatibility with as many games as possible. This could be made into a separate plugin though, since there's an API to select gameobjects or open objects in inspector.

GetComponent<RuntimeUnityEditor5>().Instance.TreeViewer.SelectAndShowObject(/*some transform*/);
GetComponent<RuntimeUnityEditor5>().Instance.Inspector.InspectorPush(new InstanceStackEntry(/*some object*/, "Tab name"));

ManlyMarco avatar Jan 07 '20 13:01 ManlyMarco

yeah I was looking for alternate methods too for compatibility and was hard pressed to find anything. Alot only work for either Canvas or Mesh objects, a require adding a script to the GO. I wanted to avoid all that, and just essentially have a hook in the event system... but it still required a damn collider. Unity internals are using raycasts are that requires a collider. I'll let you know if I come across something cause it's frustrates me also. Something so easy, yet so hard.

wh0am15533 avatar Jan 07 '20 23:01 wh0am15533

have you seen this? RuntimeEditor (it's the original developer)

Lots of great example coding. But it's more geared to in-house uses given it's integration during game build. But some good stuff none the less.

wh0am15533 avatar Jan 13 '20 08:01 wh0am15533

This one is very nice, I didn't see it before. Thanks for linking it!

ManlyMarco avatar Jan 13 '20 14:01 ManlyMarco

np. you'll find the within it, are also prior release unitypackages also. current dev build is 2.1.0, I find that v2.0.5 the best without the AR/VR overhead that their adding now. Note: On his forum post for the product he states in-depth on how to use it without resourcemaps. Also, look under issues at the repo for the licensing (https://choosealicense.com/no-permission/) Look under branches. MIT baby! ;) Also, I found a solution this particular 'issue'. that doesn't use any colliders, i'lll post it later when I have alittle more time. Actually, found 3 ways but this one seemed to play the nicest without performance hits.

wh0am15533 avatar Jan 13 '20 18:01 wh0am15533

Here's a vertex raycaster that works without any colliders. just attach to a camera. read the NOTE.txt, It detects both Worldspace and UI objects. It has some added features that can be pulled out if nothing fancy is needed. I'm not sure where I originally found it, some forum I was reading through. Anyways, see how this works out. VertexRaycast.zip

Another method I was playing with is through camera culling. It works also. GetNearestObjectsWithCullingGroup.zip

Other methods include KD-Trees or OcTrees, but I think those are a bit heavy, due to having to rebuild the tree's anytime something moves.

wh0am15533 avatar Jan 15 '20 01:01 wh0am15533

VertexRaycast method looks promising, if it works well I'll add it in together with the gizmos when I get some time to work on this project. Again, thank you for the help!

ManlyMarco avatar Jan 16 '20 13:01 ManlyMarco