Search Unity

GUISpriteUI GUI Object Library with Controls (Updated)

Discussion in 'iOS and tvOS' started by prime31, Jan 13, 2010.

  1. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,426
    As a thank you to the folks of this forum I am sharing the first version of an iPhone GUI library. It is inspired by SpriteManager, SpriteUI and GUIManager and borrows some code from each. With all these sprite/GUI options why make another? None of the others did exactly what I wanted. I created the GUISpriteUI library to have an easy, highly efficient way to draw a game HUD and to provide menus between levels and on start up. GUISpriteUI uses no collections classes and uses standard arrays for all data. GUISpriteUI also makes it easy to get your HUD setup with code that makes sense from the start.

    GUISpriteUI comes prepackaged with the following elements:

    GUISprite: standard sprite object
    GUISpriteButton: standard button with highlighted and normal images. Kicks off a delegate when touched
    GUISpriteToggleButton: 3 state toggle button (checkbox). Sends a delegate function when changed as well
    GUIKnob: volume control-like know with a highlighted and normal state. Can send events continuously (every touchesMoved) or only onTouchesEnded
    GUISlider: horizontal or vertical slider that can send events continuously or only onTouchesEnded
    GUIProgressBar:: progress bar or health meter. The 'bar' portion of the element can be either stretched or only a portion of the image used when it is less than full (see the demo. It toggles between stretched and normal)
    GUIJoystick: fully customizable joystick and optional background image that hangs out behind the joystick
    GUISwipeDetector: detects swipes in all 4 directions or any combination of them inside the given rectangle.


    GUISpriteUI lets you create your own GUITouchableSprite subclasses so that you can make any UI object that you need and it will work seamlessly with the rest of the library.

    Each touchable sprite can have it's touch area set to an area smaller or larger than it's size. You can also have the touch area be a different size when the element is highlighted. This is useful for sliders and knobs so that your finger doesn't have to be directly on them when sliding around.

    The attached package has a fully working scene with all the GUISpriteUI UI elements so you can see how to set everything up. For new project setup just follow the included read me.

    Here are a few samples of a few elements so you can see how easy it is to use GUISpriteUI:

    Code (csharp):
    1. Vector2 textureSize = GUISpriteUI.instance.textureSize;
    2.  
    3. // Create a button at the point 10, 10 from the top-left corner of the screen that is 108 px wid and 37 px high
    4. // UVRect defines the pixel location on your texture map the graphic to use for this button
    5. GUISpriteButton playButton = GUISpriteUI.instance.addSpriteButton( new Rect( 10, 10, 108, 37 ), 3, new UVRect( 0, 0, 108, 37, textureSize ) );
    6.  
    7. // Optional: Add a different graphic for the highlighted state
    8. playButton.highlightedUVframe = new UVRect( 0, 37, 108, 37, textureSize );
    9.  

    Code (csharp):
    1. GUISpriteButton scores = GUISpriteUI.instance.addSpriteButton( new Rect( 10, 57, 108, 37 ), 3, new UVRect( 0, 74, 108, 37, textureSize ) );
    2. scores.highlightedUVframe = new UVRect( 0, 111, 108, 37, textureSize );
    3.  
    4. // Expand our highlighted touch area 30 pixels all around
    5. scores.highlightedTouchOffsets = new GUIEdgeOffsets( 30, 30, 30, 30 );
    6.  
    7. // When the button is touched, this function will be called
    8. scores.action = onTouchUpInsideScoresButton;
    9.  
    10. // Set the opacity to 0.5 for this button
    11. scores.color = new Color( 1, 1, 1, 0.5f );
    Code (csharp):
    1. // Horizontal Slider.  Be sure to offset the sliderKnobs Y value to line it up properly
    2. GUISprite hSliderKnob = GUISpriteUI.instance.addSprite( new Rect( 20, 245, 30, 50 ), new UVRect( 120, 130, 30, 50, textureSize ), 1 );
    3. GUISlider hSlider = new GUISlider( new Rect( 20, 250, 200, 40 ), 2, new UVRect( 120, 80, 200, 40, textureSize ), hSliderKnob, onHSliderChanged );
    4. GUISpriteUI.instance.addTouchableSprite( hSlider );
    5.  
    6. // Increase our hit area a bit while we are tracking along the slider
    7. hSlider.highlightedTouchOffsets = new GUIEdgeOffsets( 20, 30, 20, 30 );
    8. hSlider.value = 0.2f;

    Example animation call:

    Code (csharp):
    1. Vector3 originalPosition = sprite.clientTransform.eulerAngles;
    2. GUISpriteUI.instance.to( sprite, duration, GUIAnimationProperty.EulerAngles, to, Easing.Sinusoidal.factory(), Easing.EasingType.Out );
    A more complicated animation that pulses the alpha of an element from 0.1 to 1.0 would be:
    Code (csharp):
    1. private IEnumerator pulseOptionButton( GUISpriteButton optionsButton )
    2.     {
    3.         GUIAnimation ani;
    4.        
    5.         while( true )
    6.         {
    7.             ani = GUISpriteUI.instance.to( optionsButton, 0.7f, GUIAnimationProperty.Alpha, 0.1f, Easing.Linear.factory(), Easing.EasingType.In );
    8.             yield return ani.chain();
    9.            
    10.             ani = GUISpriteUI.instance.to( optionsButton, 0.7f, GUIAnimationProperty.Alpha, 1.0f, Easing.Linear.factory(), Easing.EasingType.Out );
    11.             yield return ani.chain();
    12.         }
    13.     }
    14.  
    15. // Start pulsing a button
    16. StartCoroutine( pulseOptionButton( optionsButton ) );
    17.  
    All animation routines allow you chain them by just yieding the chain() method. This will return a WaitForSeconds of the amount of time remaining in the animation.
     

    Attached Files:

    Last edited: Apr 26, 2011
  2. teddylife

    teddylife

    Joined:
    Dec 31, 2009
    Posts:
    14
    tried ur scripts.

    Look promising good work man..

    I manage to get one button work, but my limited C# skill stopped me to going forward, i wan to create a setting button where it located top of the screen, when i hit the button, a menu will be pops up.. izzit possible to do that based on the SpriteUI?
     
  3. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,426
    It's very possible and pretty simple. I believe you can actually use the library just fine with Javascript as long as you put it in the Plugins or Standard Assets folders so that it gets compiled first.

    Something like the following (untested) code should be all you need:

    Code (csharp):
    1. GUISpriteButton settingsButton = GUISpriteUI.instance.addSpriteButton( new Rect( 10, 10, 108, 37 ), 3, new UVRect( 0, 0, 108, 37, textureSize ) );
    2. settingsButton.action = onTouchSettingsButton;
    3.  
    4.  
    5. public void onTouchSettingsButton( GUISprite sender )
    6. {
    7.     // Add a background that covers the whole screen and is on a lower level (10)
    8.     GUISprite somePopup = GUISpriteUI.instance.addSprite( new Rect( 0, 0, 480, 320 ), new UVRect( 0, 100, 480, 320, textureSize ), 10 );
    9.    
    10.     // Add any buttons you need for your menu here
    11. }
    I added an animation framework to GUISpriteUI (very, very lightweight using only Coroutines and no Update functions) so if there are some people actually using it I will keep this post updated with the latest version.
     
  4. teddylife

    teddylife

    Joined:
    Dec 31, 2009
    Posts:
    14
    Thanks for the speed bump, will try out and let you know tomorrow as in my location is GMT +8:00 so is middle of the night... :)
     
  5. patch24

    patch24

    Joined:
    Mar 29, 2009
    Posts:
    120
    Very nice uprise78. I'll see about integrating it into my game. Currently I'm using spriteUI. Your screen space and texture UV coord args looks alot easier to read/debug. Button size separately defined, sliders, health bars...all very nice.
    thanks!
     
  6. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,426
    @patch, readability was one major reason for creating it. I was getting annoyed with trying to figure out what parameter I was changing with SpriteUI. That was the big push for going to a Rect based design. It made a lot more sense. The other big thing was performance. There was a lot of room for improvement in that department in SpriteUI, SpriteManager and GUIManager.
     
  7. teddylife

    teddylife

    Joined:
    Dec 31, 2009
    Posts:
    14
    I follow along your code, but it gave me following error:

    Assets/Scripts/GUIMenu.cs(27,129): error CS0103: The name `textureSize' does not exist in the current context

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class GUIMenu : MonoBehaviour {
    5.  
    6.     // Use this for initialization
    7.     void Start () {
    8.        
    9.         Vector2 textureSize = GUISpriteUI.instance.textureSize; //Save the texture size for future usage.
    10.        
    11.         //Create MenuButton
    12.         //**new Rect ( postion_x, postion_y, object_width/object_position_in_texture, object_height/object_position_in_texture)
    13.         GUISpriteButton settingsButton = GUISpriteUI.instance.addSpriteButton( new Rect( 439, 0, 41, 43 ), 3, new UVRect( 11, 594, 41, 43, textureSize ));
    14.         settingsButton.highlightedUVframe = new UVRect( 53, 594, 41, 43, textureSize);
    15.         settingsButton.action = onTouchSettingsButton;
    16.  
    17.    
    18.     }
    19.    
    20.     //Function to call void Start
    21.    
    22.     //MenuButton Action
    23.     public void onTouchSettingsButton(GUISpriteButton sender)
    24.     {
    25.        
    26.         Debug.Log ("Touched");
    27.         GUISprite somePopup = GUISpriteUI.instance.addSprite( new Rect( 67, 41, 364, 239 ), new UVRect( 0, 0, 346, 239, textureSize ), 10 );
    28.     }
    29. }
    I think is the texureSize is not define any help?
     
  8. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,426
    Just change your action to look like this:

    Code (csharp):
    1.    //MenuButton Action
    2.    public void onTouchSettingsButton(GUISpriteButton sender)
    3.    {
    4.        
    5.       Debug.Log ("Touched");
    6.       GUISprite somePopup = GUISpriteUI.instance.addSprite( new Rect( 67, 41, 364, 239 ), new UVRect( 0, 0, 346, 239, GUISpriteUI.instance.textureSize ), 10 );
    7.    }
    You were trying to use the textureSize variable was scoped to your Start() method in your onTouchSettingsButton method.
     
  9. teddylife

    teddylife

    Joined:
    Dec 31, 2009
    Posts:
    14
    Thanks for the help, but there is another problem pops up..

    Sorry i'm noob....

    the error message shows :

    IndexOutOfRangeException: Array index is out of range.
    (wrapper stelemref) System.Object:stelemref (object,intptr,object)
    GUISpriteManager.addSprite (.GUISprite sprite) (at Assets/Plugins/GUISpriteManager.cs:337)
    GUISpriteManager.addSprite (Rect frame, UVRect uvFrame, Int32 depth) (at Assets/Plugins/GUISpriteManager.cs:317)
    GUIMenu.onTouchSettingsButton (.GUISpriteButton sender) (at Assets/Scripts/GUIMenu.cs:27)
    GUISpriteButton.onTouchEnded (Vector2 touchPos, Boolean touchWasInsideTouchFrame) (at Assets/Plugins/GUIElements/GUISpriteButton.cs:82)
    GUISpriteUI.lookAtTouch (iPhoneTouch touch) (at Assets/Plugins/GUISpriteUI.cs:239)
    GUISpriteUI.Update () (at Assets/Plugins/GUISpriteUI.cs:98)
     
  10. teddylife

    teddylife

    Joined:
    Dec 31, 2009
    Posts:
    14
    Done! fix dy, due to the UI Sprite Count i put 1 instead of two.. =.=

    anyway I'm looking forward to explore more on the UISprite. Cool work!
     
  11. teddylife

    teddylife

    Joined:
    Dec 31, 2009
    Posts:
    14
    One question how do i remove the sprite after I addSprite?

    I use the togglebutton as a foundation for my menu. i addSprite when the toggle selected= true; but i wanna kill the UISprite when my selected = false

    Code (csharp):
    1.         public void onToggleButtonChanged( GUISpriteToggleButton sender, bool selected )
    2.     {
    3.         Debug.Log( "onToggleButtonChanged to: " + selected );
    4.         if(selected == true) {
    5.             Debug.Log("Yes");
    6.             GUISprite somePopup = GUISpriteUI.instance.addSprite( new Rect( 67, 41, 364, 239 ), new UVRect( 0, 0, 346, 239, GUISpriteUI.instance.textureSize ), 10 );
    7.         GUISprite HelpButton = GUISpriteUI.instance.addSprite ( new Rect ( 134, 138, 209, 59), new UVRect(0, 239, 209, 59, GUISpriteUI.instance.textureSize ), 10 );
    8.         }
    9.        
    10.         if(selected == false ) {
    11.                 Debug.Log("Remove this idiot");
    12.         }
    13.     }
     
  12. JohnDoe2

    JohnDoe2

    Joined:
    Nov 11, 2009
    Posts:
    25
    This looks really interesting to me. Unfortunately I'm working mainly on a PC where I can't seem to get this to work.

    For one there are compile errors where the scripts make use of iPhone specific input. To work around this I downloaded the iTouch.cs which is included in the GUIManager [1]. I had to add one more enum, but now I can get it to compile and actually display stuff on the PC. Find attached my slightly altered iTouch.cs file that works with this GUI system.

    Now drawing the huds using this works fine on the PC. Unfortunately I still can't click on buttons. I'm wondering if there's any way for you to look for what's causing this. I know I'm asking quite a bit here. I already looked into this but I only have limited iPhone input handling knowledge so I couldn't find a solution.



    [1] http://forum.unity3d.com//viewtopic.php?t=23557&highlight=guimanager
     

    Attached Files:

  13. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,426
    @JohnDoe2, all the logic for triggering button/slider events is based on touches. It wouldn't be too difficult to change it though. You will just need to modify 2 functions in the GUISpriteUI.cs file. The Update() function grabs and analyzes the touches. Just delete the touch code and replace it with the equivalent mouse processing code. (I have no idea what it is because I have only worked with the iPhone version of Unity).
     
  14. JohnDoe2

    JohnDoe2

    Joined:
    Nov 11, 2009
    Posts:
    25
    Thanks a lot. Will give that a try. After the weekend that is :)
     
  15. bpatters

    bpatters

    Joined:
    Oct 5, 2009
    Posts:
    164
    I'm looking into using this tool for my HUD but I'm wondering how you handle this scenario:

    I have lots of objects in game that you can touch and swipe on and interact with. However I don't want those interactions to occur if their behind the HUD control you touched.

    Right now I handle all my "touch" dispatching in a single Update function and then process in the order necessary to ensure this occurs. With this tool I'd looks that ability to have a single touch consumer dispatcher and I'm not sure how to handle this. For example:

    I have a movement pad which you touch and slide in order to move that direction. I also allow the player to touch swipe in the scene to rotate the camera but I don't want the rotation to occur when the user is touch swiping on the movement pad.

    I also don't want scene objects to be touch activated if the user touches on the buttons. However I haven't figured out how to prioritize touch handling without having a single touch dispatcher.

    Any Idea's how I'd do this and use this tool?
     
  16. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,426
    GUISpriteUI can handle all that. You will want to make a new sublass of GUITouchablSprite. Use the GUIButton as a template but add one extra override function.you will be overriding the GUITouchablSprite touchMoved method. In touchMoved just reposition your joystick as the users finger moves around. This can be your joystick. You can also make it more user friendly by icreasing the hit area while it's being touched. Make one more sublass of GUITouchableSprite for your swipe detector. Create this one with a UVRect size of 0,0,0,0 so it is invisible. Make it fullscreen (or as big as you want it) and put it on a higher level than all your other touchables. You can then use the touchMoved method in there to listen for swipes and it will only register touches when the user isn't already touching something else on a lower level (all your other HUD controls). Hopefully that all makes sense.
     
  17. bpatters

    bpatters

    Joined:
    Oct 5, 2009
    Posts:
    164
    That make sense and will handle that scenario. How do I handle scenarios like door's which you touch and they open and other scene objects which respond to touches? I only want them to respond to touches if the HUD doesn't consume the touch.

    I'm planning on having a lot of interactive objects in the scene that the player can touch and interact with and need a method to know if the HUD consumed a touch (IE user interacted with or is interacting with a HUD object for a specific touch id) so I can ignore the touch in that scenario for the objects.

    What I was thinking of doing is modifying the GUISpriteUI and changing the Update() method to something like:
    processUpdate(ref iPhoneTouch[] touchesConsumed);

    and calling it from my own Update method and then skipping touch dispatching to other objects for finger id's that the GUISpriteUI consumed.

    Right now I process all touches and allow UI First dibs and if it doesn't use the touch then I figure out the object that was touched and dispatch the touch to it for processing.
     
  18. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,426
    That should work. You can also do it backwards and use the full screen touchable area From the previous post as a dispatcher. If the touch gets to it then it missed all buttons so you can then either detct the swipe or kick off a raycast to check for your non HUD touchables.
     
  19. bpatters

    bpatters

    Joined:
    Oct 5, 2009
    Posts:
    164
    Good idea. One question, even though the UV set is 0 isn't having an invisible quad over the entire screen going to cause a massive FPS hit due to fill rate limitations of the iphone? I see some pretty significant FPS hits when I have alpha blended objects covering large portions of the screen and I start to hit fill rate limits.
     
  20. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,426
    It shouldn't cause any issues at all because the quad will literally be of zero size. The touchable area is defined seperately and you can make that cover the full screen.
     
  21. bpatters

    bpatters

    Joined:
    Oct 5, 2009
    Posts:
    164
    Sounds great, forgot the touchable area was separate from the size.

    Going to start replacing my current UI with it now :).
     
  22. bpatters

    bpatters

    Joined:
    Oct 5, 2009
    Posts:
    164
    So I gave it a shot today and found a problem, not sure what the fix is yet though.

    I created a simple subclass of GUITouchableSprite that I use as a my movement pad.

    I then added it to the screen using:
    Code (csharp):
    1.  
    2. touchPad = new MovementPad(new Rect(10,192,128,128),0,new UVRect(0,0,128,128,new Vector2(512,512)));
    3.         GUISpriteUI.instance.addTouchableSprite(touchPad);
    4.  
    I then have the following relevant code in the implementation:
    Code (csharp):
    1.  
    2. public MovementPad (Rect frame, int depth, UVRect uvFrame ): base( frame, depth, uvFrame )
    3. {
    4. }
    5.  
    6. public override void onTouchBegan( Vector2 touchPos )
    7.     {
    8.         touchPosition = touchPos;
    9.         touchDownPosition = touchPos;
    10.         touchDown = true;
    11.         Debug.Log("received touch down on :"+touchPos);
    12.         highlighted = true;
    13.     }
    14.  
    15.    
    16.     public override void onTouchMoved( Vector2 touchPos )
    17.     {
    18.         touchPosition = touchPos;
    19.         Debug.Log("received touch move to :"+touchPos);
    20.     }
    21.    
    22.    
    23.     public override void onTouchEnded( Vector2 touchPos, bool touchWasInsideTouchFrame )
    24.     {
    25.        touchDown = false;
    26.        highlighted = false;
    27.        
    28.            Debug.Log("received touch end at :"+touchWasInsideTouchFrame);
    29.     }
    30.  

    First time I touch the pad it works great. All subsequent touches call onTouchBegan immediately followed by onTouchEnd. I then get the touch moves
    and when I release another onTouchEnd.

    So I have two problems:
    1. The immediate onTouchEnd prevents me from processing it correctly because I don't know if it was the real touch end or not.
    2. It also automatically calls onTouchEnd when I move out of bounds of the touchpad instead of "locking" the mouse. I don't strictly need this feature but It's very handy for a touch pad so people don't all of a sudden stop moving cause they went outside the bounds of the touchpad.

    I'm trying to find and fix #1 but not quite sure how your managing touches yet. I don't understand why after it processes touches once it makes them "unused" immediately in the Update function.
     
  23. bpatters

    bpatters

    Joined:
    Oct 5, 2009
    Posts:
    164
    I changed the GUISpriteUI Update method to the below by commenting out the onTouchEnded and it seems to work now. Not sure of any unintended side effects to functionality I haven't used yet.

    Code (csharp):
    1.  
    2. protected void Update()
    3.     {
    4.         // Only do our touch processing if we have some touches
    5.         if( iPhoneInput.touchCount > 0 )
    6.         {
    7.             // Examine all current touches
    8.             for( int i = 0; i < iPhoneInput.touchCount; i++ )
    9.                 lookAtTouch( iPhoneInput.GetTouch( i ) );
    10.            
    11.             // Check all the unused touches so we can call exit if a touch isn't on a button anymore
    12.             for( int i = _numTouches - 1; i >= 0; --i )
    13.             {
    14.                 if( _touchUsed[i] == false )
    15.                 {
    16.                     if( _spriteSelected[i] != null ) {
    17.                         //_spriteSelected[i].onTouchEnded( Vector2.zero, false );
    18.                     }
    19.    
    20.                     removeTouch( i );
    21.                 }
    22.             }
    23.            
    24.             // Set all touchUsed to false
    25.             for( int i = 0; i < MAX_CHANGED_TOUCHES; ++i )
    26.                 _touchUsed[i] = false;
    27.            
    28.         }
    29.  
    30.         // Take care of updating our UVs, colors or bounds if necessary
    31.         if( meshIsDirty )
    32.         {
    33.             meshIsDirty = false;
    34.             updateMeshProperties();
    35.         }
    36.     }
    37.  
     
  24. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,426
    The first thing I would do is define a larger standard touch area for the joystick. After that I would make the highlighted touch area even larger. That will give you a nice buffer for allowing the finger to slide outside of the bounds of the joystick. You can then lock the joystick graphic position to a smaller area than the touch area.

    Another method could be mimicing the GUISlider. It is a touchable area and then the actual slider knob is not touchable. You can make the joystick a regular GUISprite (not touchable) then let the touchable area behind it handle it's placement and the touches.
     
  25. JohnDoe2

    JohnDoe2

    Joined:
    Nov 11, 2009
    Posts:
    25
    Just letting you know that i got this to work on the PC now. All that's left was adding the iTouch.cs script to a gameobject so the update routine that's already in there would get called.
    Also I had to change line 38 in GUISpriteUI.cs to
    iPhoneSettings.screenOrientation = iPhoneScreenOrientation.Landscape;
    to get the buttons to show up in the correct locations. But im not sure if it wasnt me in the first place who changed this to the value i found it in today (LandscapeLeft, instead of Landscape).
    cheers
     
  26. teddylife

    teddylife

    Joined:
    Dec 31, 2009
    Posts:
    14
    Animation allow to do bouncing effect on my GUI? I know is kinda silly question, but appreciate if help... and is there any documentation about the animation script that u using?
     
  27. JohnDoe2

    JohnDoe2

    Joined:
    Nov 11, 2009
    Posts:
    25
    Hey, how do you create a new UILeayer in unity? Can't seem to figure this one out.
     
  28. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,426
    @teddylife, the animation system if VERY simple but it will allow you to make a bouncing animation very easily. Check out the demo project and you will see something very similar to a bounce on one of the buttons when you touch it. There is also a pulse happening on one of the buttons. If you take the same idea and just change the animation there to position instead of alpha you will have a bounce.

    Something like this (untested):

    Code (csharp):
    1. private IEnumerator bounceOptionButton( GUISpriteButton optionsButton )
    2.    {
    3.       Vector3 top = new Vector3( 20, 300, 2 );
    4.       Vector3 bottom = new Vector3( 20, 0, 2 );
    5.       GUIAnimation ani;
    6.        
    7.       while( true )
    8.       {
    9.          ani = GUISpriteUI.instance.to( optionsButton, top, GUIAnimationProperty.Position, 1.0f, Easing.Linear.factory(), Easing.EasingType.In );
    10.          yield return ani.chain();
    11.          
    12.          ani = GUISpriteUI.instance.to( optionsButton, bottom, GUIAnimationProperty.Position, 1.0f, Easing.Linear.factory(), Easing.EasingType.Out );
    13.          yield return ani.chain();
    14.       }
    15.    }
    16.  
    17. // Start bouncing a button
    18. StartCoroutine( bounceOptionButton( optionsButton ) );

    @JonDoe2, I don't have Unity in front of me so this will be a rough guess. In the properties inspector from the Edit menu there will be a layers item. The first 8 layers i believe are taken by Unity so on the next open slot you can add a layer name.
     
  29. JohnDoe2

    JohnDoe2

    Joined:
    Nov 11, 2009
    Posts:
    25
    Got this working. Thanks again for being so quick and helpful ;)

    Unfortunately I'll have to stress you again. Is there any suggested way of hiding previously created buttons? I know there's the removeSprite method, but I feel that would be an overkill. Especially as I'd like to hide/unhide specific buttons based on the current gameplay situation. Do you have any show/hide routines already working for the elements?
     
  30. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,426
    @JohnDoe2, there should be a show/hide method already in there. If you are using MonoDevelop it should popups in the autocomplete when you type 'GUISpriteUI.instance.' Don't quote me on this because I am not at a computer with the source code but it might be GUISpriteUI.instance.showSprite and GUISpriteUI.instance.hideSprite.
     
  31. MugOfPaul

    MugOfPaul

    Joined:
    Jun 13, 2008
    Posts:
    34
    There's definitely an issue with in the Update loop where bpatters had to modify it. It's easiest to see the bug by tapping the Play button in the Sample GUI scene. Tap it once, and it responds properly. But consecutive taps don't work/are flaky. It won't respond properly again until you tap outside the area of the Play button and tap it again.

    Thanks for putting this little framework together. It makes more sense to me than the others. 8)
     
  32. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,426
    I'll give it a look and get an update out.
     
  33. poppers112

    poppers112

    Joined:
    Jan 14, 2009
    Posts:
    4
    Let me preface my post with the fact that I am not very experienced with Unity (and even less so with C#). I am using the GUI with a JS script, but if you provide any code in C# I think I can convert to JS.

    As I was trying to use this GUI and came across 2 things I couldn't figure out.

    1. I tried to make a screen sized button that was 90% off the screen that will later be moved down into full view. The display of the button was 90% off screen, however the touchable area of the button covered the entire screen.

    2. I want to create an array like iPhoneInput.touches but only of touches that are not hitting my GUI elements. I have a 0 sized button with a touchable area of the entire screen. However I don't know how to (or when to) add / remove touches from my array.

    Any help would be greatly appreciated.
     
  34. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,426
    For number 2 if you have a 0 sized button with a hit area that takes up the whole screen then there will be no touches that don't hit your gui. Without seeing your code for number 1 there isnt much I can say. I did a test and was able to make a button almost all the way offscreen that didn't get touched except where the defined hit area was.
     
  35. p-lux

    p-lux

    Joined:
    Oct 21, 2008
    Posts:
    59
    Hi uprise78,

    I didn't read your code for now, but I have some questions about optimizations :

    - how are you sure that the "1 draw call" thing is more efficient than the core GUI classes ? What I think is that coroutines may be less performant than the classical update call. Even if you group all you coroutines calls to build your GUI system and draw one time all the sprites, people can quickly write bad code and forget about the perfs. Did you tested the classical way before writing coroutines and compared the perfs ? (not trolling, just wondering heh :oops: )

    - is the boxing/unboxing really happening in the System.Collections classes ? And what about generics that are now supported by Unity (not on the iPhone , sure). Even if you use simple arrays, there is always a moment where you're forced to cast your objects, no ? Even with abstract or inherits...no ? The use of generics really speeds up things now, did you tried these too ?

    Otherwise, I'll try your code when the time will allow it to me :)

    Bye :)
     
  36. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,426
    The more Update functions you have the slower your code will be. GUISpriteUI only uses coroutines for animations and nothing else so it is very efficient. Quote straight from Unitys optimization docs:

    Code (csharp):
    1. 3. Use Coroutines. The problem with Update calls is that they happen every frame.
    The core GUI classes are horrendously slow. A quick forum search will show tons of posts about that. Boxing/unboxing is part of .NET and there is no avoiding it when putting value types in collection classes (generics solve this but they are not available on the iPhone). There is no casting when you use the built in array type. Quote straight from the Unity docs:

    Code (csharp):
    1. 4. Use Builtin arrays
    2. Builtin arrays are fast, very fast, so use them.
     
  37. bpatters

    bpatters

    Joined:
    Oct 5, 2009
    Posts:
    164
    Any idea how to make the GUI not be affected by Fog?
    In my dungeons I have a decent amount of fog and in the town I don't, my UI gets brighter/dimmer based upon the fog.

    I'm using the Z-Enabled shader that came with the package if that matters.
     
  38. blue2fox

    blue2fox

    Joined:
    Jun 8, 2008
    Posts:
    91
    I've just downloaded GUIManager, and played around with it a bit. It seems really amazing. However, I'm wondering how I can change the images on a GUISpriteButton.

    I'm trying to allow the player to select to the next weapon as they tap on the button, and I was hoping the weapon's image would be displayed on the button.

    I've been using GUIManager with Javascript, and so far what I've done is:

    Code (csharp):
    1.  
    2. private var machineGunFrame : UVRect;
    3. private var shotGunFrame : UVRect;
    4. private var flameGunFrame : UVRect;
    5. private var weaponFrame : Rect;
    6. private var weaponCount : int = 0;
    7.  
    8. function Start()
    9. {
    10.      machineGunFrame = new UVRect( 0, 0, 128, 128, textureSize );
    11.     shotGunFrame = new UVRect( 0, 128, 128, 128, textureSize );
    12.     flameGunFrame = new UVRect( 128, 128, 128, 128, textureSize );
    13.    
    14.     weaponFrame = new Rect(330, 15, 128, 128);
    15.    
    16.     weaponButton = GUISpriteUI.instance.addSpriteButton( weaponFrame, 3, machineGunFrame) as GUISpriteButton;
    17.    
    18.     weaponButton.action = OnWeaponButton;
    19. }
    20.  
    21. function OnWeaponButton(sender : GUISpriteButton)
    22. {
    23.     weaponCount++;
    24.  
    25. }
    26.  
    Any help would be appreciated. Thanks.
     
  39. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,426
    I have no clue how to get fog out of the equation.

    As for changing the image, all you need to do is swap the UV. Each GUISpriteButton has a normalUVRect and a highlightedUVRect.
     
  40. blue2fox

    blue2fox

    Joined:
    Jun 8, 2008
    Posts:
    91
    Wow, thanks for the quick response uprise78.

    I tried using:

    weaponButton.normalUVFrame = shotGunFrame;

    but I get an error saying 'normalUVFrame is not a member of GUISpriteButton'

    highlightedUVFrame works though....

    This might be a silly question, but where should i go to see all the commands?

    Thanks again...
     
  41. blue2fox

    blue2fox

    Joined:
    Jun 8, 2008
    Posts:
    91
    I figured it out. It's:

    weaponButton.uvFrame = new UVRect(0,0,10,10, texturesize);

    Thanks.
     
  42. bpatters

    bpatters

    Joined:
    Oct 5, 2009
    Posts:
    164
    I figured it out.In the Shader change the following line:
    Code (csharp):
    1.  
    2. Cull Off Lighting Off ZWrite On Fog { Color (0,0,0,0) }
    3.  
    To:
    Code (csharp):
    1.  
    2. Cull Off Lighting Off ZWrite On  Fog { Mode Off }
    3.  
     
  43. bpatters

    bpatters

    Joined:
    Oct 5, 2009
    Posts:
    164
    I'm getting an index out of range exception in GUISpriteUI. Pretty much all I'm doing is is pressing the same button over and over to attack. After a while it freaks out and gets this error

    Code (csharp):
    1.  
    2. Mon Feb  1 21:11:33 unknown UIKitApplication:com.mobilizeyourlife.kog3d[0x987a][1485] <Notice>: IndexOutOfRangeException: Array index is out of range.
    3. Mon Feb  1 21:11:33 unknown UIKitApplication:com.mobilizeyourlife.kog3d[0x987a][1485] <Notice>: GUISpriteUI.lookAtTouch (iPhoneTouch touch)
    4. Mon Feb  1 21:11:33 unknown UIKitApplication:com.mobilizeyourlife.kog3d[0x987a][1485] <Notice>: GUISpriteUI.Update ()
    5.  
    6.  
    I'm trying to find the problem but could use your help if you have time to look at it. I love this library as it's really simple and powerful.
     
  44. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,426
    I think that is an easy one. Just update the MAX_CHANGED_TOUCHES. Maybe try doubling it and you should be fine.
     
  45. bpatters

    bpatters

    Joined:
    Oct 5, 2009
    Posts:
    164
    That was my first inclination, turned it up to 5 and it didn't help so posted this. I just put it to 25 and it appears to be working now.

    Great tool btw and thanks for answering posts so quickly. I like this tool over the others because it's really lightweight, simple to use, very fast, and easily extended. Could improve a bit on code documentation though but can't complain for free package :).
     
  46. poppers112

    poppers112

    Joined:
    Jan 14, 2009
    Posts:
    4
    Regarding #1, here is my code:
    Code (csharp):
    1.  
    2.     function Start () {    
    3.         var textureSize : Vector2 = GUISpriteUI.instance.textureSize;
    4.        
    5.         var playButton : GUISpriteButton = GUISpriteUI.instance.addSpriteButton( new Rect( 194, -200, 108, 237 ), 3, new UVRect( 0, 0, 108, 237, textureSize ) );
    6.        
    7.         playButton.action = onPlayClick;
    8.     }
    9.    
    10.     function onPlayClick( sender : GUISpriteButton )
    11.     {
    12.         Debug.Log( "Play clicked" + Time.time );
    13.     }
    14.  
    When I click below the play button, the onPlayClick is still triggered.


    For #2, I should have been more clear. I want to use the 0 sized/full screen touchable area button to capture all clicks for gameplay. This button is on the bottom level of my GUI so any touches to it have avoided all other GUI elements. What I don't know is what events to use to create/modify my new array of touches. Should this be done in the Update function of my gui script? This is my initial thought, but there could be a much more efficient way to handle this.
     
  47. poppers112

    poppers112

    Joined:
    Jan 14, 2009
    Posts:
    4
    I also came across this bug, but noticed it only happened if there were no other touches on the screen. I emptied out the _spriteSelected array if there are no touches on the screen (I only added the else condition in this code below).

    This code has no complicated testing done, only clicking the play button over and over again. I hope this doesn't break anything else.

    Edit: this is the Update function in GUISpriteUI.cs

    Code (csharp):
    1.  
    2.     protected void Update()
    3.     {
    4.         // Only do our touch processing if we have some touches
    5.         if( iPhoneInput.touchCount > 0 )
    6.         {
    7.             // Examine all current touches
    8.             for( int i = 0; i < iPhoneInput.touchCount; i++ )
    9.                 lookAtTouch( iPhoneInput.GetTouch( i ) );
    10.            
    11.             // Check all the unused touches so we can call exit if a touch isn't on a button anymore
    12.             for( int i = _numTouches - 1; i >= 0; --i )
    13.             {
    14.                 if( _touchUsed[i] == false )
    15.                 {
    16.                     if( _spriteSelected[i] != null )
    17.                         // bug fix? commented out this code
    18.                         _spriteSelected[i].onTouchEnded( Vector2.zero, false );
    19.    
    20.                     removeTouch( i );
    21.                 }
    22.             }
    23.            
    24.             // Set all touchUsed to false
    25.             for( int i = 0; i < MAX_CHANGED_TOUCHES; ++i )
    26.                 _touchUsed[i] = false;
    27.         }
    28.         else
    29.         {
    30.             for ( int i = 0; i < MAX_CHANGED_TOUCHES; ++i )
    31.                 _spriteSelected[i] = null; 
    32.         }
    33.  
    34.         // Take care of updating our UVs, colors or bounds if necessary
    35.         if( meshIsDirty )
    36.         {
    37.             meshIsDirty = false;
    38.             updateMeshProperties();
    39.         }
    40.     }
    41.  
     
  48. bernardfrancois

    bernardfrancois

    Joined:
    Oct 29, 2009
    Posts:
    373
    Thanks for sharing your sprite system!

    One thing I added is a script attached to the __UICamera, so I could set a callback function to be executed 'OnPostRender'.

    This was useful because I adapted a project that uses Unity's GUI system. What I do is setting all sprites hidden after UICamera finished rendering, and in the OnGUI methods I set certain sprites visible.
    Like this I could just replace lines with GUI.DrawTexture by lines that make sprites visible.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3.  
    4. public delegate void PostRenderAction();
    5.  
    6. public class UICamera : MonoBehaviour
    7. {
    8.     private PostRenderAction postRenderAction = null;
    9.    
    10.     public void SetPostRenderAction(PostRenderAction postRenderAction)
    11.     {
    12.         this.postRenderAction = postRenderAction;
    13.     }
    14.    
    15.     public void OnPostRender()
    16.     {
    17.         if (postRenderAction != null)
    18.         {
    19.             postRenderAction();
    20.         }
    21.     }
    22. }
    23.  
    Some other remarks:
    • I'm only using sprites and implementing buttons and custom controls myself, so a lot of the code is actually obsolete for me.
    • It's a pity that only one texture atlas can be used. Support for multiple atlases (with one batch per atlas) would be great.
     
  49. bernardfrancois

    bernardfrancois

    Joined:
    Oct 29, 2009
    Posts:
    373
    I noticed a bug; it's possible that some sprites are outside the camera view because of the far clip value (and probably depending on the case also because of the near clip and the camera position).

    Now these values are hard coded in GUISpriteUI.cs, for example:
    Code (csharp):
    1.  
    2.         _uiCamera.farClipPlane = 100.0f;
    3.  
    I noticed the bug when I had 96 sprites, each of them with a different depth value (1-96). I just doubled the far clip plane value, and this did the trick for now.

    I think the camera position and near/far clip values should be determined dynamically depending on the depth values of the sprites in use.
     
  50. jmcclure

    jmcclure

    Joined:
    May 5, 2009
    Posts:
    48
    Thanks very much for sharing this with the community uprise78!

    For those that are interested in having one UI for both the iPhone and a PC/Web version of their game - I managed to get this working on the PC.

    There are only 3 important changes -

    1) I had to add a modified version of the iTouch.cs file from GuiManager.cs. I added this to the Assets\Plugins directory
    2) I attached the iTouch.cs script to the UIManagerObject in the example scene.
    3) I edited GUISpriteUI where it determines _uiCamera.orthographicSize by screen orientation to look like this:

    Code (csharp):
    1.  
    2. #if UNITY_IPHONE
    3.         if ( iPhoneSettings.screenOrientation == iPhoneScreenOrientation.Landscape )
    4.         {
    5.             _screenResolution = new Vector2( 480.0f, 320.0f );
    6.             _uiCamera.orthographicSize = 160.0f;
    7.         }
    8.         else
    9.         {
    10.             _screenResolution = new Vector2( 320.0f, 480.0f );
    11.             _uiCamera.orthographicSize = 240.0f;
    12.         }
    13. #else
    14.         _screenResolution = new Vector2(Screen.width, Screen.height);
    15.         _uiCamera.orthographicSize = Screen.height / 2.0f;
    16. #endif
    17.  
    That's it! Now the code will work with any of your builds. I've attached the two files I changed/added. Just copy both into the Plugins folder (overwrite the GUISpriteUI.cs) and you should be good to go.

    Thanks to JohnDoe2 for getting me started. I just wanted to give the community an easy way to pick up and run with it.
     

    Attached Files: