# XNA Level Select Menu

I'll try to explain this the best I can. I'm trying to create a level select menu for a game I'm making. The menu is basically a group of blocks numbered 1-16, similar to something like the Angry Birds menu. What I've done is created a cursor, basically just an outline to surround a block, that the user can move to select what level they want to play. What I want it do is move from block to block instead of simply moving around on the X and Y axes as it does now.

So my question is, how can I get the cursor (highLight in the below code) to move from block to block(destinationRectangle1 etc. in the code)?

        /// Field for the "cursor"

Vector2 highLightPos = new Vector2(400, 200);

///This is the Update code

KeyboardState keyBoardState = Keyboard.GetState();
if (keyBoardState.IsKeyDown(Keys.Up))
highLightPos.Y--;
if (keyBoardState.IsKeyDown(Keys.Down))
highLightPos.Y++;
if (keyBoardState.IsKeyDown(Keys.Right))
highLightPos.X++;
if (keyBoardState.IsKeyDown(Keys.Left))
highLightPos.X--;

/// This is the draw code

SpriteBatch spriteBatch = ScreenManager.SpriteBatch;

Rectangle screenRectangle = new Rectangle(0, 0, 1280, 720);

Rectangle destinationRectangle1 = new Rectangle(400, 200, 64, 64);
Rectangle frameRectangle1 = new Rectangle(0, 0, 64, 64);

Rectangle destinationRectangle2 = new Rectangle(500, 200, 64, 64);
Rectangle frameRectangle2 = new Rectangle(64, 0, 64, 64);

Rectangle destinationRectangle3 = new Rectangle(600, 200, 64, 64);
Rectangle frameRectangle3 = new Rectangle(128, 0, 64, 64);

Rectangle destinationRectangle4 = new Rectangle(700, 200, 64, 64);
Rectangle frameRectangle4 = new Rectangle(192, 0, 64, 64);

Rectangle destinationRectangle5 = new Rectangle(800, 200, 64, 64);
Rectangle frameRectangle5 = new Rectangle(256, 0, 64, 64);

Rectangle destinationRectangle6 = new Rectangle(400, 300, 64, 64);
Rectangle frameRectangle6 = new Rectangle(320, 0, 64, 64);

Rectangle destinationRectangle7 = new Rectangle(500, 300, 64, 64);
Rectangle frameRectangle7 = new Rectangle(384, 0, 64, 64);

Rectangle destinationRectangle8 = new Rectangle(600, 300, 64, 64);
Rectangle frameRectangle8 = new Rectangle(448, 0, 64, 64);

Rectangle destinationRectangle9 = new Rectangle(700, 300, 64, 64);
Rectangle frameRectangle9 = new Rectangle(0, 64, 64, 64);

Rectangle destinationRectangle10 = new Rectangle(800, 300, 64, 64);
Rectangle frameRectangle10 = new Rectangle(64, 64, 64, 64);

Rectangle destinationRectangle11 = new Rectangle(400, 400, 64, 64);
Rectangle frameRectangle11 = new Rectangle(128, 64, 64, 64);

Rectangle destinationRectangle12 = new Rectangle(500, 400, 64, 64);
Rectangle frameRectangle12 = new Rectangle(192, 64, 64, 64);

Rectangle destinationRectangle13 = new Rectangle(600, 400, 64, 64);
Rectangle frameRectangle13 = new Rectangle(256, 64, 64, 64);

Rectangle destinationRectangle14 = new Rectangle(700, 400, 64, 64);
Rectangle frameRectangle14 = new Rectangle(320, 64, 64, 64);

Rectangle destinationRectangle15 = new Rectangle(800, 400, 64, 64);
Rectangle frameRectangle15 = new Rectangle(384, 64, 64, 64);

Rectangle destinationRectangle16 = new Rectangle(600, 500, 64, 64);
Rectangle frameRectangle16 = new Rectangle(448, 64, 64, 64);

spriteBatch.Begin();
spriteBatch.Draw(forestBG, screenRectangle, Color.White);
spriteBatch.Draw(highLight, highLightPos, Color.White);
spriteBatch.Draw(levelSelectTiles, destinationRectangle1, frameRectangle1, Color.White);
spriteBatch.Draw(levelSelectTiles, destinationRectangle2, frameRectangle2, Color.White);
spriteBatch.Draw(levelSelectTiles, destinationRectangle3, frameRectangle3, Color.White);
spriteBatch.Draw(levelSelectTiles, destinationRectangle4, frameRectangle4, Color.White);
spriteBatch.Draw(levelSelectTiles, destinationRectangle5, frameRectangle5, Color.White);
spriteBatch.Draw(levelSelectTiles, destinationRectangle6, frameRectangle6, Color.White);
spriteBatch.Draw(levelSelectTiles, destinationRectangle7, frameRectangle7, Color.White);
spriteBatch.Draw(levelSelectTiles, destinationRectangle8, frameRectangle8, Color.White);
spriteBatch.Draw(levelSelectTiles, destinationRectangle9, frameRectangle9, Color.White);
spriteBatch.Draw(levelSelectTiles, destinationRectangle10, frameRectangle10, Color.White);
spriteBatch.Draw(levelSelectTiles, destinationRectangle11, frameRectangle11, Color.White);
spriteBatch.Draw(levelSelectTiles, destinationRectangle12, frameRectangle12, Color.White);
spriteBatch.Draw(levelSelectTiles, destinationRectangle13, frameRectangle13, Color.White);
spriteBatch.Draw(levelSelectTiles, destinationRectangle14, frameRectangle14, Color.White);
spriteBatch.Draw(levelSelectTiles, destinationRectangle15, frameRectangle15, Color.White);
spriteBatch.Draw(levelSelectTiles, destinationRectangle16, frameRectangle16, Color.White);
spriteBatch.End();


