Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Currently selected Handle in editor script

Discussion in 'Scripting' started by keely, May 1, 2011.

  1. keely

    keely

    Joined:
    Sep 9, 2010
    Posts:
    967
    Is there a way of knowing if one of your Handles is currently selected or gets selected in CustomEditor?

    I'm currently using Handles.FreeMoveHandle for every control point in my spline-tool. I try to keep track of the selected point via hacky code, where I'm making an intelligent guess during a mouseUp-event, that tries to determine which of the points is the closest of the click. This works "ok", but I'm getting reports that it's not 100%.
     
    Last edited: May 6, 2011
  2. keely

    keely

    Joined:
    Sep 9, 2010
    Posts:
    967
    There's so much noise that I'm going to bump this once.
     
  3. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    If I understand your issue correctly, have you tried checking GUI.changed after drawing each handle? In theory, this should tell you which one was the last one with which the user interacted. Does that help?
     
    DerMatta likes this.
  4. keely

    keely

    Joined:
    Sep 9, 2010
    Posts:
    967
    I don't know yet, but seems like potential winner. I'm going to test it later when I get home. Thanks!
     
  5. mas

    mas

    Joined:
    Nov 14, 2010
    Posts:
    696
    Any more info on this issue? I am wrestling with the same problem for my Orthello OTPath spline.

    The GUI.changed technique is only set when a handle has been moved.changed, not when you just select a handle ( and it gets outlined with a yellow line )

    Will go and check if I can cast a ray against the handle.

    Update

    I have managed to find the selected handle by doing this :

    - Code goes into the OnSceneGUI() of the editor class

    - Check if the GUIUtility.hotControl has changed ( value of 0 = no control selected )

    - If it hotControl changed, find the current handle by ray casting into the world using HandleUtility.GUIPointToWorldRay(Event.current.mousePosition); and checking if that
    ray intersects with any of the created handles.

    This worlks because I know where I create my handles and within what radius the specific ray should fall to intersect with the handle.

    After I have found the right control, I can cache the controlID for future and quicker lookup. I have not managed to find a way to get a handle's ControlID right after I have created it. ( anyone??)
     
    Last edited: Aug 8, 2011
  6. BertMcD

    BertMcD

    Joined:
    Oct 27, 2011
    Posts:
    1
    Cheers for the info, it pointed me in the right direction.

    The easiest way I found to do this is to name the Control using GUI.SetNextControlName ( ".." ).

    Unfortunately as some of the Handles ( PositionHandle ) create multiple controls only
    the first one will be named, but it easy enough to build your own from the components
    provided.

    Take the PositionHandle as an example we can add the following function to recreate
    it from the other handles provided. As you can see we set the name of each of the
    controls, this will let us use the function GUI.GetNameOfFocusedControl () to get the
    name of the control and use that to lookup an array or dictionary to find the related
    item.

    Vector3 PositionHandle ( Vector3 position, Quaternion rotation, string name )
    {
    // right Axis
    GUI.SetNextControlName ( name );
    Handles.color = new Color ( 1.0f, 0.0f, 0.0f, 0.75f );
    Vector3 newPos = Handles.Slider ( position, rotation*Vector3.right );

    // Up Axis
    GUI.SetNextControlName ( name );
    Handles.color = new Color ( 0.0f, 1.0f, 0.0f, 0.75f );
    newPos += Handles.Slider ( position, rotation*Vector3.up ) - position;

    // Forward Axis
    GUI.SetNextControlName ( name );
    Handles.color = new Color ( 0.0f, 0.0f, 1.0f, 0.75f );
    newPos += Handles.Slider ( position, rotation*Vector3.forward ) - position;

    float sizeMultiplier = 0.0f;

    if ( Camera.current.orthographic )
    {
    sizeMultiplier = Camera.current.orthographicSize * 2.5f;
    }
    else
    {
    Plane screen = new Plane ( Camera.current.transform.forward, Camera.current.transform.position );
    screen.Raycast ( new Ray ( position, Camera.current.transform.forward ), out sizeMultiplier );
    }

    GUI.SetNextControlName ( name );
    Handles.color = new Color ( 1.0f, 1.0f, 1.0f, 0.75f );
    newPos += Handles.FreeMoveHandle ( position, rotation, 0.02f * sizeMultiplier, Vector3.zero, Handles.RectangleCap ) - position;
    }
     
    mrtkhosravi likes this.
  7. tgraupmann

    tgraupmann

    Joined:
    Sep 14, 2007
    Posts:
    828
    Thanks for this post. I wasn't looking for the handle name, but I did want to check if a handle was pressed before raycasting.

    Thanks.
     
  8. littlemy

    littlemy

    Joined:
    Feb 19, 2013
    Posts:
    1
    Just a little addition to this thread, in Unity 4.2 (at least) you can set the ID of some of the handles EG:

    static Vector3 Slider2D(
    int id,
    Vector3 handlePos,
    Vector3 offset,
    Vector3 handleDir,
    Vector3 slideDir1,
    Vector3 slideDir2,
    float handleSize,
    DrawCapFunction drawFunc,
    Vector2 snap,
    bool drawHelper);

    This can be used with GUIUtility.hotControl to identify which handle the user is currently interacting with
     
  9. Demigiant

    Demigiant

    Joined:
    Jan 27, 2011
    Posts:
    3,239
    *NECROBUMP*

    Anyone found a reliable way to get the id of a Handles.FreeMoveHandle and then check if it's selected?
     
  10. Dmitry-Pyalov

    Dmitry-Pyalov

    Joined:
    Dec 13, 2011
    Posts:
    125
    Yep, there is.

    Just make your own Cap function. It receives ControlID, so you can store it somewhere and check against GUIUtility.hotControl.

    Then just call one of the predefined cap functions.
     
  11. snlehton

    snlehton

    Joined:
    Jun 11, 2010
    Posts:
    99
    Thanks. I was struggling with this and your idea worked really great.

    If any of you needs some reference, here's a piece of code that allows you to edit simple segmented line by using the standard move gizmo on the selected node.

    http://pastebin.com/iAtRrggd
     
    John_Leorid likes this.
  12. Xelnath

    Xelnath

    Joined:
    Jan 31, 2015
    Posts:
    402
    Necro bump. I used the following code to detect 'clicks' on FreeMoveHandle:


    Code (CSharp):
    1.  
    2.   private float m_lastClick = -1;
    3.   private float m_doubleClickThreshold = 0.3f;
    4.  
    5.         private Dictionary<int, int> ControlToNode = new Dictionary<int, int>();
    6.         private Dictionary<int, int> NodeToControl = new Dictionary<int, int>();
    7. ....
    8.  
    9.         void OnSceneGUI()
    10.         {
    11.  
    12.  
    13.             for ( int i = 0; i < points.Count; i++ )
    14.             {
    15.                 Vector3 pos = transform.TransformPoint(points[i]);
    16.                 float handleSize = HandleUtility.GetHandleSize(pos);
    17.                 Vector3 newPos;
    18.  
    19.                     newPos = Handles.FreeMoveHandle( pos, Quaternion.identity, handleSize * 0.09f, Vector3.one,
    20.  
    21.                         ( controlID, position, rotation, size ) =>
    22.                         {
    23.                             NodeToControl[i] = controlID;
    24.                             ControlToNode[controlID] = i;
    25.                             HandleFunc( controlID, position, rotation, size );
    26.                         }
    27.                 }
    28.             }
    29.  
    30.             if ( Event.current.type == EventType.Used && wasUsed && GUIUtility.hotControl == 0 )
    31.             {
    32.                 wasUsed = false;
    33.                 Debug.LogFormat( "Click" );
    34.  
    35.                 if ( Time.time < this.m_lastClick + m_doubleClickThreshold && m_lastControl == m_lastHotControl )
    36.                 {
    37.                     if ( ControlToNode.ContainsKey( m_lastHotControl) )
    38.                     {
    39.                         Debug.Log( "Doubleclick" );
    40.                     }
    41.                 }
    42.                 else
    43.                 {
    44.                     if ( ControlToNode.ContainsKey( m_lastHotControl) )
    45.                     {
    46.                         Debug.Log( "SingleClick" );
    47.                         m_lastControl = m_lastHotControl;
    48.                         m_lastClick = Time.time;
    49.                     }
    50.                 }
    51.             }
    52.             else if ( Event.current.type == EventType.Used )
    53.             {
    54.                 wasUsed = true;
    55.                 m_lastHotControl = GUIUtility.hotControl;
    56.             }
    57.     }
    58.  
    59.  
    60.         void HandleFunc( int controlID, Vector3 position, Quaternion rotation, float size )
    61.         {
    62.             if ( controlID == GUIUtility.hotControl )
    63.                 GUI.color = Color.red;
    64.             else
    65.                 GUI.color = Color.green;
    66.             Handles.Label( position, new GUIContent( nodeTexture ), m_handleStyle );
    67.             GUI.color = Color.white;
    68.         }
    69.  
     
    Last edited: Jun 20, 2018
    anisimovdev, Vedran_M and Mr_Mow like this.
  13. DanielK2

    DanielK2

    Joined:
    Jul 17, 2017
    Posts:
    13
    Old thread, but there is something new to the topic. As the previous posts already say you can define the control ID of some handles when you create them inside OnSceneGUI(), for example

    Code (csharp):
    1.  int controlID_thatThisHandleShouldHave = 12345; //typically you obtain unused ID's via GUIUtility.GetControlID() instead of coming up with your own like in this example line
    2. Handles.Slider2D(controlID_thatThisHandleShouldHave, , , , , ); //assign your ID to the created handle
    Then afterwards you can check via

    Code (csharp):
    1. if(GUIUtility.hotControl == controlID_thatThisHandleShouldHave)
    if the control is selected.
    You can combine this info with things like MouseDownEvents, to check if a handle has been clicked .

    This works for all methods of the Handles class that take the "id" parameter. The problem is: Not all of the methods take this parameter. The good thing is: As the years go by more and more methods of the Handles class get new overloads that have this parameter. I don't know how it was back in the day but for example the Handles.FreeMoveHandle method has this parameter now, so there is no need anymore to make an own Cap function as someone further up suggested.

    It get's more complicated with methods like Handles.PositionHandle, because this internally consists of actually seven separate handles (each axis is a separate handle, etc...), so you would have to actually assign 7 separate ID's. So if you want to detect if the user clicked on a PositionHandle you have the problem, that you actually have to rebuild the whole position handle with its seven separate individual handles in order to assign the ID's by yourself. Now the new thing: This seems to change. The Unity code on Github (for Unity 2022.2) shows new public overloads for Handles.PositionHandle and Handle.RotationHandle, that finally take the id's. It takes the PositionHandleIds parameter, which is a struct in which you can assign all 7 control ID's separatly.

    It's not officially released yet, and it may turn out as an undocumented internal feature, but still could be a huge time saver in some cases.
     
    Last edited: May 1, 2022
    JankyOldDog, samana1407 and bearcoree like this.