Roy Triesscheijn’s Weblog

My programming world

Archive for March, 2010

Immediate GUIs in XNA: Scrollbars

Posted by Roy Triesscheijn on 30th March 2010

Introduction

Last time we’ve made a small IMGUI framework, now we are going to add scrollbars, or sliders if you prefer that term, to our set of controls. Sliders work a bit differently than buttons because you’ll have to know last time’s value to show the slider correctly, and we can’t store state for each slider in the IMGUI itself (or well, that goes against the idea).

Setup

Load last time’s project (see part one here)

Without further ado (and because I’m pretty busy lately) I’ll put the source code for a scrollbar here, and afterwards explain the method.

        /// <summary>
        /// Draws a vertical or horizontal scrollbar at the specified position. If the grip was moved, returns the new value, else returns the inserted value
        /// </summary>
        public float DoScrollbar(int id, Rectangle rectangle, Texture2D scrollbarTexture, Texture2D gripTexture, float max, float value, bool horizontal)
        {
            //No matter the input, value should be at least 0 and at most max
            value = Math.Min(Math.Max(value, 0), max);

            //Determine if we're hot, and maybe even active
            if (MouseHit(rectangle)) //See previous part's code for this method
            {
                hotItem = id;
                if (activeItem == -1 && mouse.LeftButton == ButtonState.Pressed)
                    activeItem = id;
            }

            //Draw the scrollbar
            spriteBatch.Draw(scrollbarTexture, rectangle, ActiveColor);

            //Position the grip relative on the scrollbar and make sure the grip stays inside the scrollbar
            //Note that the grip's width if vertical/height if horizontal is the scrollbar's smallest dimension.
            int gripPosition; Rectangle grip;
            if (horizontal)
            {
                gripPosition = rectangle.Left + (int)((rectangle.Width - rectangle.Height) * (value / max));
                grip = new Rectangle(gripPosition, rectangle.Top, rectangle.Height, rectangle.Height);
            }
            else
            {
                gripPosition = rectangle.Bottom - rectangle.Width - (int)((rectangle.Height - rectangle.Width) * (value / max));
                grip = new Rectangle(rectangle.Left, gripPosition, rectangle.Width, rectangle.Width);
            }

            //Draw the grip in the correct color
            if (activeItem == id || hotItem == id)
            {
                spriteBatch.Draw(gripTexture, grip, ActiveColor);
            }
            else
            {
                spriteBatch.Draw(gripTexture, grip, InActiveColor);
            }

            //If we're active, calculate the new value and do some bookkeeping to make sure the mouse and grip are in sync
            if (activeItem == id)
            {
                if (horizontal)
                {
                    //Because the grip's position is defined in the top left corner
                    //we need to shrink the movable domain slightly so our grip doesnt
                    //draw outside the scrollbar, while still getting a full range.
                    float mouseRelative = mouse.X - (rectangle.X + grip.Width / 2);
                    mouseRelative = Math.Min(mouseRelative, rectangle.Width - grip.Width);
                    mouseRelative = Math.Max(0, mouseRelative);
                    //We then calculate the relative mouse offset 0 if the mouse is at
                    //the left end or more to the left. 1 if the mouse is at the right end
                    //or more to the right. We then multiply this by max to get our new value.
                    value = (mouseRelative / (rectangle.Width - grip.Width)) * max;
                }
                else
                {
                    //same as horizontal bit in the end we inverse value,
                    //because we want the bottom to be 0 instead of the top
                    //while in y coordinates the top is 0.
                    float mouseRelative = mouse.Y - (rectangle.Y + grip.Height / 2);
                    mouseRelative = Math.Min(mouseRelative, rectangle.Height - grip.Height);
                    mouseRelative = Math.Max(0, mouseRelative);
                    value = max - (mouseRelative / (rectangle.Height - grip.Height)) * max;
                }
            }
            return value;
        }