PS, I'm aware that this code is probably inefficient, cumbersome or that there's a better way to draw parts of a tile sheet. Any suggestions would be appreciated.

There are many solutions to this, but my favorite is to make an array of positions for these rectangles. For 16 levels, it would be a 4x4 array. Then have a variable representing which element of the array is "selected", and a variable representing the current position of the highlighter.

Now in your update function, always move the highlighter x% of the way toward the selected rectangle. Say, 5% to start, then adjust to get the desired speed.

This creates a smooth movement, which is always a nice effect, and it prevents glitches if the user moves the selection a second time before the highlighter reaches the first destination.

This also makes the controls easy, as when the player hits an arrow key you can just add or subtract one to the "selected" variable's x or y component.

This is just a theoretical explanation, but I hope it helps.

• Thanks for the answer but arrays are currently beyond my abilities. – user29901 Jun 24 '13 at 13:36

I would put both Rectangles into a List(s):

These are class level! Do not make inside your Draw method... scary...

private List<Rectangle> destinationRect = new List<Rectangle>();
private List<Rectangle> frameRect = new List<Rectangle>();

destinationRect.add(new Rectangle(400,200,64,64)); // 1
frameRect.add(new Rectangle(0,0,64,64)); // 1

// do for rest //


Now [class level again] add these Color fields:

Color RegularColor = Color.White;
Color HighlightedColor = Color.Tomatoe;


A 'pointer' field:

private Vector2 _elementPointer = Vector2.Zero;
private int _pointerAsIndex = 0;


Now in your update method:

KeyboardState keyBoardState = Keyboard.GetState();

var moveAmount = Vector2.Zero;

if (keyBoardState.IsKeyDown(Keys.Up))
moveAmount.Y = -1;
if (keyBoardState.IsKeyDown(Keys.Down))
moveAmount.Y = 1;
if (keyBoardState.IsKeyDown(Keys.Right))
moveAmount.X = 1;
if (keyBoardState.IsKeyDown(Keys.Left))
moveAmount.X = -1;

var futureLevelSelectPos = _elementPointer + moveAmount;

if (InBounds((int)futureLevelSelectPos.X, (int)futureLevelSelectPos.Y))
{
_elementPointer = futureLevelSelectPos;
_pointerAsIndex = (int)(_elementPointer.Y * 4 + _elementPointer.X);
}


A helper method:

private static bool InBounds(int x, int y)
{
return x >= 0 && x < 4 &&
y >= 0 && y < 4;
}


Back to Drawing:

spriteBatch.Begin();

for (var i = 0; i < destinationRect.Count; i++)
{
if (i != _pointerAsIndex)
spriteBatch.Draw(levelSelectTiles, destinationRect[i], frameRect[i], RegularColor);
else
spriteBatch.Draw(levelSelectTiles, destinationRect[i], frameRect[i], HighlightedColor);
}


This will work best visually with a 4 x 4 grid so play around with the numbers and bounds checking to get the effect you want.

• Thanks for the answer. I'm getting a few errors, futureLevelSelect does not exist in the current context, _elementPointer.Y * 4 + _elementPointer.X; Cannot implicitly convert type float to int, an explicit conversion exists(are you missing a cast?) and destinationRect.Length; i++ Error 3 'System.Collections.Generic.List<Microsoft.Xna.Framework.Rectangle>' does not contain a definition for 'Length' and no extension method 'Length' accepting a first argument of type 'System.Collections.Generic.List<Microsoft.>' could be found (are you missing a using directive or an assembly reference?). – user29901 Jun 23 '13 at 23:03
• Sorry to reply twice, I'm pretty new to programming, not sure how to fix these errors. – user29901 Jun 23 '13 at 23:04
• if (InBounds(futureLevelSelectPos)) is giving me the error No overload for method InBounds takes 1 arguments. All of the other errors are resolved though, thanks for the edit. – user29901 Jun 24 '13 at 0:05