Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

non-HUD control panel ideas?

Discussion in 'General Discussion' started by tomjoad, Apr 21, 2007.

  1. tomjoad

    tomjoad

    Joined:
    Apr 14, 2007
    Posts:
    37
    Maybe someone has some thoughts about how to go about implementing this:

    I have a scene with objects the user can click on to bring up a simple control panel for that object. I have it working in a HUD style using GUITextures but I would like to try making this within the scene so it shares the transform of the clicked object. (the controls are simple play/stop/fast forward type of buttons).

    I guess there are two ways to go about it:

    --construct a game object with cubes (planes?) for each button and show/hide it when the user clicks

    --try to do it in one plane by tracking where the mouse is on the plane and changing the texture. Not sure how precise mouse tracking on a simple plane would be*

    (are planes more or less otimized than cubes in Unity?)
     
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,758
    You say you want it to share the same transform as the object - may I ask what benefit this would give? If you have it working with GUITextures (it probably looks better that way anyway), it's probably best to stick with that.
     
  3. tomjoad

    tomjoad

    Joined:
    Apr 14, 2007
    Posts:
    37
    Oh, they are supposed to be functioning control panels in the 3D world - to do stuff like switching channels on a tv, changing audio, etc. - so the control panels are "properties" of the objects they control. At least that is the current design plan. It may suck/look bad as you say but I wanted to give it a try.
     
  4. aaronsullivan

    aaronsullivan

    Joined:
    Nov 10, 2005
    Posts:
    986
    I'm sorry I don't have a more detailed response, but it's sort of a performance issue.

    You'd probably use a lot less objects with the single plane method and raycasting can now give you [edit] UV coordinate information back, so you should be able to use a single plane just fine.

    If you're not worried about too many objects, using the 1 object per "button" might be simpler.
     
  5. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Either the objects or a plane would be fine...it just depends on the look you're going for, I would think. You're not going to see any performance difference, unless you have many of these objects all visible at once. Doing the separate objects might be a little easier to script, but you can use raycasting and then use RaycastHit.point to get the point in space where the raycast hit the plane. Or it might be more convenient to use RaycastHit.textureCoord, which will get the uv texture coordinate of where the ray hit. Both will be perfectly accurate.

    (The built-in plane is a grid of 200 polygons, so it's probably more suited to larger objects. Just make your own, so you can have just 2 polygons.)

    --Eric
     
  6. tomjoad

    tomjoad

    Joined:
    Apr 14, 2007
    Posts:
    37
    Thanks for the replies! Good to know that the built-in plane has so many polys. I can make something much lighter than that.

    The deal with this GUI is that only one control panel will be active/visible at a time so the resource hit won't be bad. I'm more inclined/used to the single plane approach though. Anyone know if there are examples or tips on getting texture coordinates?

    I'm thinking that given a mouse event on a plane, if I have some hot spot rects defined relative for that plane I can then drill into the texture to check pixel color to see if it is over a button, etc.

    Insane?
    :?:
     
  7. aaronsullivan

    aaronsullivan

    Joined:
    Nov 10, 2005
    Posts:
    986
    No need to check for color. Just see if it hit inside the hotspots you defined. :) Right?
     
  8. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Nope, sounds reasonable to me. ;) If you checked against a color, you can define any sort of oddly-shaped hotspot you wanted by drawing it. You could also just check against the UV coordinates. Or maybe a combination of the two. How about something like this:

    Code (csharp):
    1.  
    2. function OnMouseDown () {
    3.     var hit : RaycastHit;
    4.     var theCamera = Camera.main;
    5.     Physics.Raycast (theCamera.ScreenPointToRay(Input.mousePosition), hit);
    6.    
    7.     var tex : Texture2D = renderer.material.mainTexture;
    8.     var pixelUV = hit.textureCoord;
    9.     var clickedColor = tex.GetPixel(pixelUV.x * tex.width, pixelUV.y * tex.height);
    10.    
    11.     switch (clickedColor) {
    12.         case Color.red:
    13.             print ("Clicked on red");
    14.             break;
    15.         case Color.blue:
    16.             print ("Clicked on blue");
    17.             break;
    18.         case Color (1, 1, 0, 1):
    19.             if (pixelUV.x < .5)
    20.                 print ("Clicked on yellow on left side");
    21.             else
    22.                 print ("Clicked on yellow on right side");
    23.             break;
    24.         default:
    25.             print ("Clicked somewhere else");
    26.     }
    27. }
    28.  
    Assuming you attach the above script to a UV-mapped object with a mesh collider (other collider types won't work for textureCoord--the built-in plane works nicely for testing in this case), which is using the texture below, and assuming the camera you're using is called Main Camera, that should work. Not sure if that's the best way, but you get the idea I hope....

    --Eric
     

    Attached Files:

  9. aaronsullivan

    aaronsullivan

    Joined:
    Nov 10, 2005
    Posts:
    986
    But... then your interface is limited by colors, no? Updating the color scheme in your game would then require coding. It's workable, but I'll just put in my 2 cents.

    For a flexible, reusable solution, make a public array of structures that have a string for the button name, uvX, uvY, uvW, and uvH.

    Now you could have OnMouseDown() check your uv hit against each item in the array and send a message out to whatever the buttons control using that name.

    Tweakable in the properties list, to boot.
     
  10. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Nah, just expose the colors as public variables instead of hard-coding them, so you can edit them in the inspector. That's what I would do if I were to actually use that method; the code was just an example of functionality.

    Code (csharp):
    1.  
    2. var arrayOfColors : Color[];
    3.  
    Presto--editable list of colors. Nifty. :) The array of structures idea works too, of course, though you're limited to rectangles, but that's actually probably preferrable in most cases. If you really wanted funky clickable shapes but didn't want to bother with colors specifically, then combine the two, sort of like I did with the yellow thing in that script, but more specific. First check to see if you hit any color that isn't the background color, and if so, then check against an array of coordinates to decide what actually happens.

    --Eric
     
  11. hsparra

    hsparra

    Joined:
    Jul 12, 2005
    Posts:
    750
    Could you also have the array as a list of GameObjects? Then on mouse down ask those objects if the click is contained within their bounds.
     
  12. tomjoad

    tomjoad

    Joined:
    Apr 14, 2007
    Posts:
    37
    hey thanks for all the replies to my initial question! I've been tinkering with this 'billboard' GUI approach and it works pretty well. The code below isn't generalized - just tinkering with it.

    Wondering if using setPixels[] would be a more optimized way of doing the various states rather than switching (and storing) entire panel textures

    Code (csharp):
    1.  
    2.  
    3. var tMap : Texture2D;
    4. var tLeft : Texture2D;
    5. var tRight : Texture2D;
    6. var tCenter : Texture2D;
    7. var tBase : Texture2D;
    8. private var pState = false;
    9. private var currentRollover = "None";
    10. function Update() {
    11.    if (pState) {
    12.       var btn = rpGetButton();
    13.       switch(btn) {
    14.          case "Left" : if(currentRollover != "Left") {
    15.             renderer.material.mainTexture = tLeft;
    16.             currentRollover = btn;
    17.             }
    18.          break;
    19.          case "Center" : if(currentRollover != "Center") {
    20.             renderer.material.mainTexture = tCenter;
    21.             currentRollover = btn;
    22.             }
    23.          break;
    24.          case "Right" : if(currentRollover != "Right") {
    25.             renderer.material.mainTexture = tRight;
    26.             currentRollover = btn;
    27.             }
    28.          break;
    29.          default : if(currentRollover != "None") {
    30.             renderer.material.mainTexture = tBase;
    31.             currentRollover = btn;
    32.             }
    33.          }
    34.       }
    35.    }
    36. function OnMouseEnter() {
    37.    pState = true;
    38.    }
    39. function OnMouseExit() {
    40.    pState = false;
    41.    if(currentRollover != "None") {
    42.       renderer.material.mainTexture = tBase;
    43.       currentRollover = "None";
    44.       }
    45.    }
    46. function OnMouseDown() {
    47.    if(currentRollover != "None") {
    48.       print(currentRollover);
    49.       }
    50.    }
    51. function rpGetButton() {
    52.    var hit : RaycastHit;
    53.    var theCamera = Camera.main;
    54.    Physics.Raycast (theCamera.ScreenPointToRay(Input.mousePosition), hit);
    55.    var pixelUV = hit.textureCoord;
    56.    var clickedColor = tMap.GetPixel(pixelUV.x * tMap.width, pixelUV.y * tMap.height);
    57.    switch (clickedColor) {
    58.       case Color.red : return "Left";
    59.       break;
    60.       case Color.blue : return "Center";
    61.       break;
    62.       case Color.yellow : return "Right";
    63.       break;
    64.       default : return "None";
    65.       }
    66.    }
    67.  
    68.  
    69.  
    70.  
     
  13. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Only thing with that code is that you probably want to do some checking with the raycast to see if it's hitting anything (see the example code here). The reason I didn't bother was because I was using it in an OnMouseDown function, which would never get called if the hit wasn't going to succeed.

    --Eric