As you can see a scrollbar is a bit more complicated than a button, but the code is still pretty short. I think the code is pretty self explanatory, but I’ll add a short overview of the steps here:
-A scrollbar is called with as parameter a float called value, this value indicates where on the scrollbar the grip should be (between 0 and max)
-First trim value so that it is between 0 and max
-Determine if the mouse is over the scrollbar, and if so check if the mouse is being pressed
-Draw our scrollbar background in the right color
-Now calculate where our grip should be positioned and how big it should be (two code paths, one for horizontal and one for vertical)
-If we have a scrollbar background of 60×500 we want our grip to be of size 60×60, and vice-versa
-If we have a value near max we want the grip to be more to the right (or top) and vice-versa
-Now draw our grip
-Last, if the scrollbar is the activeItem, we need to check if the mouse (and thus the grip) was moved (again two code paths).
-This is slightly tricky so take a good look at that code.
-In the end, return the value (which was changed if the grip was moved).

Well, now we have that nice scrollbar, how are we going to use it?

        private IMGUI imgui; //construct/load these fields somewhere
        private Texture2D backgroundTexture;
        private Texture2D gripTexture;
        private float scrollvalue = 0;

        public void DrawImGui()
        {
            imgui.Update(gameTime); //normally you would do this in the Update method of your screen
                                              //but for code-compactness reasons in tutorials, I'll just add it here

            imgui.Begin();

            scrollvalue = imgui.DoScrollbar(1, new Rectangle(10, 10, 300, 50), backgroundTexture, gripTexture, 100, scrollvalue, true);
            //More GUI items

            imgui.End();
        }

(Note the call to imgui.Update(…) which I might have forgotten last time (oops).

Easy isn’t it? A simmilar technique is used for text input, but you’d need an extra state to keep track of the item the mouse last clicked that receives keyboard input, I’ll go over that next time. I would add a nice picture here to show you how this all looks, but tbh my programmer art is so ugly that I’m scared it would frighten you away from using this technique.

kick it on GameDevKicks.com

Tags: , , , ,
Posted in Blog, General Gamedesign, XNA | 1 Comment »

End of march update

Posted by Roy Triesscheijn on 25th March 2010

As you might have noticed there where not so much updates as usual this month. Me and a few classmates have been very busy working on a project for Q-Free Netherlands, a company that works on automatic license plate recognition software. Unfortunately we don’t get to work on the core product, but until I see and sign the non disclosure agreement I can’t really say what I’m working. (Nothing so exciting that it should require a NDA to be signed, but it’s standard practice in most industries). The assignment is part of the International Software Engineering Project, and my team works together with three Swedish students via video conferencing. The teachers have set up a very nice TRAC environment and I’m really starting to like TRAC, altough it is a very spartan environment.

Other than that I’ve worked a tiny bit on Space Ace, but I’ve got nothing new to show. I’ve drawn out some ideas with the help of my roommates which have been stuck in my head since I began working this project.

I hope to be able to finish my Immediate GUI tutorial next week, or the week after, so stay tuned for that!

Tags: , ,
Posted in Blog | No Comments »

Immediate GUIs in XNA: Setup and a button

Posted by Roy Triesscheijn on 10th March 2010

Introduction

Part 2 (Scrollbars) is located here

Immediate GUI’s are a pretty new concept to creating and drawing GUIs. One of the big differences between an Immediate (IM) GUI and a normal GUI is that an IMGUI is non persistent. Every frame a manager determines which screens/buttons/widgets have to be drawn and only these are drawn. There are no Button objects, Form objects or objects at all. GUI items are immediately drawn by calling a method. Most of these methods are prefixed by ‘Do’ to show the immediate nature of the button. Because of this, IMGUIs have a very low memory profile. However some objects are created every frame for drawing purposes and these might go havoc with the garbage collector if you don’t watch them properly (especially on the Xbox).

Because no objects are used, we have to use a special trick to keep track of ‘events’. To accompany this, every GUI item that can have a certain state, or can return something (for example a button, for which we want to know if it was clicked) is given a unique id (usually an integer).  The IMGUI manager holds a couple of variables to determine what item was clicked, what item the mouse was over and what item is receiving keyboard input. So the state literally consists of 3 integers, instead of a great series of objects.

Let’s create a framework for our own IMGUI in XNA.

Setup

Fire up a new XNA Windows Game project and create a new class called IMGUI. Add the following using statements:

using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

After that add the following fields:

private SpriteBatch spriteBatch;
private MouseState mouse;
private int hotItem = -1;
private int activeItem = -1;

We need a SpriteBatch to draw our GUI. The hotItem will store the ID of the item where the mouse is over. The activeItem will store the item being clicked. We will also store the mouse state so we don’t have to get the mouse state in every GUI item.

Let’s add an update method to automatically update our mouse.

public void Update(GameTime gameTime)
{
    mouse = Mouse.GetState();
}

Nothing fancy here, but this will keep track of the mouse.

We want our IMGUI to be selfcontained but we still need to begin and end our spriteBatch. We also need to clear the hotItem (the item where the mouse is over) before beginning. And at the end we need to check if the mouse is still pressed, if not we need to store in activeItem that no item is being clicked. To accommodate this, we will make a Begin() and End() methods, not unlike those of the normal spriteBatch.

public void Begin()
{
        //Clear hot items before we begin.
        hotItem = -1;
        spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.SaveState);
}

