Creating a progress bar for game content loading and value status
When playing a game, especially for some big games, at the initialization phase, a progress bar will show you the game object loading status and percents. As a time-based game, the progress bar represents the remaining time. Moreover, a Role Playing Game (RPG) often uses the progress bar to present the current life value. A progress bar is a very common control in game development and is easy to use. In this recipe, you will find the inner code for creating a progress bar.
Getting ready
In Windows Phone 7 XNA programming, two methods will let you create the progress bar. One is using the rectangle for drawing the background and forefront. This is simple but not flexible and stylish. If you want to make some innovative and unusual visual effects, the primitives will not meet your needs. The second method will give you much more space to realize your idea for the progress bar. You can use graphic design tool to draw the background and forefront images as you like, then render these images and change the size of the forefront image to comply with the on going percents; even the round or other shapes can be used for presenting the progress status. In this example, we will use the rectangle images (second method) for implementing the progress bar in Windows Phone 7.
How to do it…
The following steps give you a complete guidance to develop a progress bar in your Windows Phone 7 game:
- Create a Windows Phone Game project in Visual Studio 2010 named ProgressBar, change Game1.cs to ProgressBarGame.cs and insert a ProgressBar.cs in the project. Then add ProgressBarBackground.png and ProgressBarForefront. png to the content project.
- Add a ProgressBar class in ProgressBar.cs to the main project. Add the code to the ProgressBar class fields:
[code]
// SpriteBatch for drawing 2D image
SpriteBatch spriteBatch;
// ProgressBar forefront and background images
Texture2D texForefront;
Texture2D texBackground;
// The background and forefront positon
Vector2 backgroundPosition;
Vector2 forefrontPosition;
// The offset of forefront image from the background.
float forefrontStartOffSetX;
float forefrontStartOffSetY;
// Current value of progressbar
public int Value;
// The Min and Max values of progressbar
public int Min;
public int Max;
// Percent of current value around 100
float percent;
// the actual rendering width of forefront image
float actualWidth;
// The direction of progress.
bool increase = false;
[/code] - Next, we define the Increase property:
[code]
// The increasing direction
public bool Increase
{
get
{
return increase;
}
set
{
// When increasing, the Value begins from Min
if (value)
{
increase = value;
Value = Min;
}
// When decreasing, the Value begins from Max
else
{
increase = value;
Value = Max;
}
}
}
[/code] - The next step is to define the constructor of the ProgressBar class:
[code]
public ProgressBar(Vector2 position, Texture2D forefront,
Texture2D background, SpriteBatch spriteBatch)
{
this.spriteBatch = spriteBatch;
texForefront = forefront;
texBackground = background;
backgroundPosition = position;
// Calculate the offset for forefront image
forefrontStartOffSetX = (texBackground.Width –
texForefront.Width) / 2;
forefrontStartOffSetY = (texBackground.Height –
texForefront.Height) / 2;
// Create the forefront image position
forefrontPosition = new Vector2(backgroundPosition.X +
forefrontStartOffSetX,
backgroundPosition.Y + forefrontStartOffSetY);
// Intitialize the Min and Max
Min = 0;
Max = 100;
// Set the increasing direction from high to low.
Increase = false;
}
[/code] - After the constructor, the following method definition is the Update(), so add the method to the ProgressBar class:
[code]
public void Update(GameTime gameTime)
{
// If decreasing and Value greater than Min, minus the
// Value by one
if (Increase && Value < Max)
{
Value++;
}
else if (Value > Min)
{
Value–;
}
// Compute the actual forefront image for drawing
percent = (float)Value / 100;
actualWidth = percent * texForefront.Width;
}
[/code] - The final step of creating the ProgressBar class is to define the Draw() method:
[code]
public void Draw()
{
spriteBatch.Draw(texBackground, backgroundPosition,
Color.White);
spriteBatch.Draw(texForefront, forefrontPosition, new
Rectangle(0, 0, (int)actualWidth,
texForefront.Height), Color.White);
}
[/code] - Use the ProgressBar class in our game. First, add the code to the class field:
[code]
// Texture objects for background and forefront images
Texture2D texForefront;
Texture2D texBackground;
// The background image position
Vector2 position;
// Progress bar object
ProgressBar progressBar;
[/code] - Then insert the initialization code to the LoadContent() method:
[code]
// Load the background and forefront images
texForefront =
Content.Load<Texture2D>(“ProgressBarForefront”);
texBackground =
Content.Load<Texture2D>(“ProgressBarBackground”);
// Initialize the progress bar
position = new Vector2(200, 240);
progressBar = new ProgressBar(position, texForefront,
texBackground, spriteBatch);
[/code] - Next, insert the code to the Update() method:
[code]
// Update the progress bar
progressBar.Update(gameTime);
[/code] - [code]
// draw the progress bar
spriteBatch.Begin();
progressBar.Draw();
spriteBatch.End();
[/code] - Now, build and run the application, and it will run as shown in the following screenshots:
How it works…
In step 2, the texForefront and texBackground are the Texture2D objects that hold the progressBar forefront and background images. The next two variables forefrontStartOffSetX and forefrontStartOffSetY indicate the offset position of forefront from the background; Value stores the progressBar current value; the Min and Max defines the range of the progressBar; percent and actualWidth will be used to calculate and store the current width of the forefront image respectively; the last variable increase represents the direction of the progressBar value increasing.
In step 3, if the Increase value is false, which means it is decreasing, the Value begins from the right with Max. Otherwise, the Value will begin from the left.
In step 4, notice the computation for forefront image offset, we use the background image width minus the width of the forefront image, get the gap between the left sides of the two images, then use the gap value and divide it by 2, get the offset on the X-axis from the background for the forefront image. The offset on the Y-axis is similar. After getting the offset of the forefront image, we set Min to 0 and Max to 100—the value range of progressBar. The last line is to define the increasing direction. False, here, stands for the progress value that will decrease from 100 to 0, right to left.
In step 5, the first part of the Update() method is to change Value by one, according to the increasing direction. The second part is about computing the actual width of the forefront image for rendering.
In step 6, this code draws the background image and forefront image on screen. Notice the third parameter in the Drawing() method for the forefront image. This is a Rectangle parameter, which represents the part of the forefront image for rendering in every frame; it helps you to adjust the size of the forefront image for presenting the value variation of the progress bar.
In step 7, the texForefront stands for the forefront image of the progress bar; the texBackground represents the background image of the progress bar; position defines the progress bar position on the Windows Phone 7 screen; the last variable progressBar is the progress bar object which will perform the different progress behaviors.
Creating buttons in your game
In any type of game, button control is always the most basic and important part. In a GUI system, button control often plays a role in linking different parts of other controls. When you input some text in a text control, you click the button to send the message or confirm it as a command. When you are using a listbox, the up and down buttons help you look up special information that you need in the game, such as choosing weapons. In the development phase, programmers can define specific behaviors of the button events to implement the game logic or effects. To implement a button in the Windows Phone 7 XNA framework is not a hard mission. In this recipe, you will learn how to build your own button in Windows Phone 7.
How to do it…
The following steps will show you the working code for creating the buttons for your Windows Phone 7 game:
- Create a Windows Phone Game project named Button, change Game1.cs to ButtonGame.cs. Then add the Button.cs to the main project and button_image.png and gameFont.spriteFont to the content project.
- Create the Button class in the Button.cs file. Add the line to the class as a field:
[code]
// Button texture
Texture2D texButton;
// SpriteBatch for drawing the button image
SpriteBatch spriteBatch;
// Button position on the screen
public Vector2 Position;
// Color alpha value
public int Alpha = 255;
// Button color
Color color;
// Timer for game elapsed time accumulation
float timer;
// The Tapped bool value indicates whether tap in the button
region
public bool Tapped;
// Event handler OnTapped to react with tap gesture
public event EventHandler OnTapped;
[/code] - Then, define the HitRegion property of the Button class:
[code]
// Get the hit region
public Rectangle HitRegion
{
get
{
return new Rectangle((int)Position.X, (int)Position.Y,
texButton.Width, texButton.Height);
}
}
[/code] - Next, give the class constructor Button() of the Button class:
[code]
// Initialize the button without text
public Button(Texture2D texture, Vector2 position, SpriteBatch
spriteBatch)
{
this.texButton = texture;
this.Position = position;
this.spriteBatch = spriteBatch;
color = Color.White;
}
[/code] - After the class constructor, the important Update() method that reacts to the tap gesture looks similar to the following code:
[code]
// Update the button
public void Update(GameTime gameTime, Vector2 touchPosition)
{
// React to the tap gesture
Point point = new Point((int)touchPosition.X,
(int)touchPosition.Y);
// If tapped button, set the Hovered to true and trigger
// the OnClick event
if (HitRegion.Contains(point))
{
Tapped = true;
OnTapped(this, null);
}
else
{
Tapped = false;
}
}
[/code] - The final step to build the Button class is to define the Draw() method:
[code]
// Draw the button
public virtual void Draw(GameTime gameTime)
{
timer += (float)gameTime.ElapsedGameTime.TotalMilliseconds;
// Draw the button texture
if (Tapped)
{
// Flash the button through the alpha value changing
if (timer > 100)
{
// If the Alpha is 255, set it to 0
if (Alpha == 255)
{
Alpha = 0;
}
// If the Alpha value is 0, set it to 255
else if (Alpha == 0)
{
Alpha= 255;
}
// Set the color alpha value
color.A = (byte)Alpha;
// Set the timer to 0 for next frame
timer = 0;
}
// Draw the button image
spriteBatch.Draw(texButton, HitRegion, null, color, 0,
Vector2.Zero, SpriteEffects.None, 0);
}
else
{
spriteBatch.Draw(texButton, HitRegion,
null,Color.White, 0,
Vector2.Zero, SpriteEffects.None, 0);
}
}
[/code] - Use the Button class in our main game class. Insert code to the ButtonGame class field:
[code]
// Sprite Font for showing the text
SpriteFont font;
// Text object
string textTapState = “Random Color Text”;
// Text color;
Color textColor = Color.White;
// Random object for showing the random color
Random random;
// Button object
Button button;
// Button texture;
Texture2D buttonTexture;
[/code] - Initialize the random variable in the Initialize() method, and add the following code to the method:
[code]
random = new Random();
[/code] - Load the button image and initialize the button object. Add the code to the LoadContent() method:
[code]
font = Content.Load<SpriteFont>(“gameFont”);
buttonTexture = Content.Load<Texture2D>(“button_image”);
Vector2 position = new Vector2(
GraphicsDevice.Viewport.Width / 2 – buttonTexture.Width / 2,
GraphicsDevice.Viewport.Height/2 – buttonTexture.Height / 2);
button = new Button(buttonTexture, position, spriteBatch);
button.OnTapped += new EventHandler(button_OnTapped);
[/code] - Next is the reaction method for the button OnTapped event:
[code]
void button_OnTapped(object sender, EventArgs e)
{
textColor.R = (byte)random.Next(0, 256);
textColor.G = (byte)random.Next(0, 256);
textColor.B = (byte)random.Next(0, 256);
}
[/code] - Get the tapped position and pass it to the Button.Update() method, paste the code in to the Update() method:
[code]
TouchCollection touches = TouchPanel.GetState();
if(touches.Count > 0 && touches[0].State ==
TouchLocationState.Pressed)
{
Vector2 tappostion = touches[0].Position;
button.Update(gameTime, tappostion);
}
[/code] - Draw the button on screen, and put the following lines of code to the Draw() method:
[code]
spriteBatch.Begin(SpriteSortMode.Immediate,
BlendState.NonPremultiplied);
button.Draw(gameTime);
spriteBatch.DrawString(font, textTapState, new Vector2(0, 0),
textColor);
spriteBatch.End();
[/code] - Ok, we have done the code work. Build and run the project, and the application should run similar to the following screenshot; when we tap the button, it will flicker and generate a random color for the text on the top-left corner.
How it works…
Steps 2–6 are about creating the Button class.
In step 2, the texButton is the button texture; spriteBatch will render the button texture on screen; Position specifies the location of the button on screen. Alpha represents the alpha value of the button color; timer will be used to accumulate the game elapsed time; bool value tapped will indicate the tapping state of the button; OnTap is the event handler to handle the button tap gesture.
In step 3, the HitRegion property will return the bound surrounding the button for tap validation.
In step 4, the constructor initializes the button texture, position, and color.
In step 5, within the Update() method of the Button class, the code checks the tapped position to see whether it’s inside the button HitRegion. If yes, set Tapped to true and trigger the OnTapped event, else, it will be false.
In step 6, the first line is to accumulate the game elapsed time in milliseconds. The following code draws the button image. If the button is tapped and the elapsed time is greater than 100, it will flicker. The effect is implemented by setting the Alpha value of button color. If the Alpha value equals 255 (Opaque), we set it to 0 (Transparent). Otherwise, the value will be set from 0 to 255. After that, the latest alpha value will be assigned to Color.A , the alpha factor of Color. Then, reset the timer for the next frame. The last line will render the flickering effect on screen.
Steps 7–11 are about using the Button class in the main game class.
In step 7, the font object will render the text on screen; the textTapState stores the text to be displayed; textColor specifies the text color; random will be used to generate a random color for the text; the button variable represents the Button class instance; the buttonTexture loads the button image.
In step 9, the button_OnTapped() method will run if the OnTapped event happens. In the event reaction method, we set the R, G, and B factors of text color randomly, because the RGB value is from 0 to 255, so the random value for each of them must be inside the range.
In step 10, we get the tapped position for the button hit region validation.
In step 11, notice we must set the BlendState.NonPremultiplied because we change the button image Alpha value linearly.
Creating a listbox to speed up your information management in a game
Listbox is a list-style control, which collects the information in the list. For games, list control often plays a role in information management. The items in the listbox are presented one-by-one vertically or horizontally. You can choose the information entry through the control. In this recipe, you will master the technique of building your own listbox.
Getting ready
The example will create a listbox in Windows Phone 7. When you click the scrollbar down or the down button, the listbox will show the list items from the latest index. Once you tap one of the items, the text of the item will be presented at the top-left of the screen. Now, let’s begin with building the Button class.
How to do it…
The following steps will show you the complete process of creating the GUI listbox control:
- Create the Windows Phone Game project named ListBoxControl, change Game1.cs to ListBoxControlGame.cs. Add the Button.cs, ScrollBar. cs, and ListBox.cs files to the main project, and add gameFont.spriteFont, ListBoxBackground.png, ScrollBarDown.png, and ScrollBarUp.png to the content project.
- Create the Button class in Button.cs. First, insert the lines as the field of the Button class:
[code]
// Button texture
Texture2D texButton;
// SpriteBatch for drawing the button image
SpriteBatch spriteBatch;
// Button position on the screen
public Vector2 Position;
// Button color
Color color;
// The Tapped bool value indicates whether tap in the button
region
public bool Tapped;
// Event handler OnTap to react with tap gesture
public event EventHandler OnTapped;
[/code] - The next part is the HitRegion property:
[code]
// Get the hit region
public Rectangle HitRegion
{
get
{
return new Rectangle((int)Position.X, (int)Position.Y,
texButton.Width, texButton.Height);
}
}
[/code] - After the property definition, the constructor will be:
[code]
// Initialize the button without text
public Button(Texture2D texture, Vector2 position, SpriteBatch
spriteBatch)
{
this.texButton = texture;
this.Position = position;
this.spriteBatch = spriteBatch;
color = Color.White;
}
[/code] - Then, we define the Update() method:
[code]
// Update the button
public void Update(GameTime gameTime, Vector2 touchPosition)
{
// React to the tap gesture
Point point = new Point((int)touchPosition.X,
(int)touchPosition.Y);
// If tapped button, set the Hovered to true and trigger
// the OnClick event
if (HitRegion.Contains(point))
{
Tapped = true;
OnTapped(this, null);
}
else
{
Tapped = false;
}
}
[/code] - The final method in the Button class is the Draw() method:
[code]
// Draw the button
public virtual void Draw(GameTime gameTime)
{
// Draw the button texture
if (Tapped)
{
spriteBatch.Draw(texButton, HitRegion, null,
Color.Red, 0,
Vector2.Zero, SpriteEffects.None, 0);
}
else
{
spriteBatch.Draw(texButton, HitRegion,
null,Color.White, 0,
Vector2.Zero, SpriteEffects.None, 0);
}
}
[/code] - Create the ScrollBar class in ScrollBar.cs. As the class field, we use the following code:
[code]
// SpriteBatch for drawing the scrollbar
SpriteBatch spriteBatch;
// ScrollBar up and down buttons
Button scrollUp;
Button scrollDown;
// Textures for scrollbar up and down buttons
Texture2D texScrollUp;
Texture2D texScrollDown;
// The position of scrollbar
public Vector2 Position;
// The positions of scrollbar up and down buttons
public Vector2 scrollUpPosition;
public Vector2 scrollDownPosition;
// Event handler when scrollbar up button tapped
public event EventHandler OnScrollUpTapped;
// Event handler when scrollbar down button tapped
public event EventHandler OnScrollDownTapped;
// The ScrollBar Height and Width
public int ScrollBarHeight;
public int ScrollBarWidth;
[/code] - The following code is the ScrollDownBound and ScrollUpBound property of the ScrollBar class:
[code]
// The Bound of Scrollbar down button
public Rectangle ScrollDownBound
{
get
{
return new Rectangle((int)scrollDownPosition.X,
(int)scrollDownPosition.Y,
(int)texScrollDown.Width,
(int)texScrollDown.Height);
}
}
// The Bound of Scrollbar up button
public Rectangle ScrollUpBound
{
get
{
return new Rectangle((int)scrollUpPosition.X,
(int)scrollUpPosition.Y,
(int)texScrollDown.Width,
(int)texScrollDown.Height);
}
}
[/code] - After the properties, the constructor of the ScrollBar class should be as follows:
[code]
// ScrollBar constructor
public ScrollBar(Vector2 position, int scrollbarHeight,
ContentManager content, SpriteBatch spriteBatch)
{
// Load the textures of scroll bar up and down button
texScrollDown = content.Load<Texture2D>(“ScrollBarDown”);
texScrollUp = content.Load<Texture2D>(“ScrollBarUp”);
Position = position;
this.spriteBatch = spriteBatch;
// Get the scrollbar width and height
this.ScrollBarWidth = texScrollDown.Width;
this.ScrollBarHeight = scrollbarHeight;
// The position of scrollbar up button
this.scrollUpPosition = new Vector2(
Position.X – ScrollBarWidth / 2, Position.Y);
// The position of scrollbar down button
this.scrollDownPosition = new Vector2(
Position.X – ScrollBarWidth / 2,
Position.Y + ScrollBarHeight – texScrollDown.Height);
// Instance the scrollbar up and down buttons
scrollUp = new Button(texScrollUp, scrollUpPosition,
spriteBatch);
scrollDown = new Button(texScrollDown, scrollDownPosition,
spriteBatch);
}
[/code] - Next, we define the Update() method of the Scrollbar class:
[code]
// Scrollbar Update method
public void Update(GameTime gameTime, Vector2 tappedPosition)
{
// Check whether the tapped position is in the bound of
// scrollbar up button
if (ScrollDownBound.Contains((int)tappedPosition.X,
(int)tappedPosition.Y))
{
// If yes, set the Tapped property of scrollbar down
// button to true
scrollDown.Tapped = true;
// Set the Tapped property of scrollbar up button to
// false
scrollUp.Tapped = false;
// Trigger the scrollbar down button event
OnScrollDownTapped(this, null);
}
else if(ScrollUpBound.Contains((int)tappedPosition.X,
(int)tappedPosition.Y))
{
// If yes, set the Tapped property of scrollbar up
// button to true
scrollUp.Tapped = true;
// Set the Tapped property of scrollbar down button to
// false
scrollDown.Tapped = false;
// Trigger the scrollbar up button event
OnScrollUpTapped(this, null);
}
}
[/code] - Then, draw the scrollbar on screen by using the Draw() method:
[code]
// Draw the scrollbar
public void Draw(GameTime gameTime)
{
// Draw the scrollbar down and up buttons
scrollDown.Draw(gameTime);
scrollUp.Draw(gameTime);
}
[/code] - Create the ListBox class in the ListBox.cs file. We add the following code as the class field:
[code]
// Game object holds the listbox
Game game;
// SpriteBatch for drawing listbox
SpriteBatch spriteBatch;
// SpriteFont object for showing the listbox text items
SpriteFont font;
// The listbox background texture
Texture2D texBackground;
// The collection of listbox text items
public List<string> list;
// The position of listbox on screen
public Vector2 Position;
// The count of the listbox text items
public int Count;
// Scrollbar object to control the text items for showing
ScrollBar scrollBar;
// The Index for locating the specified item in listbox
public int Index = 0;
// The bounds of showed items
List<Rectangle> listItemBounds;
// The index of selected items
public int SelectedIndex = 0;
// The selected item
public string SelectedItem = “”;
// The selected area for highlighting the selected item
Texture2D SelectedArea;
// The offset from the position of listbox as the beginning of
// drawing the text items
Vector2 Offset;
// The width and height of listbox
int ListBoxWidth;
int ListBoxHeight;
// The total number of items presenting in listbox
int ShowedItemCount = 0;
[/code] - As properties, the CharacterHeight and Bound look similar to the following:
[code]
// Get the character height o text item
public float CharacterHeight
{
get
{
if (font != null && list.Count > 0)
{
// The Y value represents the character height in
// the returned Vector2 value
// of SpriteFont.MeasureString()
return font.MeasureString(list[0]).Y;
}
else
{
throw new Exception();
}
}
}
// Get the bound of listbox
public Rectangle Bound
{
get
{
return new Rectangle((int)Position.X, (int)Position.Y,
texBackground.Width, texBackground.Height);
}
}
[/code] - The next block of code is the constructor of the ListBox class:
[code]
// Listbox constructor
public ListBox(Vector2 position, ContentManager content,
SpriteBatch
spriteBatch, Game game)
{
this.game = game;
this.spriteBatch = spriteBatch;
listItemBounds = new List<Rectangle>();
list = new List<string>();
Position = position;
font = content.Load<SpriteFont>(“gameFont”);
texBackground =
content.Load<Texture2D>(“ListBoxBackground”);
ListBoxWidth = texBackground.Width;
ListBoxHeight = texBackground.Height;
// Define the scrollbar position relative to the position
// of listbox
Vector2 scrollBarPosition = new Vector2(
Position.X + ListBoxWidth + 40, Position.Y);
// Instance the scrollbar
scrollBar = new ScrollBar(scrollBarPosition, ListBoxHeight,
content, spriteBatch);
scrollBar.OnScrollUpTapped += new
EventHandler(scrollBar_OnScrollUpTapped);
scrollBar.OnScrollDownTapped += new
EventHandler(scrollBar_OnScrollDownTapped);
// Define the offset for drawing the text items
Offset = new Vector2(20, 4);
}
[/code] - Now, we define the reaction method of the tap event of the scrollbar’s up and down buttons:
[code]
// The reaction method of scrollbar down button tapping event
void scrollBar_OnScrollDownTapped(object sender, EventArgs e)
{
// If the current item index plus the ShowedItemCount
// is less
// than count of list items, increase the Index
if (Index + ShowedItemCount < Count)
{
Index++;
}
}
// The reaction method of scrollbar up button tapping event
void scrollBar_OnScrollUpTapped(object sender, EventArgs e)
{
// If the current item index is greater than 0, decrease
the
// Index
if (Index > 0)
{
Index–;
}
}
[/code] - The following important method in the ListBox class is the Update() method:
[code]
// Check the tapping state of scrollbar and the selection of
// listbox items
public void Update(GameTime gameTime, Vector2 tapposition)
{
scrollBar.Update(gameTime, tapposition);
CheckSelected(tapposition);
}
[/code] - The definition of CheckSelected() is as follows:
[code]
// Get the selected index and item in listbox
private void CheckSelected(Vector2 tappedPosition)
{
for (int i = 0; i < ShowedItemCount; i++)
{
// Check whether the tapped position is in the region
of
// listbox and in which one of the item bounds.
if (Bound.Contains(
(int)tappedPosition.X, (int)tappedPosition.Y)
&& tappedPosition.Y <
listItemBounds[i].Y + CharacterHeight)
{
SelectedIndex = i;
SelectedItem = list[Index + i];
break;
}
}
}
[/code] - Before giving the definitions of the AddItem() and RemoveItem() methods, let’s give the definition of the GetListItemBound() method:
[code]
private void GetListItemBound(List<String> list)
{
// If the count of the items is greater than 0
if (list.Count > 0)
{
Rectangle itemBound;
// If the current count of item is less than the
// ShowedItemCount, set the LoopBound to Count, else,
// set it to ShowedItemCount.
int LoopBound = Count < ShowedItemCount ? Count :
ShowedItemCount;
// Get the item bounds
for (int i = 0; i < LoopBound; i++)
{
itemBound = new Rectangle(
(int)Position.X,
(int)(Position.Y + Offset.Y) +
font.LineSpacing * i,
(int)ListBoxWidth, (int)CharacterHeight);
listItemBounds.Add(itemBound);
}
}
}
[/code] - Next it’s time for implementing the AddItem() and the RemoveItem() methods:
[code]
// Add text item to listbox
public void AddItem(string str)
{
// Add the text item to the list object
this.list.Add(str);
// Update total number of list items
Count = list.Count;
// Set the limited count for showing the list items
if (list.Count == 1)
{
ShowedItemCount = (int)(texBackground.Height /
CharacterHeight);
}
// Get the text item bounds
listItemBounds.Clear();
GetListItemBound(list);
}
[/code] - Now, define the RemoveItem() method:
[code]
public void RemoveItem(string str)
{
// Delete the text item from the list items
this.list.Remove(str);
// Update the total number of list items
Count = list.Count;
GetListItemBound(list);
}
[/code] - After the text item management functions, is the Selection Area creating method:
[code]
// Create the texture of the selected area
private void CreateSelectedArea(Rectangle rectangle)
{
// Initialize the selected area texture
SelectedArea = new Texture2D(game.GraphicsDevice,
rectangle.Width, rectangle.Height, false,
SurfaceFormat.Color);
// Initialize the pixels for the texture
Color[] pixels = new Color[SelectedArea.Width *
SelectedArea.Height];
for (int y = 0; y < SelectedArea.Height; y++)
{
for (int x = 0; x < SelectedArea.Width; x++)
{
pixels[x + y * SelectedArea.Width] =
new Color(new Vector4(125f, 125f,125f, 0.5f));
}
}
// Set the pixels to the selected area texture
SelectedArea.SetData<Color>(pixels);
}
[/code] - The final step in building the ListBox class is to draw the listbox on screen through the Draw() method:
[code]
public void Draw(GameTime gameTime)
{
// Draw the listbox background
spriteBatch.Draw(texBackground, Position, Color.White);
// The text items exist
if (Count > 0)
{
// If current count of items is less than the
// ShowedItemCount, show the items one by one
// from the beginning
if (Count <= ShowedItemCount)
{
for (int i = 0; i < Count; i++)
{
spriteBatch.DrawString(font, list[i],
Position + new Vector2(
Offset.X, Offset.Y + font.LineSpacing * i),
Color.White);
}
}
// If current count of items is greater than the
// ShowedItemCount, show the items from the current
// index.
else
{
for (int i = 0; i < ShowedItemCount; i++)
{
spriteBatch.DrawString(font, list[i + Index],
Position + new Vector2(
Offset.X, Offset.Y + font.LineSpacing * i),
Color.White);
}
}
// If the SelectionArea is not created, creat a new
// one
if (SelectedArea == null)
{
CreateSelectedArea(listItemBounds[0]);
}
// Draw the SelectedArea texture
spriteBatch.Draw(SelectedArea,
listItemBounds[SelectedIndex], Color.White);
}
scrollBar.Draw(gameTime);
}
[/code] - Woo! The ListBox class and its dependent classes are done. Now, we will use the ListBox class in our main project, and this is simple and easy to code. Insert the following lines to the ListBoxControlGame class field:
[code]
SpriteFont spriteFont;
ListBox listBox;
[/code] - Initialize the spriteFont object and listBox. Add the lines in the LoadContent() method:
[code]
// Create a new SpriteBatch, which can be used to draw
textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
spriteFont = Content.Load<SpriteFont>(“gameFont”);
listBox = new ListBox(new Vector2(200, 100), this.Content,
spriteBatch, this);
listBox.AddItem(“Item1”);
listBox.AddItem(“Item2”);
listBox.AddItem(“Item3”);
listBox.AddItem(“Item4”);
listBox.AddItem(“Item5”);
listBox.AddItem(“Item6”);
listBox.AddItem(“Item7”);
listBox.AddItem(“Item8”);
[/code] - Get the tapped position on listBox. Paste the following code to the Update() method:
[code]
TouchCollection touches = TouchPanel.GetState();
if (touches.Count > 0 && touches[0].State ==
TouchLocationState.Pressed)
{
Vector2 tapposition = touches[0].Position;
listBox.Update(gameTime, tapposition);
}
[/code] - Draw the listbox and selected text item on screen, and insert the block of code in to the Draw() method:
[code]
spriteBatch.Begin(SpriteSortMode.Immediate,
BlendState.NonPremultiplied);
listBox.Draw(gameTime);
spriteBatch.DrawString(spriteFont,
“ SelectedItem: “ + listBox.SelectedItem,
new Vector2(0, 0), Color.White);
spriteBatch.End();
[/code] - The whole project is complete. Build and run the application. It should look similar to the following screenshots:
How it works…
Steps 1–6 are about creating the Button class.
In step 2, the texButton stores the button texture; spriteBatch will render the button texture on screen; Position defines the button position on screen; the color object represents the button color; Tapped shows the tapping state of the button; OnTap is the event handler of tap gesture.
In step 3, the HitRegion property returns the button hit region around the button background texture.
In step 5, the Update() method gets the tapped position and checks whether it’s inside the button hit region. If yes, set Tapped to true and trigger the OnTapped event. Else, set Tapped to false.
In step 6, the code draws a button on the Windows Phone 7 screen. If the button is tapped, draw the button in red, or else, in white.
Steps 7–11 are about implementing the Scrollbar class.
In step 7, the spriteBatch will render the scrollbar background texture on screen; the scrollUp and scrollDown buttons will be used to increase or decrease the index of listbox items; Position stores the position of the scrollbar; scrollUpPosition and scrollDownPosition maintain the positions of the scrollUp and scrollDown buttons; the following two event handlers specify the tap events of the two scroll buttons when they are tapped. The two variables ScrollBarHeight and ScrollBarWidth define the height and width of the scrollbar buttons.
In step 8, the ScrollDownBound property returns the bound around the scrollDown button, similar to the ScrollUpBound property.
In step 9, the constructor initializes the two scrollbar buttons and gets the scrollbar width and height.
In step 10, the Update() method checks the tapped position to see whether it’s in the ScrollDownBound, if yes, set the Tapped of scrollDown to true and the property of scrollUp to false, then trigger the OnScrollDownTapped event; otherwise, do similar things for ScrollUpBound.
Steps 12–22 are to build the ListBox class using Button and Scrollbar classes.
In step 12, the game is the object of Game that supplies the GraphicDevice for drawing the selection area texture; spriteBatch will draw the listbox on screen; font will be used to draw the text of the listbox items; texBackground holds the listbox background texture; list is the collection of listbox text items; Position specifies the position of the listbox; Count shows the current total number of listbox items; scrollbar is the object of ScrollBar used to explore the listbox items; Index shows the current beginning index in listbox items; listItemBounds is the collection the bounds of list items. The following two variables indicate the index of the selected item in the listbox; if the item is selected, the SelectedArea will present a rectangle texture around the item; Offset is the position for drawing the text item relative to the position of the listbox; ShowedItemCount saves the maximum limitation of the number for rendering listbox items.
In step 13, the CharacterHeight returns the character height of the listbox text item. The Bound property gets the rectangle surrounding the listbox.
In step 15, when the scrollUp button is tapped, the Index will increase by 1 if the sum of Index plus ShowedItemCount is less than the amount of listbox items. While the scrollDown button is tapped and the Index is greater than 0, we will decrease the Index by one.
In step 16, first the Update() method checks the tapped position, to see whether it’s inside the buttons of the scrollbar. Then, use the CheckSelected() method to check the listbox item selection.
In step 17, because the ShowedItemCount limits the number for showing items, we just need to do the same steps for the loop. In the body of the for loop, first we check the tapped position to see whether it’s in the bound of the listbox, then we examine the lower-side of the current item bound to see whether it is greater than the Y factor of the tapped position. If so, it means the tapped position is in the region of the current item bound that is selected. Now, we set the current i to the SelectedIndex and the content of the current item to SelectedItem. The break here is important, as we only need the first selected one.
In step 18, we implement the GetListItemBound() method. When the list.Count is greater than 0 and if the current count of listbox item is less than ShowedItemCount, the loop will be equal to the Count; otherwise, the ShowedCount should be the LoopBound. In the loop, the code generates the item bound with the CharacterHeight and ListBoxWidth.
In step 19, once every new listbox item is added to the list, we will update the Count. This stands for the total number of listbox items. We get the ShowedItemCount when there is an item in the listbox. After that, we obtain the bounds of items through the GetListItemBound() method defined in step 18.
In step 21, the CreateSelectedArea() method first creates a new texture— SelectedArea which has the same size as the method parameter—rectangle. The second line defines the dimension of pixels equal to the SelectedArea. In the for loop, we set the actual color to each pixel of the new texture. Finally, the SetData() method copies the pixels to the SelectedArea for texture drawing.
In step 22, the first line of the Draw() method draws the listbox background texture. When the Count is greater than 0 and is equal to, or less than, the ShowedItemCount, the list item will be drawn one by one from the beginning of the list. Otherwise, we draw the items from the current index. After that, if one of the list items is selected, the SelectionArea will also be rendered around the selected item.
Steps 23–26 are for drawing the listbox on screen in the main game class ListBoxControlGame.
Creating a text input control to communicate with others in a game
Textbox is a very common and useful control in applications, reading the input and displaying the symbols in the main area. For multiplayer games, players love to use the control to communicate with each other for exchanging their thoughts. A textbox control can also act like a command line for controlling the game settings. With textbox control and corresponding functions, you can do a lot of things. In this recipe, you will learn how to make your own textbox control in Windows Phone 7.
How to do it…
The following steps will help you to implement a text input control for communicating in your own Windows Phone 7 game:
- Create a Windows Phone Game project in Visual Studio 2010 named TextBox, and change Game1.cs to TextBoxGame.cs. Then, add cursor.png, button.png, backspace.png, TextboxBackground.png, and gameFont.spriteFont to the content project.
- Now, let’s develop a button class for input. First of all, in the Button.cs file we declare the class field and property:
[code]
// Button texture
Texture2D texButton;
// SpriteBatch for drawing the button image
SpriteBatch spriteBatch;
// SpriteFont for drawing the button text
SpriteFont font;
// Button text
public String Text = “”;
// Button text position on the screen
public Vector2 TextPosition;
// Button text size
public Vector2 TextSize;
// Button position on the screen
public Vector2 Position;
// The Clicked bool value indicates whether tap in the button
public bool Clicked;
// Event handler when tap on the button
public event EventHandler OnClicked;
// Get the hit region
public Rectangle HitRegion
{
get
{
return new Rectangle((int)Position.X, (int)Position.Y,
texButton.Width, texButton.Height);
}
}
[/code] - Next, we define two overload constructors of the Button class:
[code]
// Initialize the button without text
public Button(Texture2D texture, Vector2 position, SpriteFont
font, SpriteBatch spriteBatch)
{
this.texButton = texture;
this.Position = position;
this.spriteBatch = spriteBatch;
this.font = font;
}
// Initialize the button with text
public Button(Texture2D texture, Vector2 position, String
text, SpriteFont font, SpriteBatch spriteBatch)
{
this.texButton = texture;
this.Position = position;
this.spriteBatch = spriteBatch;
this.Text = text;
// Compute the text size and place the text in the center
// of the button
TextSize = font.MeasureString(Text);
this.TextPosition = new Vector2(position.X +
texture.Width / 2 – TextSize.X / 2, position.Y);
this.font = font;
}
[/code] - In the following step, we will make the button react to the tap gesture. Add the Update() code as follows:
[code]
// Update the button
public void Update(GameTime gameTime, Vector2 touchPosition)
{
// React to the tap gesture
Point point = new Point((int)touchPosition.X,
(int)touchPosition.Y);
// If tapped button, set the Hovered to true and trigger
// the OnClick event
if (HitRegion.Contains(point))
{
Clicked = true;
OnClicked(this, null);
}
// Update the button
public void Update(GameTime gameTime, Vector2 touchPosition)
{
// React to the tap gesture
Point point = new Point((int)touchPosition.X,
(int)touchPosition.Y);
// If tapped button, set the Hovered to true and trigger
// the OnClick event
if (HitRegion.Contains(point))
{
Clicked = true;
OnClicked(this, null);
}
[/code] - The final step for the Button class is to draw it on the screen. To do this, we use this block of code:
[code]
// Draw the button
public virtual void Draw()
{
// Draw the button texture
if (!Clicked)
{
spriteBatch.Draw(texButton, HitRegion, Color.White);
}
else
{
spriteBatch.Draw(texButton, HitRegion, Color.Red);
}
// Draw the button text
spriteBatch.DrawString(font, Text, TextPosition,
Color.White);
}
[/code] - In this step, we begin to write the TextBoxControl class. In TextBoxControl.cs, add the lines to the TextBoxControl class as fields:
[code]
// SpriteBatch for drawing the textbox texture
SpriteBatch spriteBatch;
// SpriteFont for drawing the textbox font
SpriteFont spriteFont;
// Textbox background texture
Texture2D texBackGround;
// Textbox cursor texture
Texture2D texCursor;
// Textbox Bound for showing the text
public Rectangle Bound;
// Textbox position
public Vector2 Position;
// Textbox cursor position
public Vector2 CursorPosition;
// Timer used to control the cursor alpha value
float timer;
// Text position in the textbox
public Vector2 TextPosition;
// The text size of the showing text
public Vector2 textSize;
// The character size of the textbox text
private float characterSize;
// Alpha value for the cursor
int alpha = 255;
// The cursor color
Color cursorColor;
// TypedText stores the typed letters
public string TypedText = “”;
// ShowedText saves the text shown in the textbox
public string ShowedText = “”;
[/code] - Next, we add the properties to the TextBoxControl class:
[code]
// Get the character size
public float CharacterSize
{
get
{
textSize = spriteFont.MeasureString(TypedText);
characterSize = textSize.X / TypedText.Length;
return characterSize;
}
}
// Get the text size
public Vector2 TextSize
{
get
{
return textSize = spriteFont.MeasureString(TypedText);
}
}
// Get the bound for showing the text
public int ShowedCharacterBound
{
get
{
return (int)(Bound.Width / CharacterSize);
}
}
[/code] - The following part is about the TextBoxControl class initialization, and the constructer looks as follows:
[code]
// Initialize the textbox
public TextBoxControl(Vector2 position, Texture2D texCursor,
Texture2D texBackground, SpriteFont font, SpriteBatch
spriteBatch)
{
this.Position = position;
this.spriteBatch = spriteBatch;
this.texCursor = texCursor;
this.spriteFont = font;
this.texBackGround = texBackground;
// Set the bound of textbox control
Bound = new Rectangle((int)position.X, (int)position.Y,
texBackGround.Width, texBackGround.Height);
// Set the cursor position
this.CursorPosition = new Vector2(position.X + 10,
position.Y + 10);
// Set the text position
this.TextPosition = new Vector2(position.X + 10,
position.Y);
// Set the cursor color with alpha value
cursorColor = new Color(255, 255, 255, alpha);
}
[/code] - After the initialization, the following code is the definition of the Update() method:
[code]
public void Update(GameTime time)
{
// Accumulate the game elapsed milliseconds
timer += (float)time.ElapsedGameTime.TotalMilliseconds;
// Every 500 milliseconds the alpha value of the cursor
will
// change from 255 to 0 or 0 to 255.
if (timer > 500)
{
if (alpha == 255)
{
alpha = 0;
}
else if (alpha == 0)
{
alpha = 255;
}
cursorColor.A = (byte)alpha;
timer = 0;
}
}
[/code] - Then we define the Draw() method :
[code]
public void Draw()
{
// Draw the textbox control background
spriteBatch.Draw(texBackGround, Position, Color.White);
// Draw the textbox control cursor
spriteBatch.Draw(texCursor, CursorPosition, cursorColor);
// Draw the textbox showing text
spriteBatch.DrawString(spriteFont, ShowedText,
TextPosition, Color.White);
}
[/code] - From this step, we will use the Button class and the TextBoxControl class in the main game class. Now, add the lines to the TextBoxGame class fields:
[code]
// SpriteFont object
SpriteFont font;
// TextboxControl object
TextBoxControl textBox;
// Button objects
Button buttonA;
Button buttonB;
Button buttonBackspace;
[/code] - Initialize the textbox control and buttons. Insert the code to the LoadContent() method:
[code]
// Load the textbox textures
Texture2D texCursor = Content.Load<Texture2D>(“cursor”);
Texture2D texTextboxBackground =
Content.Load<Texture2D>(“TextboxBackground”);
// Load the button textures
Texture2D texButton = Content.Load<Texture2D>(“button”);
Texture2D texBackSpace = Content.Load<Texture2D>(“Backspace”);
font = Content.Load<SpriteFont>(“gameFont”);
// Define the textbox position
Vector2 position = new Vector2(400, 240);
// Initialize the textbox
textBox = new TextBoxControl(position, texCursor,
texTextboxBackground, font, spriteBatch);
// Initialize the buttonA
buttonA = new Button(texButton, new Vector2(400, 350), “A”,
font, spriteBatch);
buttonA.OnClicked += new EventHandler(button_OnClicked);
// Initialize the buttonB
buttonB = new Button(texButton, new Vector2(460, 350), “B”,
font, spriteBatch);
buttonB.OnClicked += new EventHandler(button_OnClicked);
// Initialize the backspace button
buttonBackspace = new Button(texBackSpace,
new Vector2(520, 350), font, spriteBatch);
buttonBackspace.OnClicked += new
EventHandler(buttonBackspace_OnClicked);
[/code] - Define the event handling code for buttonA and button, which is same for both:
[code]
void button_OnClicked(object sender, EventArgs e)
{
// Add the button text to the textbox TypedText
// Update the position of textbox cursor
textBox.CursorPosition.X = textBox.TextPosition.X +
textBox.TypedText += ((Button)sender).Text;
textBox.TextSize.X;
// Get the textbox showed character bound
int showedCharacterBound = textBox.ShowedCharacterBound;
// check whether the textbox cursor goes outside of the
// textbox bound
if (textBox.CursorPosition.X > textBox.Bound.X +
textBox.Bound.Width)
{
// If yes, set cursor positon at the right side of
// the textbox
textBox.CursorPosition.X = textBox.TextPosition.X +
textBox.CharacterSize * showedCharacterBound;
// Show the TypedText from end to the left in
// the range for showing characters of textbox
textBox.ShowedText =
textBox.TypedText.Substring(textBox.TypedText.Length –
showedCharacterBound – 1, showedCharacterBound);
}
else
{
// If not, just set the current TypedText to the
// showedText
textBox.ShowedText = textBox.TypedText;
}
}
[/code] - The next block of code is the handling code for the backspace button:
[code]
void buttonBackspace_OnClicked(object sender, EventArgs e)
{
// Get the length of TypedText
int textLength = textBox.TypedText.Length;
// Check whether the TypedText is greater than 0
if (textLength > 0)
{
// If yes, delete the last character
textBox.TypedText = textBox.TypedText.Substring(0,
textLength – 1) ;
// Get the current showed character count.
int showedCharacterCount = (int)(textBox.TextSize.X /
textBox.CharacterSize);
// Check whether the current showed character count is
less than
// the textbox showed character bound
if (showedCharacterCount <=
textBox.ShowedCharacterBound)
{
// If yes, just update the cursor position with
// current text size and the showedText with
// current text
textBox.CursorPosition.X = textBox.TextPosition.X
+ textBox.TextSize.X;
textBox.ShowedText = textBox.TypedText;
}
else
{
// If not, show the TypedText from end to the
// left in the range for showing characters
// of textbox
textBox.ShowedText = textBox.TypedText.Substring(
textBox.TypedText.Length –
textBox.ShowedCharacterBound,
textBox.ShowedCharacterBound);
}
}
}
[/code] - Trigger the button event. Add the code to the Update() method:
[code]
TouchCollection touches = TouchPanel.GetState();
if(touches.Count > 0 && touches[0].State ==
TouchLocationState.Pressed)
{
buttonA.Update(gameTime, touches[0].Position);
buttonB.Update(gameTime, touches[0].Position);
buttonBackspace.Update(gameTime, touches[0].Position);
}
textBox.Update(gameTime);
[/code] - Draw the textbox and buttons on screen. Paste the code into the Draw() method:
[code]
spriteBatch.Begin();
textBox.Draw();
buttonA.Draw();
buttonB.Draw();
buttonBackspace.Draw();
spriteBatch.End();
[/code] - Now, build and run the application. When you tap button A and button B, the textbox will show the input as shown in the following screenshot to the left. When you tap the backspace button, it will look similar to the following screenshot on the right:
How it works…
Steps 2–5 are responsible for creating the Button class:
In step 2, the texButton stores the button texture; font will be used to render the button text, we use the button Text for the input text; the position variable tells you where the button is on the screen; the bool value Clicked indicates whether the tap gesture takes place in the button hit region; when the button is clicked, the OnClicked event will be triggered. The HitRegion property returns the bound of the button for clicking.
In step 3, the first constructor initializes the button without text. The second constructor initializes the button with text and places the text in the center of the button. The SpriteFont.MeasureString() method computes and returns the text size as a Vector2, the X value holds the text width, and the Y value holds the text height.
In step 4, the reacting code first gets the tapped position, then use the Rectangel. Contains() method to check whether the position is inside the hit region, if yes, we set the Clicked to true and trigger the OnClicked() event.
Steps 6–10 are about creating the TextBoxControl class:
In step 6, the first four variables deal with the textbox texture and font; the following Bound variable stores the textbox bound for showing text; Position indicates the location of the textbox control on the screen; the CursorPosition represents the cursor place within the textbox control bound; the timer variable will be used to control the alpha value of the cursor for the flashing effect; the TextPosition shows the text position inside the textbox control; textSize represents the size of the TypedText; the characterSize defines the size of a single character of the TypedText; the ShowedText stores the text that will be presented in the textbox.
In step 7, the CharacterSize returns the size of a single character in the TypedText, we use SpriteFont.MeasureString() to compute the size of the TypedText, then use the X value of the textSize and divide the TypedText length to get the unit character length; the TextSize returns the size of TypedText; ShowedCharacterBound returns the region for showing the TypedText.
In step 9, the Update() method checks whether the accumulated milliseconds are greater than 500 or not. If yes and the alpha value is equal to 255 (opaque), it will be set to 0 (transparent), and vice versa. After setting the latest alpha value to alpha factor of cursor color—cursorColor.A, we reset the timer for the next interval.
Steps 11–16 are about using the Button and TextBoxControl class in the main class. We will draw the button and textbox control on the Windows Phone 7 screen and perform the reactions for text input and delete.
In step 11, the textBox stands for the TextBoxControl; buttonA represents the button for input character A; buttonB is used to input character B; the buttonBackspace will delete the character of the TypedText from the end to the beginning.
In step 12, the code loads the textures for the textbox and buttons first. Then, it initializes their event handling code.
In step 13, the code reacts to the event triggered from buttonA or buttonB. The first line casts the sender to Button. Then add the Text value to the TextBoxControl.TypedText. After getting the text, the cursor position is updated following the new TypedText. The rest of the code deals with the situation when the length of the TypedText is greater than the textbox bound. If this happens, the cursor will still stay at the right-side of the textbox, the showedText will be the substring of the TypedText from the end to the left in the range for showing characters of the textbox. On the other hand, the entire TypedText will be drawn.
In step 14, as the reaction code for the backspace button, at the beginning, we get the length of the TypedText. Then check whether it is greater than 0. If yes, we delete the last character. The rest of the code works with the state when the deleted TypedText length is greater or less than the textbox bound. If greater, the showedText will range from the end of the deleted TypedText to the left about the showed character count of the textbox. Otherwise, the cursor will follow the current TypedText, which will be completely rendered on the screen.