Search Unity

  1. Unity 2019.1 is now released.
    Dismiss Notice

Prime31 UIToolkit Multi Resolution GUI Solution Ready for Use...and it's free

Discussion in 'iOS and tvOS' started by prime31, May 3, 2011.

  1. Matkins

    Matkins

    Joined:
    Aug 24, 2009
    Posts:
    150
    @ Prime

    I'm working on a letterbox system so that i can have a fixed aspect ratio regardless of the device's aspect ratio. This is more inspired by the huge variability of Android phone aspect ratios rather than the difference between iPhone and iPad.

    I moded UI.cs Awake() call so that the ui camera's viewport rect is adjusted to a fixed aspect ratio (specifiable in the inspector). But this breaks some of the sprite positioning, and all of the touchFrames (even though the displayed touch debug areas still appear correct, they're actually not correct). So clearly some modifications need to be made in other scripts if I want to successfully implement this letterbox functionality into UIToolkit.

    So, if you think what I'm working on here is a good idea, perhaps you can help me by pointing me to the things that will need to be corrected, to be in accordance with the ui camera's viewport rect.

    You can see my UI.cs mods so far on my branch:
    https://github.com/matkins85/UIToolkit/blob/master/Assets/Plugins/UIToolkit/UI.cs
     
  2. Acumen

    Acumen

    Joined:
    Nov 20, 2010
    Posts:
    1,041
    @Prime31
    I got another question.
    As bjohnson said, we switched out our entire GUI system with the help of your toolkit and it pretty much works like a charm. I was able to test it today on an actual device for the very first time - and boy is it impressive.
    Thanks so much for making this available. Combined with your other very valuable tools and plugins, we've got everything laid out for a nice release.

    What I was wondering.
    Would it be possible to use this Kit for actual ingame purposes ?
    Theoretically we could switch out all the 3d assets to a prerendered 2dsprite approach. Would this be a performant way of doing things with your toolkit too, or would you say its use is limited to actual GUI, because of they way it handles things ?
    The texture atlas would have the same size as it is now, but we would save thousands of polyons that wouldn't have to be rendered at all, but instead it would be 2d images only with the help of your toolkit.
    And we wouldn't have the ugly aliasing effect around all the 3d elements - which basically make up 90% of the screen.

    Basically the game consists of elements dropping from the sky and the player has to tap these away. There's no fancy effects or sprite animations involved, at the very best one image gets swapped out. So the sprites wouldn't be used in a very advanced way, not differing too much from the GUI itself.
    Or is the code involved in the GUI stuff too complicated/advanced/limited in order to work in the actual game itself ?
     
  3. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,427
    @Acumen, you can definitely use it for an in game HUD. That is one of it's main purposes. It is quite lightweight and perfect for a HUD.

    @matkins, you will need to modify a bunch of stuff to get things working at a particular aspect ratio. The entire UIToolkit was built with pixel perfect in mind so forcing a particular aspect ratio will throw off a lot of little things. Is there any reason the relative positioning methods don't fit your needs?
     
  4. Acumen

    Acumen

    Joined:
    Nov 20, 2010
    Posts:
    1,041
    Mh, we actually use it for the HUD already. I think I didn't explain myself properly, once again.

    What I meant was, would it be possible and performant to use the toolkit, not only for the GUI and HUD but also for the actual game elements itself.
    The game uses a 3d background environment and 3d models of the "bombs" and extra items. These weigh in at around 4000 triangle in average.

    So I was wondering, since the perspective is always the same, in theory the entire game could use a sprite atlas, with the background being a prerendered 480x320 picture and all bombs and items being prerendered sprites too, faking the perspective and volume of the former 3d models.

    From my artist point of view the benefits would be no ugly aliasing effect that the 3d models come with and filesize would be smaller since we don't use meshes anymore.

    Obviously I can't grasp/compare how a GUI/HUD behaves and is set up and programmed to work, compared to the more complex game environment with all the rules to work with.

    So I think I gotta ask again:
    Would we really only benefit from switching out 3d models/environments with pure 2d sprites, or will there be new issues and hurdles coming up with using the toolkit in such a way - it maybe wasn't designed for.
     
  5. Matkins

    Matkins

    Joined:
    Aug 24, 2009
    Posts:
    150
    @prime

    The game is a fixed screen 2D game. It makes sense for me to use a letterboxed main camera so the game plays equally on all devices, ie, the screen matches the play area, the edge of the screen is the edge of the game.

    If i'm letterboxing the game, then ideally i'd like to letterbox the gui, else it'd look a bit odd on some devices, as if some gui elements are popping out of the screen.

    UIToolkit's design is really ideal on iPhone where the aspect ratio is the same on all devices. It's also not too bad on iPad, just slightly wider, doesn't hurt too much. But on Android it's totally messed up. Most devices differ, and the definition of most devices is inbetween the 3 and 4 iPhone models anyway, so SD art is too small and the HD art is too big.

    Do you think having optional letterboxing, and optional sprite scaling, is going to be a nightmare to implement into the current UIToolkit?
     
  6. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,427
    @Acumen, you should be fine going that route. The library is very efficient and only modifies itself a maximum of once per frame.
     
  7. Acumen

    Acumen

    Joined:
    Nov 20, 2010
    Posts:
    1,041
    Hm, that definitely sounds interesting.
    If we may not be able to pull it off for this project, we definitely will put some thinking into this for future games. Seems like a very streamlined process after all - at least from what I can gather from the comments of my genius programming buddy.
    Thanks for answering so quickly, once again.
     
  8. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,427
    @Matkins, I'm not really sure how difficult it would be. I would say give it a go and if you start an avalanche after the first few changes you make run away fast.

    If all else fails, I would personally just use the relative positioning extensions. You can set a per-device pixel valued buffer (top/bottom margin) and just position everything from that location.
     
  9. mmuller

    mmuller

    Joined:
    Nov 23, 2010
    Posts:
    87
    Question, did I read previously that someone was looking into having text elements with optional underlying sprites? and if so whats the progress? It would make translations a breeze for future games :)
     
  10. gregmax17

    gregmax17

    Joined:
    Jan 23, 2011
    Posts:
    183
    Does anyone know why this bit of code doesn't work in Javascript?

    Code (csharp):
    1.  
    2. var scores = UIContinuousButton.create( "scoresUp.png", "scoresDown.png", 0, 0 );
    3. scores.positionFromTopLeft( .24f, .02f );
    4.  
    I get the error "'positionFromTopLeft' is not a member of 'UIContinousButton'"
     
  11. Matkins

    Matkins

    Joined:
    Aug 24, 2009
    Posts:
    150
    @gregmax17

    Do this instead:

    Code (csharp):
    1.  
    2. var scores = UIContinuousButton.create( "scoresUp.png", "scoresDown.png", 0, 0 );
    3. IPositionablePositioningExtensions.positionFromTopLeft( scores, .24f, .02f );
    4.  
     
    Last edited: Jun 9, 2011
  12. Matkins

    Matkins

    Joined:
    Aug 24, 2009
    Posts:
    150
    I don't see this functionality. With UIRelative I can position from an edge, but can't position relatively from a position. I could add that method to UIRelative though, i guess that's what you meant...

    EDIT: never mind, i'll keep this stuff external for now, incase i'm the only one working this way.
     
    Last edited: Jun 9, 2011
  13. actuallystarky

    actuallystarky

    Joined:
    Jul 12, 2010
    Posts:
    183
    @Prime 31

    I can't seem to recreate buttons after I've destroyed them. At first I thought this had something to do with all the messing about I was doing preserving the UI.cs objects between loads and destroying dupes but I've isolated it down to a simple case that doesn't involve loading at all.

    First I define an empty button variable. And assign it to a created button in start().

    UIButton buttonPlay;

    Code (csharp):
    1. void Start()
    2. {
    3.        buttonPlay = UIButton.create ("Button_Play.png", "Button_Play_pressed.png", 10, Screen.height);
    4. }
    If I then delete that button with -

    Code (csharp):
    1. buttonPlay.delete
    the button appears deleted but if I then try to recreate that button and reassign it back to the original variable with

    Code (csharp):
    1. buttonPlay = UIButton.create ("Button_Play.png", "Button_Play_pressed.png", 10, Screen.height);
    nothing shows on screen, though I can see the buttonsprite being created in the inspector. So am I doing something horribly wrong or is there a genuine bug?
     
  14. Matkins

    Matkins

    Joined:
    Aug 24, 2009
    Posts:
    150
    @actuallystarky

    I'm doing precisely what you're doing there, except i use .destroy() instead of delete. That works for me.
     
  15. actuallystarky

    actuallystarky

    Joined:
    Jul 12, 2010
    Posts:
    183
    @Matkins

    I got excited there for a second, until I realised that it was a typo on my behalf - I am using .destroy(), I just typed the wrong thing in my post.

    So it works for you? Must be some of my jiggery pokery in the UI classes then. I'll try reverting all my changes and see what that does.
     
  16. mmuller

    mmuller

    Joined:
    Nov 23, 2010
    Posts:
    87
    @actuallystarky (how twitter !),

    Rather than destroy the instance why not use:

    UI.firstToolkit.removeElement (buttonPlay);

    Instead? It might be better than using destroy... Also from my experience to destroy an element you could try:

    Destroy (buttonPlay);

    Maybe? Bear in mind I am using c# rather than JS and might be completely wrong!

    Regards,

    mmuller
     
  17. actuallystarky

    actuallystarky

    Joined:
    Jul 12, 2010
    Posts:
    183
    @Matkins

    Doh! I was creating my button offscreen so of course I couldn't see it. So destroying and then recreating seems to work fine within the same scene. Cool.

    So the problem I'm left with is when I have the UI not destroying on load, everything works fine loading through the levels until I get back to my original front end screen. My front end script tries to recreate the buttons but it doesn't work for some reason. It works just fine if I do destroy the UI object and reload everything but I lose my awesome loading screen :( Strange.

    I'll keep poking at it.
     
  18. actuallystarky

    actuallystarky

    Joined:
    Jul 12, 2010
    Posts:
    183
    @Matkins

    Found it! And what have I learnt late this night when I should be in bed? I have learnt not to assume that destroy(gameobject) happens immediately. So my code that read like this -

    Code (csharp):
    1.     protected override void Awake ()
    2.     {
    3.         //  Search for all UI objects in the scene
    4.         UI[] uis = FindObjectsOfType (typeof(UI)) as UI[];
    5.         foreach (UI ui in uis)
    6.         {
    7.             if (ui != transform.parent.GetComponent<UI> ())
    8.             {
    9.                 lastInstance = false;
    10.                 Destroy (transform.parent.gameObject);
    11.             }
    12.         }
    13.        
    14.         // Set instance to this so we can be accessed from anywhere
    15.         instance = this;
    16.        
    17.         base.Awake();
    18.  
    19.         _spriteSelected = new UITouchableSprite[5];
    20.         for( int i = 0; i < 5; ++i )
    21.             _spriteSelected[i] = null;
    22.     }
    really needs one important line added -

    Code (csharp):
    1.     protected override void Awake ()
    2.     {
    3.         //  Search for all UI objects in the scene
    4.         UI[] uis = FindObjectsOfType (typeof(UI)) as UI[];
    5.         foreach (UI ui in uis)
    6.         {
    7.             if (ui != transform.parent.GetComponent<UI> ())
    8.             {
    9.                 lastInstance = false;
    10.                 Destroy (transform.parent.gameObject);
    11.                 [B]return[/B];
    12.             }
    13.         }
    14.        
    15.         // Set instance to this so we can be accessed from anywhere
    16.         instance = this;
    17.        
    18.         base.Awake();
    19.  
    20.         _spriteSelected = new UITouchableSprite[5];
    21.         for( int i = 0; i < 5; ++i )
    22.             _spriteSelected[i] = null;
    23.     }
    Note the return statement after the destroy. This halts the awake function just before it assigns the Instance variable. I was having dupe UI objects reassign the instance variable to themselves and then deleting themselves, breaking the UI quite thoroughly.
     
  19. Matkins

    Matkins

    Joined:
    Aug 24, 2009
    Posts:
    150
    @prime

    The trouble with just offsetting the sprites from top and bottom to create the margin i need for the letterbox (as opposed to being able to adjust the ui camera viewport) is that sprites will still be visible when outside the supposed screen area. If you imagine the black letterbox top and bottom of screen, and i animate a button to exit the top of the screen, it will appear to go over the black bar rather than beneath it and out of view. I'm trying to decide if I can live with that or not. Seems a bit weird.

    In Nano Panda they've opted for a kind of letterboxed solution on Android. But instead of the excess area being black it has a background pattern there. I guess if I take that approach then having sprites overhanging the main screen area doesn't look so bad.
     
  20. Matkins

    Matkins

    Joined:
    Aug 24, 2009
    Posts:
    150
    @actuallystarky

    I hope you realize that the rest of the code in that Awake won't get called if the return is called. If what you're trying to do is delay the rest of the code for a frame then change Awake's type from void to IEnumerator and then do "yield return null;"

    But even that might not work because the ui list in your foreach was declared the frame before... so perhaps just putting "break;" instead of "return;" will be what you want.

    But apologies if i'm misunderstanding what you're trying to do.
     
  21. raptoplob

    raptoplob

    Joined:
    May 24, 2011
    Posts:
    12
    @prime
    I'm changing the button image by using the following code:
    Code (csharp):
    1.  
    2. function statusClicked(sender : UIButton ){
    3.    
    4.     var statusActiveSprite =  UI.firstToolkit.addSprite( "pda_status_active.png", -4000,0);
    5.     sender.uvFrame = statusActiveSprite.uvFrame;   
    6.     statusActiveSprite.destroy();
    7. };
    8.  
    Is this the best method to do so? Also is destroying the sprite once its used the most efficient option?

    However I've got another problem now... When I click another button I will need the previously selected button to return to the normal image, however the only way I could reference a UI element was through the 'sender' object passed to the function. I thought that declaring the variables at the top of the script would work, but that actually stops the whole thing from work. Thanks again.
     
  22. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,427
    @matkins, why not make a UISprite that is black or patterned, stick it on a high zIndex and have that be your letterbox?

    @raptoplob, that is the correct way to change the image used. You definitely have something wrong with your code if you can't keep a reference to a button around. I almost never make buttons without keeping a reference to them.
     
  23. Vandash

    Vandash

    Joined:
    May 12, 2011
    Posts:
    11
    Hi Prime :)
    Been using two UIToolkit to have a fullscreen texture as well as my UI, and my UI is behind the fullscreen texture..
    I tried to add some basic "material.renderQueue += drawOrder;" code, but it breaks everything, and screen turns white.

    What would you recommand to make sure my fullsreen texture (which is on a UIToolkit atlas) renders before the other gui elements (on another UIToolkit atlas) ?

    (for the fullscreen texture i'm using the special material for multiple UIToolkit, and everything else works fine except the draw order)
     
  24. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,427
  25. Vandash

    Vandash

    Joined:
    May 12, 2011
    Posts:
    11
    It works like a charm, thank you :)
     
  26. BurgZerg-Arcade

    BurgZerg-Arcade

    Joined:
    Jun 22, 2009
    Posts:
    119
    I have been looking around while I play with the UIToolkit (awesome job btw) and was wondering if there was a way to add UIHorizontalLayout to a UIVerticlalPanel, or a different way to achieve what I am after? Basically I want to use one of the layouts to layout some elements in a certain order/direction, but then add them to a larger grouping container.

    Thing slots in an inventory in a 3x3 grid.

    Any help would be appreciated.

    Petey
     
  27. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,427
    @Burg, it is quite possible that a UIHorizontalLayout can be put into a UIVerticalLayout. I have never actually tried so I can't confirm for sure but it's worth a shot.
     
  28. BurgZerg-Arcade

    BurgZerg-Arcade

    Joined:
    Jun 22, 2009
    Posts:
    119
    It is looking for a UISprite. It will error if you try to add a UIHorizontalLayout. Right now I am not seeing a way to do what I want the way I want. I just wanted to make sure I was not missing something simple.

    Petey
     
  29. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,427
    Hmmm...there was an object hierarchy change not to long ago and I'm wondering if UISprite is the proper requirement. It is quite possible it can be changed to a class lower in the hierarchy but don't quote me on that.
     
  30. BurgZerg-Arcade

    BurgZerg-Arcade

    Joined:
    Jun 22, 2009
    Posts:
    119
    It is time for Dr Who with the family. I will take a look after the son is in bed. I was halfway through the next tutorial before I realized I could not do what I wanted the way I wanted. I will post a solution when I get the tutorial done.
     
  31. BurgZerg-Arcade

    BurgZerg-Arcade

    Joined:
    Jun 22, 2009
    Posts:
    119
    It is late and I am headed to bed, but I did get the video done. The problem occurs at 12:20. Well off to bed with me. I will take another look at it tomorrow when the son is down for his nap.
     
  32. actuallystarky

    actuallystarky

    Joined:
    Jul 12, 2010
    Posts:
    183
    I've got some text in my game up and running. All the functions work nicely and I can fade it out, position it,change colour etc. and it all works as expected.

    The problem I'm finding is that the actual spacing of the font is terrible. I get large spaces between certain letters and other letters are cramped. I'm using Hiero for font generation and no amount of tweaking with the padding gets a decent result. Also, unless I use large padding settings (2 on all sides) I get lines around the edges of my fonts. I've tried using BMFont but that gives even worse results.

    I thought it was just a problem with my pipeline but when I looked at the text demo scene I notice that it displays both problems ie. terrible font spacing and white lines around the characters.

    I have spent far too much time banging my head against this issue. Has anyone managed to get a decent result with text using Prime31's UI solution and if so, could they give me any tips on font generation?

    I'm using a PC with windows 7.
     
  33. actuallystarky

    actuallystarky

    Joined:
    Jul 12, 2010
    Posts:
    183
    This image demonstrates the issues I'm finding. The top text is a GUIText. The middle is OnGUI. The bottom is using the Prime31 solution using a Hiero-generated bitmap with padding a 1 on all sides, no effects. Note the bad spacing and the garbage pixels between letters.
     

    Attached Files:

  34. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,427
    @starky, is your atlas set to be overridden as a truecolor? That would account for the odd pixels. The spacing is almost definitely coming from Hiero. It gets fussy sometimes. I know it's a paid solution but http://glyphdesigner.71squared.com/ is well supported and by far the most modern and useable of the font generators. It may be worth a try.
     
  35. actuallystarky

    actuallystarky

    Joined:
    Jul 12, 2010
    Posts:
    183
    I've looked at Glyph designer and I was impressed. The only drawback is I do 99% of my development on my PC and that's a Mac-only solution. Strangely there seems to be no equivalent for us in Windows land. I may buy it anyway but I am loathe to bring yet another step into my font pipeline. I guess if I went that route I wouldn't have to flip the image in photoshop anymore.

    Thanks Prime.
     
  36. actuallystarky

    actuallystarky

    Joined:
    Jul 12, 2010
    Posts:
    183
    @Prime

    Sorry, got another one for you. I'm currently using a small black sprite stretched out across the screen for fades. This works really well except for odd sorting problems. The best example of this is a text instance where some of the letters will appear underneath the fade as expected but other letters will be completely invisible until the fade object is completely transparent and then they suddenly appear. This happens with other objects as well seemingly at random, even getting different results every time I load the game.

    I have the fade sprite at depth 0 and the text instance at depth 20. I'm pretty sure it can't be depth settings anyway as I'm getting different results within the one text instance object.

    Any ideas? Would separating out the fade sprite onto a separate UIToolkit help?
     
  37. tvle83

    tvle83

    Joined:
    Apr 16, 2011
    Posts:
    14
    I am trying to set up a slider that moves up and down and on a click event it stops and based on where the slider is located when it stops a score is given. Can someone point me in the right direction on how to accomplish this? I was looking through the code and it seems like I would have to add a click event to the slider class?

    To better illustrate my idea it is similar to those flash games that use a cannon and a meter moves up and down and the higher it is when you click the more powerful it is, but it is moving back and forth and you have to time it right for it to have maximum power.

    Thank you
     
  38. blastone

    blastone

    Joined:
    Apr 7, 2009
    Posts:
    168
    @Prime31 Thanks for creating such a great free resource. Its great.

    I have a couple of queries, as I am planning on using it as a sprite as well as a GUI tool.

    1) Is it possible to add callbacks on animation states?

    2) When using texturepacker for animations, if I use the crop or the trim options to compack my sprites, the playback is "jiggly" but if I just pack the full size sprites on a grid its perfect. Does the animation handler ignore the sourceSize stuff? Or is there a way to reposition the various sized sprites based on the texturepacker offsets.

    Thanks for the help and feedback.
     
  39. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,427
    @starky, it sounds like your fade mask is setup properly with regard to zIndex. It sounds like you may have found a Unity bug. I'll see if I can reproduce it when I get a few spare minutes.

    @tvle, every available control has events and there are animation methods built in. Have a look at the demo scenes for examples.

    @blastone, the animation class would definitely benefit from some Action callbacks in there. Currently you can yield on callit it to know when it is complete. The quads for animation and their UVs don't get repositioned between frames which is why the sprites need to be the same size.
     
  40. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    24,929
    Should increase this to 12 at least, or more, because any game using it will crash on ipads hard with 6 fingers and probably escape detection as its not something people will usually test, but can occur in the hands of end-users. 11 touches is the max from ios now.

    Or put an error check in, quoted for notice, Alexey managed to find this, and I never would have. I was prepared to try drastic measures...!
     
  41. pikpiak

    pikpiak

    Joined:
    Jun 6, 2010
    Posts:
    13
    Hi prime, are there any methods to disable the Joystick created?
    I can turn it on with a bool statement, but it stays there even after bool is false.
    Searched UIJoystick.cs and couldn't find a function that moves the joystick ( hide it out of screen ) or clears it.

    Hope you can help me, thanks.
     
  42. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,427
    @pikpiak, all UISprites and their subclasses have the following method to hide/show them:

    public virtual bool hidden
     
  43. pikpiak

    pikpiak

    Joined:
    Jun 6, 2010
    Posts:
    13
    Ok, I tried "myJoystick._joystickSprite.hidden" and it works only after i changed the _joystickSprite var in UIJoystick.cs to public instead of private.

    Now the joystick disappears but the joystick background stays.

    Is there a better method to turn them both off 1 shot? Pardon my weak C# skills.
     
  44. raptoplob

    raptoplob

    Joined:
    May 24, 2011
    Posts:
    12
    Is there a way to group components that isn't horizontal or vertical layouts? I'm drawing out some text and images in a table style format and they are then hard to remove/hide.

    Which gets me to my next question, in terms of performance is it better to create once and hide/reveal when needed, or create and destroy the UI elements everytime?

    Thanks again!
     
  45. raptoplob

    raptoplob

    Joined:
    May 24, 2011
    Posts:
    12
    Also, has drag and drop been created yet? If not how would be the best way to implement it?

    I've seen the onTouchBegan and onTouchEnded functions on UIButton, but they kick out an error saying:

    "BCE0031: Language feature not implemented: InPlaceAddition."

    Any thoughts?
     
    Last edited: Jun 15, 2011
  46. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,427
    @raptoblob, you can create your own custom layout containers pretty easily. Just use the existing classes as a template. Hide/show will probably be a tiny bit more performant. All verts created are reused so it will be a close race.
     
  47. reissgrant

    reissgrant

    Joined:
    Aug 20, 2009
    Posts:
    726
    Prime, thanks for creating this! This is truly a great asset!

    I've added a reverse progress bar (if you want the progress bar to move the opposite way).




    *EDIT sent a pull request on github.
     
    Last edited: Jun 15, 2011
  48. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,427
    @reissgrant, can you send a pull request over with the change on Github so I can merge it into the mainline?
     
  49. reissgrant

    reissgrant

    Joined:
    Aug 20, 2009
    Posts:
    726
    Sure, let me learn github real quick ;)
     
  50. prime31

    prime31

    Joined:
    Oct 9, 2008
    Posts:
    6,427
    @rissgrant, it is a great joy to work with. Once you go Github you never go back :) If you are on a Mac, grab the free version of Gitbox which makes working with Git a snap.