public void End()
{
    spriteBatch.End();
    //If the user isn't clicking anything anymore, unset the active item.
    if (mouse.LeftButton == ButtonState.Released)
    {
        activeItem = -1;
    }
}

It might still seem a bit odd why we have to do this. But when we create our first GUI item, it will make a lot more sense. So let’s get right on it!

/// <summary>
/// Draws a standard button with specified texture at the location of the rectangle
/// and returns if the button was clicked.
/// </summary>
/// <param name="id">Unique ID</param>
/// <param name="rectangle">Rectangle specifying the position of the button</param>
/// <param name="texture">Texture for the button</param>
/// <returns>True if button was clicked, otherwise False</returns>
public bool DoButton(int id, Rectangle rectangle, Texture2D texture)
{
      //Determine if we're hot, and maybe even active
      if (MouseHit(rectangle))
      {
      	hotItem = id;
            if (activeItem == -1 && mouse.LeftButton == ButtonState.Pressed)
                    activeItem = id;
      }

      //Draw the button
      Color drawColor;
      if (hotItem == id)
      {
      	if (activeItem == id)
            {
            	drawColor = Color.White;
            }
            else
            {
                 drawColor = Color.LightGray;
            }
      }
      else
      {
            drawColor = Color.Gray;
      }
            spriteBatch.Draw(texture, rectangle, drawColor);

      //If we are hot and active but the mouse is no longer down, we where clicked
      //Line updated after comment by anonymous user, I feel so silly now!
      return mouse.LeftButton == ButtonState.Released && hotItem == id && activeItem == id
}
//Small helper function that checks if the mouse is contained in the rectangle
//Might even be unneeded but I like not having to manually write ‘new Point…’ every time.
private bool MouseHit(Rectangle rectangle)
{
      return rectangle.Contains(new Point(mouse.X, mouse.Y))
}

As you can see this is not much code for a button, because we got rid of all the state information and just bother ourselves with drawing a button, we don’t need to do much at all. Let’s look at what we have here.  First we determine if the mouse is over our button. If that is the case we are ‘hot’. We set the hotItem to our id, we also check if no item is active, and if the mouse’s left button is pressed. If so we set ourselves to be the activeItem as well.

We then start to draw our button (nothing fancy, we just change the tint we apply to our texture, and then draw the texture at the specified place).

At the end of our method we check if the mouse was released. If the mouse released and we where the hot and active item (the mouse is over our button and the mouse was pressed when over our button) then we return true to signal that we where clicked, else we return false. Because we store those nifty hot and active item integers this is all we need to determine if we were pressed.

Using the button and the IMGUI would look something like this:

        IMGUI imgui = new IMGUI(spriteBatch);
        private Texture2D buttonTexture;
        public void DrawImGui()
        {
            imgui.Update(gameTime); //normally you would do this in the Update method of your screen
                                              //but for code-compactness reasons in tutorials, I'll just add it here
            imgui.Begin();

            if (imgui.DoButton(1, new Rectangle(0, 0, 64, 64), buttonTexture))
            {
                Console.Out.WriteLine("The button with id: 1 was pressed");
            }

            //More GUI items

            imgui.End();
        }

Easy isnt it? Just make sure that you keep your ids unique, and that you give the same button the same id every frame. Of course after a button is no longer used, you can reuse the id (make sure though that you wait at least one frame, or to check that the hotItem and activeItem where not set to the id you are going to reuses, else some false clicks might occur, but generally this shouldn’t be a problem).

If there is more animo I’ll write a following part somewhere next week. Here we are going tot tackle scrollbars and textfields, which are slightly more interesting because they require their own output to be inputted next frame.

Oh btw, one of the biggest sources on immediate GUIs is the website mollyrocket.com IMGUI video I’ve directly linked the one hour long video. Skip the first few minutes about “why this is a video tutorial, because he doesn’t have much time etc..” and delve straight into the interesting part!

Update 30-03-2010 added a call to imgui.Update(..) which I forgot and only saw in the next installment in this series.

kick it on GameDevKicks.com

Tags: , , , , , ,
Posted in Blog, General Gamedesign, XNA | 6 Comments »

Getting the Left, Forward and Back vectors from a View Matrix directly

Posted by Roy Triesscheijn on 4th March 2010

I was wondering why I had to calculate the forward and left vectors for my arcball camera manually and why these results differed from ViewMatrix.Left and the likes.  So I asked at the xna forums and almost immediately Jeremy Walsh pointed me in the right direction.  He pointed out to me that view matrices are transposed from a normal matrix (meaning that the rows and columns are switched). To get the right vectors from the view matrix, we have to transpose it again to get the original matrix, however this generates a lot of garbage, so he told me that its better to construct the vectors from the matrix cells themselves.

And so I did, and I’ve packaged them into my neat PositionalMath class (which I might release some day). Here are the methods to get all the information you want from those view matrices, without having to calculate the forward (lookat – position) and crossing that.

// Because a ViewMatrix is an inverse transposed matrix, viewMatrix.Left is not the real left
        // These methods returns the real .Left, .Right, .Up, .Down, .Forward, .Backward
        // See: http://forums.xna.com/forums/t/48799.aspx        

        public static Vector3 ViewMatrixLeft(Matrix viewMatrix)
        {
            return -ViewMatrixRight(viewMatrix);
        }

        public static Vector3 ViewMatrixRight(Matrix viewMatrix)
        {
            return new Vector3(viewMatrix.M11, viewMatrix.M21, viewMatrix.M31);
        }

        public static Vector3 ViewMatrixUp(Matrix viewMatrix)
        {
            return new Vector3(viewMatrix.M12, viewMatrix.M22, viewMatrix.M33);
        }

        public static Vector3 ViewMatrixDown(Matrix viewMatrix)
        {
            return -ViewMatrixUp(viewMatrix);
        }

        public static Vector3 ViewMatrixForward(Matrix viewMatrix)
        {
            return -ViewMatrixBackward(viewMatrix);
        }

        public static Vector3 ViewMatrixBackward(Matrix viewMatrix)
        {
            return new Vector3(viewMatrix.M13, viewMatrix.M23, viewMatrix.M33);
        }

Tags: , , , , , ,
Posted in Blog | No Comments »