Playing Audio, Windows Phone Audio

Getting Started with Windows Phone Audio

The audio system in XNA makes it possible to reproduce sound effects and music in two different ways, but the WP7 platform supports only one of them. First, we can play audio clips directly from audio files loaded at runtime, with support for the most common audio file formats. Second, in XNA, we can use Microsoft’s Cross-Platform Audio Creation Tool, also known as XACT, which is more often used for complex games with many audio files. The first approach involves loading and managing audio objects in our own code. The second approach leaves the details largely up to classes provided for working with XACT resources built at compile time and then made available from a container of objects. But we can’t use XACT with WP7 projects, so we’ll learn about the first and only option instead. We’ll cover the audio system in the Microsoft.Xna.Framework.Audio namespace with an example of the audio system.

Simple Audio Playback

There is one very easy way to get audio to play in an XNA project: by using the SoundEffect class. There is a drawback to using SoundEffect assets that really can’t be avoided: the tendency for the content project to become cluttered with asset files. Even so, the SoundEffect class is convenient and easy to use. A helper class called SoundEffectInstance is also used in conjunction with SoundEffect for audio playback. SoundEffect itself has a Play() method, but it is rudimentary. SoundEffectInstance.Play() is more capable and versatile.

Adding Audio Content

Before we can play an audio clip, we have to add it to the content system.

  1. First, right-click the Content project in Solution Explorer, choose Add, and then choose Existing Item, as shown in Figure 18.1. This opens the Add Existing File dialog box.

    Adding an existing audio file to the content project.
    FIGURE 18.1 Adding an existing audio file to the content project.
  2. Locate the audio file you want to add to the project. Select the file in the file browser and click the OK button.

Loading an Audio Asset File

The following audio file types can be added to an XNA project for use with the SoundEffect class, but remember that WP7 does not support XACT:

  • XAP
  • WAV
  • WMA
  • MP3

You can create an instance of the SoundEffect class with a variable declared in the globals section at the top of the class as follows:

[code]
SoundEffect clip;
[/code]

The SoundEffect object is created in the usual LoadContent() function where all other asset files are loaded:

[code]
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
font = Content.Load<SpriteFont>(“Arial”);
clip = Content.Load<SoundEffect>(“sound_clip”);
}
[/code]

Playing an Audio Clip

The audio clip can be played back using the SoundEffect.Play() method. There is a simple version of Play() and an overloaded version, which gives you control over the Volume, Pitch, and Pan properties. The audio clip is played directly from the object like so:

[code]
clip.Play();
[/code]

Audio Clip Length

There is really only one useful property in the SoundEffect class: Duration. This property gives you the length of the audio clip in seconds. In the Draw() function of your sample program (Simple Audio Demo), you can print out the length of the audio clip.

[code]
string text = “Clip length: “ + clip.Duration.ToString();
spriteBatch.DrawString(font, text, Vector2.Zero, Color.White);
[/code]

When it comes down to playing audio clips in gameplay code, this class is not very helpful at all. What we need is a class with more functionality.

SoundEffectInstance

The SoundEffect class works, but not very well on its own. For one thing, a SoundEffect clip played does not work well in a complex audio environment (with music and different sound effects playing at the same time). For that, we need a helper class that can repeat playback with mixing support. That class is called SoundEffectInstance. This is really what we want to use when playing audio clips.

The SoundEffectInstance class enhances the basic functionality of SoundEffect with additional properties and methods that make it more useful in a real-world game. After loading the SoundEffect in LoadContent(), we can create an instance of the SoundEffectInstance class from its CreateInstance() method:

[code]
SoundEffect clip = Content.Load<SoundEffect>(“sound_clip”);
SoundEffectInstance clipInst = clip.CreateInstance();
[/code]

This class makes available several useful methods: Play(), Stop(), Pause(), and Resume(). And the Volume, Pitch, and Pan properties have been moved from parameters into real class properties, which we can modify outside of the Play() method. In addition, we can cause an audio clip to loop during playback with the IsLooping property.

MySoundEffect Class

I have prepared a helper class with essentially the sole purpose of just combining a SoundEffect and SoundEffectInstance object with a Load() method. We will use this helper class in the example coming up next. The source code for the class is found in Listing 18.1.

LISTING 18.1 Source Code for the MySoundEffect Class

[code]
public class MySoundEffect
{
private ContentManager p_content;
public SoundEffect effect;
public SoundEffectInstance instance;
public MySoundEffect(ContentManager content)
{
p_content = content;
effect = null;
instance = null;
}
public void Load(string assetName)
{
effect = p_content.Load<SoundEffect>(assetName);
instance = effect.CreateInstance();
}
}
[/code]

Creating the Audio Demo Program

The Audio Demo program is a complete example of audio for WP7 with some user input features as well, as shown in Figure 18.2. Five buttons are located on the screen that can be touched to play the five audio clips loaded into the program.

The Audio Demo program.
FIGURE 18.2 The Audio Demo program.

Button Class

The audio sample project uses a helper class called Button to draw numbered buttons on the screen that respond to screen touch input events. The Button class requires a bitmap file called button.png that is just a transparent image with a border around its edges. Listing 18.2 contains the source code for the Button class.

LISTING 18.2 The Button Class

[code]
public class Button : Sprite
{
public string text;
private SpriteBatch p_spriteBatch;
private SpriteFont p_font;
public Button(ContentManager content, SpriteBatch spriteBatch,
SpriteFont font) : base(content, spriteBatch)
{
p_spriteBatch = spriteBatch;
p_font = font;
Load(“button”);
text = ““;
color = Color.LightGreen;
}
public void Draw()
{
base.Draw();
Vector2 size = p_font.MeasureString(text);
Vector2 pos = position;
pos.X -= size.X / 2;
pos.Y -= size.Y / 2;
p_spriteBatch.DrawString(p_font, text, pos, color);
}
public bool Tapped(Vector2 pos)
{
Rectangle rect = new Rectangle((int)pos.X, (int)pos.Y, 1, 1);
return Boundary().Intersects(rect);
}
}
[/code]

Audio Demo Source

The main source code for the Audio Demo project is found in Listing 18.3. The required assets are a button image and five audio files named clip1, clip2, clip3, clip4, and clip5.

LISTING 18.3 Source Code for the Audio Demo Program

[code]
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
TouchLocation oldTouch;
SpriteFont font;
Button[] buttons;
MySoundEffect[] sounds;
int current = -1;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = “Content”;
TargetElapsedTime = TimeSpan.FromTicks(333333);
oldTouch = new TouchLocation();
}
protected override void Initialize()
{
base.Initialize();
this.IsMouseVisible = true;
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
font = Content.Load<SpriteFont>(“WascoSans”);
//create buttons
buttons = new Button[5];
for (int n = 0; n < 5; n++)
{
buttons[n] = new Button(Content, spriteBatch, font);
buttons[n].text = (n+1).ToString();
buttons[n].position = new Vector2(100 + 110 * n, 200);
}
//create sound clips
sounds = new MySoundEffect[5];
for (int n = 0; n < 5; n++)
{
sounds[n] = new MySoundEffect(Content);
sounds[n].Load(“Audio//clip” + (n+1).ToString());
}
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
ButtonState.Pressed)
this.Exit();
TouchCollection touchInput = TouchPanel.GetState();
if (touchInput.Count > 0)
{
TouchLocation touch = touchInput[0];
if (touch.State == TouchLocationState.Pressed)
{
current = -1;
int n = 0;
foreach (Button b in buttons)
{
int x = (int)touch.Position.X;
int y = (int)touch.Position.Y;
if (b.Boundary().Contains(x,y))
{
current = n;
sounds[current].instance.Play();
break;
}
n++;
}
}
oldTouch = touch;
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin(SpriteSortMode.FrontToBack,
BlendState.AlphaBlend);
spriteBatch.DrawString(font, “Audio Demo”, Vector2.Zero,
Color.White);
foreach (Button b in buttons)
{
b.Draw();
}
if (current > -1)
{
string text = “Clip length: “ +
sounds[current].effect.Duration.ToString();
spriteBatch.DrawString(font, text, new Vector2(0, 400),
Color.White);
}
spriteBatch.End();
base.Draw(gameTime);
}
}
[/code]

We have now fully covered the XNA audio system with an example of how to load and play audio files. The helper class, MySoundEffect, can be dropped into any WP7 project and used to more easily handle the audio needs of a game.

Windows Phone Using Location Services (GPS)

GPS 101

Let’s spend a few minutes to just learn the basics of GPS, in order to better use it in our code. What GPS boils down to—the nitty-gritty—are two floating-point numbers representing the X and Y position on the Earth. The X value has traditionally been called longitude, and the Y value has been known as the latitude. From a game programming perspective, this is an easier way to grasp the terms, but a naval veteran would scoff at the overly simplistic way this is being presented. We’ll gloss over issues of precision in order to grasp the concepts first.

Longitude represents the “X” or horizontal coordinate on the surface of the Earth, running east or west from the zero point.

Latitude represents the “Y” or vertical coordinate on the surface of the Earth, running north or south from the zero point.

The origin (0,0) is located about 400 miles off the western coast of Africa, southwest of Nigeria and south of Ghana. From that origin point, longitude increases to the right (east), and decreases to the left (west); latitude increases up (north), and decreases down (south). In other words, it is oriented exactly like the Cartesian coordinate system we’ve been using all along for our trig-heavy examples. This makes translating GPS coordinates for the purpose of making a reality game a cinch!

To help make sense of the coordinate system, Table 17.1 shows the approximate latitude and longitude values of several major cities in the world, formatted in a way that makes sense to game programmers (such that longitude comes before latitude— remember, we aren’t navigating here). Note that these are far from precise, just rough estimates to present the general location of each city. More precise GPS coordinates will include up to six decimal places of increasing precision, down to just 10 feet or less in granularity.

GPS Data for Major Cities

If you want to learn more about latitude and longitude coordinates, there is an interactive world map available online at http://itouchmap.com/latlong.html.

Windows Phone Location Services

XNA provides us with a geographic location service in a library located in a namespace called System.Device.Location. This library is not included in the project’s references by default, so we must add it to use this library in our program.

Adding the Location Services Library

  1. Right-click References in the Solution Explorer, and then choose Add Reference.
  2. In the dialog box that comes up, there is a list with the .NET tab already in view, as shown in Figure 17.1. Select System.Device from the list and click the OK button.
  3. The geographic location services library is in a namespace called System.Device.Location, which must be added with a using statement to any program that needs these services:
    [code]
    using System.Device.Location;
    [/code]

Using the Location Services

To read the current device’s GPS location, we create an object using the GeoCoordinateWatcher class:

[code]
GeoCoordinateWatcherSim watcher;
[/code]

It is okay to create the watcher object in Initialize() or LoadContent(), or in response to a user event:

[code]
watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.Default);
[/code]

At this point, the object is created but is not yet receiving any GPS data. We have to create an event handler to handle position status change events. The trigger that causes such an event is movement of the device, which can be fine-tuned with the MovementThreshold property:

[code]
watcher.MovementThreshold = 20;
[/code]

Adding a reference to the System.Device library.
FIGURE 17.1 Adding a reference to the System.Device library.

The first event we’ll tap into is StatusChanged. A new event method will need to be created to correspond with the name of the method passed to this new event object. In this case, the example is using a string called statusText, which can be printed out from the main Draw() call. Optionally, a programmer-defined status could be set here and used elsewhere in the game:

[code]
watcher.StatusChanged += new EventHandler
<GeoPositionStatusChangedEventArgs>(watcher_StatusChanged);
void watcher_StatusChanged(object sender,
GeoPositionStatusChangedEventArgs e)
{
switch (e.Status)
{
case GeoPositionStatus.Disabled:
statusText += “Location service has been disabledn”;
break;
case GeoPositionStatus.Initializing:
statusText += “Location service is initializingn”;
break;
case GeoPositionStatus.NoData:
statusText += “Location service is not returning any datan”;
break;
case GeoPositionStatus.Ready:
statusText += “Location service is receiving datan”;
break;
}
}
[/code]

The actual movement of the GPS device triggers position change events that we can tap into with the PositionChanged event. A similar event method will have to be created for this event as well. In this example, a GeoCoordinate variable called coord is set using the passed parameter that contains the GPS location data:

[code]
watcher.PositionChanged += new EventHandler
<GeoPositionChangedEventArgs<GeoCoordinate>>(watcher_PositionChanged);
void watcher_PositionChanged(object sender,
GeoPositionChangedEventArgs<GeoCoordinate> e)
{
coord = e.Position.Location;
}
[/code]

Simulating Position Changes

The WP7 emulator does not have a GPS receiver, and even if your PC has one, the emulator doesn’t know how to use it—the emulator is a self-contained system that only uses the networking of your PC to simulate connectivity. I say “simulate” because in a real WP7 device, that Internet connection would come through the airwaves, presumably G3 or G4, depending on what the service provider supports.

There is a workaround for the limitation. If you want to create a game that uses location services, it’s a given you must be able to test it extensively, and even with a real WP7 device, testing GPS code can be a challenge. So, even with hardware, it may be preferred to develop this code with a GPS simulation rather than the real thing. With a simulation, you can define the location data yourself and write the gameplay code to respond to location data in a predictable way. Only the final testing stages of the game would need to be done “in the field.”

So, a question arises: How do we simulate GPS data?

The solution is to write a class that inherits from GeoLocationWatcher and then fill in data events with a timer that generates real-time updates via GeoLocation events. Voilà!

GeoLocationSim

There are three classes involved in the geographic location simulator. The first is GeoLocationSim, which inherits directly from GeoCoordinateWatcher, the main GPS class in XNA. There are quite a few properties, events, and methods defined in this abstract class that are required to pass this off as a legitimate GeoLocation class so that it works with normal GeoLocation code, but we don’t need all of that for testing purposes. Nevertheless, they are all required. In the sample project for this hour, I have added all three classes in a source file called GeoLocationSim.cs. First, take a look at Listing 17.1, the code for the sim class.

LISTING 17.1 Base GeoLocation Simulation Class

[code]
abstract public class GeoLocationSim : GeoCoordinateWatcher
{
private GeoPosition<GeoCoordinate> current;
private Timer timer;
public GeoLocationSim()
{
current = new GeoPosition<GeoCoordinate>();
Status = GeoPositionStatus.Initializing;
RaiseStatusChanged();
}
private void RaiseStatusChanged()
{
GeoPositionStatusChangedEventArgs args =
new GeoPositionStatusChangedEventArgs(Status);
if (StatusChanged != null)
{
StatusChanged(this, args);
}
}
private void RaisePositionChanged()
{
GeoPositionChangedEventArgs<GeoCoordinate> args =
new GeoPositionChangedEventArgs<GeoCoordinate>(current);
if (PositionChanged != null)
PositionChanged(this, args);
}
public void OnTimerCallback(object state)
{
try
{
if (Status == GeoPositionStatus.Initializing)
{
Status = GeoPositionStatus.NoData;
RaiseStatusChanged();
}
StartGetCurrentPosition();
TimeSpan next = GetNextInterval();
timer.Change(next, next);
}
catch (Exception)
{
throw;
}
}
protected void UpdateLocation(double longitude, double latitude)
{
GeoCoordinate location = new GeoCoordinate(latitude, longitude);
if (!location.Equals(current.Location))
{
current = new GeoPosition<GeoCoordinate>(
DateTimeOffset.Now, location);
if (Status != GeoPositionStatus.Ready)
{
Status = GeoPositionStatus.Ready;
RaiseStatusChanged();
}
RaisePositionChanged();
}
}
abstract protected TimeSpan GetNextInterval();
abstract protected void StartGetCurrentPosition();
//override base property
public GeoPositionPermission Permission
{
get { return GeoPositionPermission.Granted; }
}
//override base property
public GeoPosition<GeoCoordinate> Position
{
get { return current; }
}
//override base event
public event EventHandler<GeoPositionChangedEventArgs
<GeoCoordinate>> PositionChanged;
//override base method
public void Start(bool suppressPermissionPrompt)
{
Start();
}
//override base method
public void Start()
{
TimeSpan span = GetNextInterval();
timer = new Timer(OnTimerCallback, null, span, span);
}
//override base property
public GeoPositionStatus Status
{
get;
protected set;
}
//override base event
public event EventHandler
<GeoPositionStatusChangedEventArgs> StatusChanged;
//override base method
public void Stop()
{
timer.Change(Timeout.Infinite, Timeout.Infinite);
Status = GeoPositionStatus.Disabled;
RaiseStatusChanged();
}
//override base method
public bool TryStart(bool suppressPermissionPrompt, TimeSpan timeout)
{
Start();
return true;
}
}
[/code]

Filling in GPS Data with Timing

SampleGeoCoord is a helper class that is used to fill in GPS position data with timing. Each position coordinate corresponds to a one-second interval at which the position update event is triggered. So, this class supplies longitude, latitude, and time.

[code]
public class SampleGeoCoord
{
public double Longitude { get; set; }
public double Latitude { get; set; }
public TimeSpan Time { get; set; }
public SampleGeoCoord(double Longitude, double Latitude, int seconds)
{
this.Longitude = Longitude;
this.Latitude = Latitude;
this.Time = new TimeSpan(0, 0, seconds);
}
}
[/code]

GeoCoordinateWatcherSim

The GeoCoordinateWatcherSim is our main workhorse simulation class, inheriting directly from GeoLocationSim. This class puts the GeoLocationSim properties, methods, and events to work using data populated within an array of SampleGeoCoord objects. In the example coming up that uses this class, I’ve centered the coordinates around Los Angeles, with 60 seconds of random locations within a radius of about 100 miles around the city coordinates (-118, 34). Listing 17.2 contains the code for the GeoCoordinateWatcherSim class.

LISTING 17.2 Usable GeoCoordinateWatcherSim Worker Class

[code]
public class GeoCoordinateWatcherSim : GeoLocationSim
{
List<SampleGeoCoord> events;
int currentEventId;
Random rand = new Random();
public GeoCoordinateWatcherSim(GeoPositionAccuracy accuracy)
{
currentEventId = 0;
events = new List<SampleGeoCoord>();
//create random coordinates in Los Angeles
for (int n = 1; n < 60; n++)
{
double Long = -118 – rand.Next(2) – rand.NextDouble();
double Lat = 33 + rand.Next(2) + rand.NextDouble();
events.Add(new SampleGeoCoord(Long, Lat, n));
}
}
private SampleGeoCoord Current
{
get
{
return events[currentEventId % events.Count];
}
}
protected override void StartGetCurrentPosition()
{
this.UpdateLocation(Current.Longitude, Current.Latitude);
currentEventId++;
}
protected override TimeSpan GetNextInterval()
{
return Current.Time;
}
}
[/code]

Creating the Geo Position Demo

Let’s write a program to demonstrate the GeoCoordinateWatcherSim class in action. The example requires only a font, because it just prints out the longitude and latitude of the geographical coordinate data and the status of the watcher. The code for the Geo Position Demo program is found in Listing 17.3, and Figure 17.2 shows the program running. Note that this example will work on a WP7 device without the simulated data with a single line change, from

[code]
watcher = new GeoCoordinateWatcherSim(…);
[/code]

to

[code]
watcher = new GeoCoordinateWatcher(…);
[/code]

The Geo Position Demo simulates GPS movement.
FIGURE 17.2 The Geo Position Demo simulates GPS movement.

LISTING 17.3 The Geo Position Demo Program

[code]
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
TouchLocation oldTouch;
Random rand;
SpriteFont font;
string statusText = ““;
GeoCoordinateWatcherSim watcher = null;
GeoCoordinate coord = null;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = “Content”;
TargetElapsedTime = TimeSpan.FromTicks(333333);
oldTouch = new TouchLocation();
}
protected override void Initialize()
{
base.Initialize();
StartGeoLocation();
}
protected override void LoadContent()
{
rand = new Random();
spriteBatch = new SpriteBatch(GraphicsDevice);
font = Content.Load<SpriteFont>(“WascoSans”);
}
protected override void UnloadContent()
{
base.UnloadContent();
watcher.Stop();
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
ButtonState.Pressed)
this.Exit();
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin(SpriteSortMode.FrontToBack,
BlendState.AlphaBlend);
spriteBatch.DrawString(font, “Latitude: “ +
coord.Latitude.ToString(“0.000”),
new Vector2(100, 10), Color.White);
spriteBatch.DrawString(font, “Longitude: “ +
coord.Longitude.ToString(“0.000”),
new Vector2(100, 30), Color.White);
spriteBatch.DrawString(font, statusText,
new Vector2(100, 100), Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
void StartGeoLocation()
{
coord = new GeoCoordinate();
//try to create geo coordinate watcher
if (watcher == null)
{
statusText += “Starting location service…n”;
watcher = new GeoCoordinateWatcherSim(
GeoPositionAccuracy.Default);
watcher.MovementThreshold = 20;
watcher.StatusChanged += new EventHandler
<GeoPositionStatusChangedEventArgs>(
watcher_StatusChanged);
watcher.PositionChanged += new EventHandler
<GeoPositionChangedEventArgs<GeoCoordinate>>
(watcher_PositionChanged);
watcher.Start();
}
}
void watcher_StatusChanged(object sender,
GeoPositionStatusChangedEventArgs e)
{
switch (e.Status)
{
case GeoPositionStatus.Disabled:
statusText += “Location service has been disabledn”;
break;
case GeoPositionStatus.Initializing:
statusText += “Location service is initializingn”;
break;
case GeoPositionStatus.NoData:
statusText += “Location service is not returning any datan”;
break;
case GeoPositionStatus.Ready:
statusText += “Location service is receiving datan”;
break;
}
}
void watcher_PositionChanged(object sender,
GeoPositionChangedEventArgs<GeoCoordinate> e)
{
coord = e.Position.Location;
}
}
[/code]

There are many uses for GPS tracking, not to mention potential multiplayer games, but one thing to keep in mind is that GPS only provides location data, but there’s no transmitting of that data. After the location is received, that’s it—it’s data, and it’s not transmitted anywhere. GPS is read-only. So, if you have in mind a game, there must still be a network infrastructure connecting all the players, wherein each player will transmit his or her GPS location to the other players over the network. The WP7 platform supports Xbox Live for networking, so that is likely the next subject to study if you’re interested in making a networked game.

Advanced Linear and Angular Velocity

Calculating Angular Velocity

We have done a lot of work already with sprite transforms, so now it’s time to put some of these new features to the test in a real-world situation that often comes up in game projects. We’ll have a sprite move on the screen based on user input, and move in the direction it is facing. This requires some familiar trigonometry functions used in a creative way.

To begin, we need to understand the starting point for trigonometric calculations. The artwork in a game is often oriented with the “front” or “nose” pointing upward. But in our math calculations, that starting point will always be to the right, or 90 degrees clockwise from the up direction, as shown in Figure 9.1.

Trigonometry functions assume that angle 0 is right, not up.
FIGURE 9.1 Trigonometry functions assume that angle 0 is right, not up.

Math functions dealing with rotation angles and velocities always work with radians, not degrees. Using degrees in code is fine, but angles must be converted to radians during calculations. This can be done with MathHelper.ToDegrees() and MathHelper.ToRadians().

We use cosine() to calculate the X component, and sine() to calculate the Y component for the velocity of an object. In XNA, we can use the Math.Cos() and Math.Sin() methods to perform these calculations. The sole parameter passed to both of these methods is the angle that an object is facing or moving toward.

The angle will be any value from 0 to 360 degrees, including decimal values for partial degrees. When the calculations are made, the angle must be converted to radians. Suppose that the angle is 10 degrees. We convert this to radians with the following:

[code]
float radians = MathHelper.ToRadians( 10 );
// answer: radians = 0.174532925
[/code]

The angular velocity is calculated using this radian value, rounded to 0.1745 for our purposes (although the complete floating-point value is used with all decimal places in memory):

[code]
Velocity X = Cos( 0.1745 )
Velocity Y = Sin( 0.1745 )
[/code]

Figure 9.2 shows a circle with the angle and calculated values.

Calculating angular velocity.
FIGURE 9.2 Calculating angular velocity.

The results are X = 0.9848 and Y = 0.1736, as shown in the illustration. Consider the direction the arrow is facing in the illustration (10 degrees). The X and Y velocity values make sense, given that angle. Considering pixel movement on the screen, at this angle a sprite will move in the X axis much more than in the Y axis, a ratio of about five-and-a-half to one (5.5:1). So, when a sprite is moving across the screen at an angle of 10 degrees, it will move 5.5 pixels to the right (+X) for every 1 pixel down (+Y). If the angle were 180, for instance, the arrow would be pointing to the left, which would result in a negative X velocity.

Updating the Sprite Class

Some changes are needed for the Sprite class to work with the upcoming sample program. There are some new variables and some improvements to the Draw() and Rotate() methods. To make rotation more versatile, the origin variable (a float) has been moved out of Draw() and into the class’s public declarations so that it can be modified as a public variable. The Rotate() method has some improvements to make its boundary checking more accurate. The changes are included in Listing 9.1.

LISTING 9.1 Yet even more changes to the Sprite class!

[code]
public class Sprite
{
private ContentManager p_content;
private SpriteBatch p_spriteBatch;
public Texture2D image;
public Vector2 position;
public Vector2 velocityLinear;
public Color color;
public float rotation;
public float velocityAngular;
public Vector2 scaleV;
public Vector2 origin; //new
public bool alive; //new
public bool visible; //new
public Sprite(ContentManager content, SpriteBatch spriteBatch)
{
p_content = content;
p_spriteBatch = spriteBatch;
image = null;
position = Vector2.Zero;
velocityLinear = Vector2.Zero;
color = Color.White;
rotation = 0.0f;
velocityAngular = 0.0f;
scaleV = new Vector2(1.0f);
origin = Vector2.Zero; //new
alive = true; //new
visible = true; //new
}
public float scale
{
get { return scaleV.X; }
set
{
scaleV.X = value;
scaleV.Y = value;
}
}
public bool Load(string assetName)
{
try
{
image = p_content.Load<Texture2D>(assetName);
origin = new Vector2(image.Width / 2, image.Height / 2); //new
}
catch (Exception) { return false; }
return true;
}
public void Draw()
{
//Vector2 origin = new Vector2(image.Width / 2, image.Height / 2);
p_spriteBatch.Draw(image, position, null, color, rotation,
origin, scaleV, SpriteEffects.None, 0.0f);
}
public void Move()
{
position += velocityLinear;
}
public void Rotate()
{
rotation += velocityAngular;
if (rotation > Math.PI * 2)
rotation -= (float)Math.PI * 2; //change
else if (rotation < 0.0f)
rotation = (float)Math.PI * 2 – rotation; //change
}
}
[/code]

There’s a great tutorial lesson on all the functions of trigonometry on Wikipedia here: http://en.wikipedia.org/wiki/Trigonometric_functions.

Apache Helicopter Demo

The example for this section is included in the hour resource files, so you may open the project while studying the code in Listing 9.2. This demo draws a small sprite of an Apache helicopter firing bullets at whatever angle it is facing. Touching the top of the screen will cause the chopper’s nose to rotate upward. Likewise, touching the bottom of the screen will rotate the chopper’s nose downward. Touching the center of the screen will cause the chopper to fire its bullets in its current facing angle. Figure 9.3 shows the program running, and it looks almost like the start of a game! It could be, with a little work! As we continue to improve the Sprite class, the source code for our example programs continue to shrink!

The Apache helicopter demo.
FIGURE 9.3 The Apache helicopter demo.

LISTING 9.2 Source code to the Apache helicopter demo utilizing the improved Sprite class.

[code]
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Viewport screen;
SpriteFont font;
Sprite chopper;
Texture2D bullet;
Sprite[] bullets;
float rotation;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = “Content”;
TargetElapsedTime = TimeSpan.FromTicks(333333);
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
screen = GraphicsDevice.Viewport;
//create the font
font = Content.Load<SpriteFont>(“WascoSans”);
//create the helicopter sprite
chopper = new Sprite(Content, spriteBatch);
chopper.Load(“apache”);
chopper.position = new Vector2(120, 240);
chopper.origin = new Vector2(100, 22);
//load bullet image
bullet = Content.Load<Texture2D>(“bullet”);
//create bullet sprites
bullets = new Sprite[10];
for (int n = 0; n < 10; n++)
{
bullets[n] = new Sprite(Content, spriteBatch);
bullets[n].image = bullet;
bullets[n].alive = false;
}
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
ButtonState.Pressed)
this.Exit();
//get state of touch inputs
TouchCollection touchInput = TouchPanel.GetState();
//get rotation
rotation = MathHelper.ToDegrees(chopper.rotation);
//look at all touch points
foreach (TouchLocation touch in touchInput)
{
if (touch.Position.Y < 180) //top of screen
rotation -= 1.0f;
else if (touch.Position.Y > 300) //bottom
rotation += 1.0f;
else
Fire(); //middle
}
//keep rotation in bounds
if (rotation < 0.0f)
rotation = 360.0f – rotation;
else if (rotation > 360.0f)
rotation = 360.0f – rotation;
//save rotation
chopper.rotation = MathHelper.ToRadians(rotation);
//move the bullets
for (int n = 0; n < 10; n++)
{
if (bullets[n].alive)
{
bullets[n].Move();
if (bullets[n].position.X > 800)
bullets[n].alive = false;
}
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
//draw the chopper
chopper.Draw();
//draw the bullets
for (int n = 0; n < 10; n++)
{
if (bullets[n].alive)
bullets[n].Draw();
}
string text = “Angle: “ + rotation.ToString(“N4”);
spriteBatch.DrawString(font, text, new Vector2(200, 440),
Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
void Fire()
{
//look for an unused bullet
for (int n = 0; n < 10; n++)
{
if (!bullets[n].alive)
{
bullets[n].alive = true;
bullets[n].position = chopper.position;
bullets[n].rotation = chopper.rotation;
//calculate angular velocity
float x = (float)Math.Cos(bullets[n].rotation) * 10.0f;
float y = (float)Math.Sin(bullets[n].rotation) * 10.0f;
bullets[n].velocityLinear = new Vector2(x,y);
break;
}
}
}
}
[/code]

“Pointing” a Sprite in the Direction of Movement

You might be thinking, Didn’t we just do this? That’s an astute question! In fact, in the preceding example, the bullet sprites were pointed in a certain direction, and we just added the angular velocity code to make them move in that direction. Now we’re going to do the reverse: Given the direction a sprite is already moving, we want it to “point” in that direction so that it looks right. To demonstrate, we’ll cause the spaceship sprite to “orbit” around a planet and rotate while moving. To more accurately describe this situation, we want a sprite to move and point toward a target.

The trigonometry (“circular”) functions we’ve been using can be considered elementary physics. In a sense, then, we’re working on our own simple physics engine here, which is a bit of a stretch but still compelling!

Have you ever played an RTS (real-time strategy) game in which you can select units, then right-click somewhere on the map, and they would move toward that location? That is the basic way most RTS games work. Along the path, if your units encounter enemy units, they will usually fight or shoot at the enemy, unless you tell them to target a specific enemy unit with a similar right-click on it.

Well, we can do something like that with the concept covered here. Oh, there’s a lot more involved in an RTS game than just moving toward a destination, but at the core of the game is code similar to what we’re going to learn about here.

Calculating the Angle to Target

In the preceding section, we used Math.Cos() and Math.Sin() to calculate the respective X and Y components of velocity (a Vector2). These values could then be used to move a sprite in any desired direction. There’s a related calculation we can perform to do the opposite: Given a sprite’s current location, and a target location, we can calculate the angle needed to get there.

We won’t be using sine and cosine to calculate the angle. Those trig functions are useful only if you know the angle already. The reverse is, knowing where we are already headed, what is that angle?

This concept is powerful in terms of gameplay! Let’s say you do know the angle and use it to calculate velocity, then send a bullet on its way, as we did in the preceding example. Okay, that’s great. But what if you wanted to slow down that sprite, make it stop, and even begin moving in reverse? Not in terms of a bullet, but any sprite, like a spaceship or a car? We can do these things.

This code could be used to cause one sprite to continually point at another sprite. Instead of using a target screen coordinate, use the location of a moving target sprite instead!

The secret behind all this advanced velocity code is another trig function called arctangent. Arctangent is an inverse trigonometric function—specifically, the inverse of tangent, which itself is opposite over adjacent (side a divided by side b), as shown in Figure 9.4. Since I don’t want to get into deriving these trig functions, let’s just jump to the function name in XNA. There are two versions: Math.Atan(), which takes one parameter, and Math.Atan2(), which takes two parameters (double y, double x). This math function returns the angle whose tangent is the quotient of two specified numbers.

A right triangle is the basis for trigonometry. Illustration courtesy of Wikipedia.
FIGURE 9.4 A right triangle is the basis for trigonometry. Illustration courtesy of Wikipedia.

We can’t just pass the position (X and Y) of a target screen coordinate to this function, because the two parameters are actually delta values (the difference between the X and Y values of two points). That’s not as bad as it sounds, as any math major will tell you. Delta is just the difference between the two points: X2 – X1 and Y2 – Y1.

[code]
double deltaX = x2 – x1;
double deltaY = y2 – y1;
[/code]

Having these variables ready, we can calculate the angle toward a target with arctangent in XNA like so:

[code]
double angle = Math.Atan2(deltaY,deltaX);
[/code]

Shuttle Orbit Demo

So now we know how to calculate the angle to a target location. What now? Let’s put this new knowledge to use in another sample program. This one will simulate a spaceship orbiting a planet. While it’s rotating around the planet, the nose of the ship will rotate so it is oriented in the direction of movement. This isn’t exactly moving toward a target on the screen, but given the ship’s previous position and the current position, we can figure out what in direction the nose should be pointing in reverse. Figure 9.5 shows the program running, showing the space shuttle not just rotating around a planet, but rotating to keep the nose pointing forward. This isn’t exactly realistic, because while in orbit, it doesn’t matter which direction a ship or satellite is facing—it will continue to orbit the planet. But it looks cool this way in a game, especially to a younger audience. Listing 9.3 contains the source code for the program.

The spaceship points toward its path while rotating around the planet.
FIGURE 9.5 The spaceship points toward its path while rotating around the planet.

LISTING 9.3 Source code for the orbiting spaceship program.

[code]
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Viewport screen;
SpriteFont font;
Sprite shuttle, planet;
float orbitRadius, orbitAngle;
Vector2 oldPos;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = “Content”;
TargetElapsedTime = TimeSpan.FromTicks(333333);
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
screen = GraphicsDevice.Viewport;
//create the font
font = Content.Load<SpriteFont>(“WascoSans”);
//create the planet sprite
planet = new Sprite(Content, spriteBatch);
planet.Load(“planet1”);
planet.scale = 0.5f;
planet.position = new Vector2(400, 240);
//create the ship sprite
shuttle = new Sprite(Content, spriteBatch);
shuttle.Load(“shuttle”);
shuttle.scale = 0.2f;
orbitRadius = 200.0f;
orbitAngle = 0.0f;
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
ButtonState.Pressed)
this.Exit();
//remember position for orientation
oldPos = shuttle.position;
//keep angle within 0-360 degs
orbitAngle += 1.0f;
if (orbitAngle > 360.0f)
orbitAngle -= 360.0f;
//calculate shuttle position
float x = 400 + (float)Math.Cos(MathHelper.ToRadians(orbitAngle))
* orbitRadius;
float y = 240 + (float)Math.Sin(MathHelper.ToRadians(orbitAngle))
* orbitRadius;
//move the position
shuttle.position = new Vector2(x, y);
//point shuttle’s nose in the right direction
float angle = TargetAngle(shuttle.position, oldPos);
//subtract 180 degrees to reverse the direction
angle = MathHelper.WrapAngle(angle – MathHelper.ToRadians(180));
//adjust for artwork pointing up
angle += MathHelper.ToRadians(90);
//update shuttle’s rotation
shuttle.rotation = angle;
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
planet.Draw();
shuttle.Draw();
string text = “Angle: “ + shuttle.rotation.ToString(“N4”);
spriteBatch.DrawString(font, text, new Vector2(200, 440),
Color.White);
text = “Radius: “ + orbitRadius.ToString(“N0”);
spriteBatch.DrawString(font, text, new Vector2(450, 440),
Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
float TargetAngle(Vector2 p1, Vector2 p2)
{
return TargetAngle(p1.X, p1.Y, p2.X, p2.Y);
}
float TargetAngle(double x1,double y1,double x2,double y2)
{
double deltaX = (x2-x1);
double deltaY = (y2-y1);
return (float)Math.Atan2(deltaY,deltaX);
}
}
[/code]

More Sprite Transforms: Rotation and Scaling

Rotating a Sprite

XNA performs sprite rotation for us. That, in a nutshell, is the gist of this section on the subject. Digging into the trigonometry reveals that rotation calculations are in relation to the origin of the coordinate system at (0,0). It is possible to rotate a sprite the “hard way” by rotating each pixel of the sprite using the trigonometry functions sine() and cosine() the way they were used in the previous hour to rotate planets around the sun in the Solar System Demo. The same basic principle is used to rotate a sprite, but it is done very quickly in XNA thanks to a very fast SpriteBatch. Draw() method that can handle rotation like a cinch.

Since we have full control over how fast a sprite can rotate or revolve in a scene (such as the planets in the Solar System Demo), it is possible to use rotation with just partial circles to give a sprite complex movements by linking arclike movements together in interesting ways.

Rotation Angles

Not too many years ago, artists on a game project would prerotate all sprites in order to preserve quality. Back in the early days of game development, graphics hardware was very limited—which is why game programming in the “early years” was such a black art, because it required extensive knowledge of the hardware in order to eke out every possible CPU cycle to the fullest. Most games for Nintendo’s original NES fit on a 512Kb ROM cartridge. Compare that with games today, in which a single sprite animation stored as a texture might take up a few megabytes.

Figure 8.1 shows an example of the rotation frames often used in early games. The first direction is usually 0 degrees (north), and each successive frame is 45 degrees clockwise, as listed in Table 8.1.

A sprite pointing in the most common eight directions.
FIGURE 8.1 A sprite pointing in the most common eight directions.

 

Sprite Rotation Frame Directions and Angles

As the number of frames increases, so does the quality of the rotated animation, at which point we might end up with the 32 frames of rotation shown in Figure 8.2. Despite the large number of animation frames here in the form of a sprite sheet, this is not truly animation; it is just a prerendered rotation sequence. Each angle in the 32 frames of rotation represents 11 degrees of rotation. In this situation, compared to the 8-frame rotation, with 45 degrees per angle, you can see that there are four times as many frames for much higher quality.

A 32-frame prerotated sprite sheet.
FIGURE 8.2 A 32-frame prerotated sprite sheet.

Rotation actually takes place around the origin, and in trigonometric terms, we’re still working with the Cartesian coordinate system. Figure 8.3 shows an illustration of a point being rotated around the origin a certain interval.

Rotation takes place around the origin (0,0) in Cartesian coordinates.
FIGURE 8.3 Rotation takes place around the origin (0,0) in Cartesian coordinates.

Sprite Class Changes

We have been slowly adding new features to the Sprite class over the past two hours, and now the class will receive some much-needed rotation know-how. First, to differentiate between the old velocity and what we will need for rotational velocity, I have renamed the old velocity variable to velocityLinear, which is still a Vector2. A new variable has been added to the Sprite class called velocityAngular. A support method has been added, called Rotate(). Assuming that the velocityAngular property variable is set to a value other than zero, the Rotate() method will cause the sprite to rotate automatically (as the Move() method did for linear velocity in the preceding hour). Here is our new Sprite class:

LISTING 8.1 Source code for the even further improved Sprite class!

[code]
public class Sprite
{
private ContentManager p_content;
private SpriteBatch p_spriteBatch;
public Texture2D image;
public Vector2 position;
public Vector2 velocityLinear;
public Color color;
public float rotation;
public float velocityAngular;
public Sprite(ContentManager content, SpriteBatch spriteBatch)
{
p_content = content;
p_spriteBatch = spriteBatch;
image = null;
position = Vector2.Zero;
velocityLinear = Vector2.Zero;
color = Color.White;
rotation = 0.0f;
velocityAngular = 0.0f;
}
public bool Load(string assetName)
{
try
{
image = p_content.Load<Texture2D>(assetName);
}
catch (Exception) { return false; }
return true;
}
public void Draw()
{
p_spriteBatch.Draw(image, position, color);
}
public void Move()
{
position += velocityLinear;
}
public void Rotate()
{
rotation += velocityAngular;
if (rotation > Math.PI * 2)
rotation = 0.0f;
else if (rotation < 0.0f)
rotation = (float)Math.PI * 2;
}
}
[/code]

Drawing with Rotation

Besides the rotation code, some huge changes have had to be made to the Draw() method to accommodate rotation. We’re using the seventh and final overload of the SpriteBatch.Draw() method, which looks like this:

[code]
public void Draw( Texture2D texture,
Vector2 position,
Rectangle? sourceRectangle,
Color color,
float rotation,
Vector2 origin,
float scale,
SpriteEffects effects,
float layerDepth );
[/code]

There are nine parameters in this version of Draw(), which is capable of servicing all of our sprite needs for the next few chapters. But first things first: the rotation factor. The fifth parameter requires the rotation value as a float, so this is where we will pass the new rotation property (added to the preceding class).

For rotation to work correctly, we need to pass the right value for the origin parameter. The origin defines the point at which rotation will occur. If we pass Vector2.Zero, as in the sample call

[code]
Vector2 origin = new Vector2(0, 0);
float scale = 1.0f;
p_spriteBatch.Draw(image, position, null, color, rotation, origin,
scale, SpriteEffects.None, 0.0f);
[/code]

then the origin is set to the upper-left corner of the sprite (0,0), causing the result to look as shown in Figure 8.4, with the image rotation around the upper left.

The origin parameter defines the point at which rotation takes place.
FIGURE 8.4 The origin parameter defines the point at which rotation takes place.

We need to calculate the center of the image so that it will rotate correctly from the center of the image rather than the upper-left corner. This can be calculated easily enough using the width and height of the image:

[code]
Vector2 origin = new Vector2(image.Width/2,image.Height/2);
float scale = 1.0f;
p_spriteBatch.Draw(image, position, null, color, rotation,
origin, scale, SpriteEffects.None, 0.0f);
[/code]

This is the new and final code for Draw() at this point. We will also make minor changes to it again in the next section on scaling. The new version is shown in Figure 8.5.

Changing the origin to the center of the sprite’s image affects its position as well as rotation center—the origin becomes the focus for the position, even if the sprite is not rotated (with rotation set to 0.0f).

The sprite now rotates correctly, with the origin set to the center of the image.
FIGURE 8.5 The sprite now rotates correctly, with the origin set to the center of the image.

Sprite Rotation Demo

For this rotation demo, we will use user input to rotate the sprite either right or left, depending on where the screen is touched, on the right side or left side of the screen. The second screenshot of this demo is shown in Figure 8.5, referenced earlier. Now the sprite is centered on the screen and rotating from the center. Listing 8.2 contains the source code for the test program.

LISTING 8.2 Test program for the new rotation capability of our Sprite class.

[code]
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Viewport screen;
SpriteFont font;
Sprite ship;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = “Content”;
TargetElapsedTime = TimeSpan.FromTicks(333333);
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
screen = GraphicsDevice.Viewport;
//create the font
font = Content.Load<SpriteFont>(“WascoSans”);
//create the ship sprite
ship = new Sprite(Content, spriteBatch);
ship.Load(“ship”);
ship.position = new Vector2(screen.Width/2, screen.Height/2);
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
ButtonState.Pressed)
this.Exit();
//get state of touch inputs
TouchCollection touchInput = TouchPanel.GetState();
//look at all touch points (usually 1)
foreach (TouchLocation touch in touchInput)
{
if (touch.Position.X > screen.Width / 2)
ship.velocityAngular = 0.05f;
else if (touch.Position.X < screen.Width / 2)
ship.velocityAngular = -0.05f;
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
ship.Rotate();
ship.Draw();
string text = “Rotation: “ + ship.rotation.ToString(“N4”);
Vector2 size = font.MeasureString(text);
float x = (screen.Width – size.X) / 2;
spriteBatch.DrawString(font, text, new Vector2(x, 440), Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
}
[/code]

Scaling a Sprite

Sprite scaling is also handled by SpriteBatch.Draw(), but it doesn’t hurt to learn a little bit about how scaling happens. Like rotation, scaling occurs in relation to the origin of the Cartesian coordinate system, used for many trigonometry functions, although the most common we use in game programming are sine() and cosine(). Figure 8.6 shows a before-and-after result of an object (represented with just two points, which might be a line). The points are scaled by a factor of 0.5 (50%), resulting in the new positions shown.

Scaling also occurs in relation to the origin (0,0).
FIGURE 8.6 Scaling also occurs in relation to the origin (0,0).

In our previous example on rotation, the scale factor was hard-coded in the Sprite.Draw() method like so:

[code]
public void Draw()
{
Vector2 origin = new Vector2(image.Width / 2, image.Height / 2);
float scale = 1.0f;
p_spriteBatch.Draw(image, position, null, color, rotation,
origin, scale, SpriteEffects.None, 0.0f);
}
[/code]

We need to delete this out of Draw() and add a new variable to the class’s public declaration, with a new initializer in the constructor.

[code]
private ContentManager p_content;
private SpriteBatch p_spriteBatch;
public Texture2D image;
public Vector2 position;
public Vector2 velocityLinear;
public Color color;
public float rotation;
public float velocityAngular;
public Vector2 scale;
public Sprite(ContentManager content, SpriteBatch spriteBatch)
{
p_content = content;
p_spriteBatch = spriteBatch;
image = null;
position = Vector2.Zero;
velocityLinear = Vector2.Zero;
color = Color.White;
rotation = 0.0f;
velocityAngular = 0.0f;
scale = new Vector2(1.0f);
}
[/code]

The scale variable is no longer just a mere float, but has been upgraded to a Vector2. This will allow us to scale the sprite horizontally and vertically at different rates, if desired. This can be annoying, however. Every time you need to change the scale value, a Vector2 has to be set. I would much rather just set the scale as a float by default, and have the option of setting the scale individually for the horizontal or vertical axes. This is not because setting a Vector2 is time-consuming, but because the scale will most often be uniform on both axes. We want the most often-used properties and methods to reflect the most common need, not unusual needs.

We need to rename the scale variable to scaleV, and add a new property called scale. After making the modification, scaleV is initialized in the constructor, which sets both the X and the Y values. Anytime Sprite.scale is changed, both the X and the Y properties are changed.

[code]
scaleV = new Vector2(1.0f);
[/code]

The new scale property looks like this:

[code]
public float scale
{
get { return scaleV.X; }
set {
scaleV.X = value;
scaleV.Y = value;
}
}
[/code]

In the Sprite Scaling Demo coming up shortly, we can scale the sprite very large or very small by simply modifying the Sprite.Draw() method to use Sprite.scaleV (which is a Vector2). It is also now possible to scale the width and height of a sprite. See Figure 8.7.

We’re not going to add a scale velocity because that is so rarely needed that if the need does arise, it can be done with a global variable outside of the Sprite class. Or you can go ahead and add that capability to your version of the Sprite class if you want! In fact, let’s see the complete new version of the Sprite class (in Listing 8.3), just to be thorough, since that was a lot of new information to sort out.

Scaling a sprite up to 5× the normal size.
FIGURE 8.7 Scaling a sprite up to 5× the normal size.

LISTING 8.3 Source code for the updated Sprite class with new scaling abilities.

[code]
public class Sprite
{
private ContentManager p_content;
private SpriteBatch p_spriteBatch;
public Texture2D image;
public Vector2 position;
public Vector2 velocityLinear;
public Color color;
public float rotation;
public float velocityAngular;
public Vector2 scaleV;
public Sprite(ContentManager content, SpriteBatch spriteBatch)
{
p_content = content;
p_spriteBatch = spriteBatch;
image = null;
position = Vector2.Zero;
velocityLinear = Vector2.Zero;
color = Color.White;
rotation = 0.0f;
velocityAngular = 0.0f;
scaleV = new Vector2(1.0f);
}
public float scale
{
get { return scaleV.X; }
set {
scaleV.X = value;
scaleV.Y = value;
}
}
public bool Load(string assetName)
{
try
{
image = p_content.Load<Texture2D>(assetName);
}
catch (Exception) { return false; }
return true;
}
public void Draw()
{
Vector2 origin = new Vector2(image.Width / 2, image.Height / 2);
p_spriteBatch.Draw(image, position, null, color, rotation,
origin, scaleV, SpriteEffects.None, 0.0f);
}
public void Move()
{
position += velocityLinear;
}
public void Rotate()
{
rotation += velocityAngular;
if (rotation > Math.PI * 2)
rotation = 0.0f;
else if (rotation < 0.0f)
rotation = (float)Math.PI * 2;
}
}
[/code]

The complete Sprite Scaling Demo is found in Listing 8.4. A view of the program with the scale set very small is shown in Figure 8.8.

Scaling a sprite down to 10% of normal size.
FIGURE 8.8 Scaling a sprite down to 10% of normal size.

LISTING 8.4 Source code for the updated Sprite class with new scaling abilities.

[code]
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Viewport screen;
SpriteFont font;
Sprite ship;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = “Content”;
TargetElapsedTime = TimeSpan.FromTicks(333333);
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
screen = GraphicsDevice.Viewport;
//create the font
font = Content.Load<SpriteFont>(“WascoSans”);
//create the ship sprite
ship = new Sprite(Content, spriteBatch);
ship.Load(“ship”);
ship.position = new Vector2(screen.Width / 2, screen.Height / 2);
//rotate the sprite to the right
ship.rotation = MathHelper.ToRadians(90.0f);
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
ButtonState.Pressed)
this.Exit();
//get state of touch inputs
TouchCollection touchInput = TouchPanel.GetState();
foreach (TouchLocation touch in touchInput)
{
if (touch.Position.X > screen.Width / 2)
ship.scale += 0.05f;
else if (touch.Position.X < screen.Width / 2)
ship.scale -= 0.05f;
}
//keep the scaling within limits
if (ship.scale < 0.10f)
ship.scale = 0.10f;
else if (ship.scale > 5.0f)
ship.scale = 5.0f;
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
ship.Rotate();
ship.Draw();
string text = “Scale: “ + ship.scale.ToString(“N4”);
Vector2 size = font.MeasureString(text);
float x = (screen.Width – size.X) / 2;
spriteBatch.DrawString(font, text, new Vector2(x, 440),
Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
}
[/code]

That concludes our study of the three basic transforms: translation, rotation, and scaling. These techniques, along with SpriteBatch and some good math formulas, can produce any type of special effect or animated movement that we need for a game, on small game sprites, or even whole backgrounds (give that a try!).

Transforming Sprites

Translating (Moving) a Sprite

Sprite translation, or movement, can be done directly or indirectly. The direct approach involves writing code to move a sprite specifically to one location or another. The indirect approach involves setting up properties and variables that cause the sprite to move on its own based on those properties—we only write code that updates and draws the sprite, but the properties determine the movement. Although it is possible to cause a sprite to move in an arc or to follow a curve, that is not the type of movement we will be discussing in this hour. Instead, we’ll see how to move a sprite a short distance per frame (and remember, although an XNA game normally draws at 60 fps, a WP7 game runs at only 30). The “curves” our sprites will follow are actually complete circles, as you’ll see in the Solar System Demo coming up.

The math calculations we will perform in this hour to translate (move) a sprite are based on the 2D Cartesian coordinate system, represented in Figure 7.1. There are two axes, X and Y, which is why we refer to it as a “2D” system. Both the X and the Y axes start at the center, which is called the origin, residing at position (0,0). The X axis goes left (-) and right (+) from the origin. The Y axis goes down (-) and up (+) from the origin.

2D Cartesian coordinates are composed of two axes, X and Y.
FIGURE 7.1 2D Cartesian coordinates are composed of two axes, X and Y.

When making trigonometric calculations to transform a sprite’s position, the Y value must be reversed. If you look at the figure, you can see why! On a computer screen, as well as the WP7 screen, the origin (0,0) is located at the upper left, with X increasing right and Y increasing down. Since the Cartesian system represents Y positive going up, any Y coordinates we calculate must be reversed, or negated. This can be done by multiplying every Y result by -1.

Moving the position of a sprite needn’t be overly complicated. This is the easiest of the three transforms that we can calculate, because the calculation can just be skipped and the point can be moved to the desired target position (at this early stage). See Figure 7.2.

Translation (movement) from one point to another.
FIGURE 7.2 Translation (movement) from one point to another.

Using Velocity as Movement Over Time

Velocity can be calculated as distance over time. Mathematically, the formula is V = D / T. This calculation is more useful after a certain distance has been traveled in a certain amount of time, to figure out the average speed. But it is not as useful when it comes to moving sprites across the screen. For that purpose, we need to set a certain velocity ahead of time, while the distance and time variables are not as important. Let’s add a new velocity property to our ever-evolving Sprite class. Since velocity is a public property, we can use it outside of the class. Just for fun, let’s add a new method to the class to move the sprite based on velocity, as shown in Listing 7.1.

LISTING 7.1 Adding a new property to our Sprite class.

[code]
public class Sprite
{
private ContentManager p_content;
private SpriteBatch p_spriteBatch;
public Texture2D image;
public Vector2 position;
public Vector2 velocity;
public Color color;
public Sprite(ContentManager content, SpriteBatch spriteBatch)
{
p_content = content;
p_spriteBatch = spriteBatch;
image = null;
position = Vector2.Zero;
color = Color.White;
}
public bool Load(string assetName)
{
try
{
image = p_content.Load<Texture2D>(assetName);
}
catch (Exception) { return false; }
return true;
}
public void Draw()
{
p_spriteBatch.Draw(image, position, color);
}
public void Move()
{
position += velocity;
}
}
[/code]

The Move() method does something very simple—adds the velocity to the sprite’s position. The position will be rounded to the nearest X,Y pixel coordinate on the screen, but the velocity definitely will not be represented with whole numbers. Considering a game running at 60 fps, the velocity will be a fraction of a pixel, such as 0.01 (1/100th or 1% of a pixel). At this rate, assuming that the game is running at 60 fps, the sprite will move one pixel in 6/10ths of a second. This is equal to roughly two pixels per second, because 0.01 times 60 is 0.60. See Figure 7.3.

Sprite velocity in relation to frame rate.
FIGURE 7.3 Sprite velocity in relation to frame rate.

An XNA WP7 program runs at only 30 fps, so expect a difference in performance compared to Windows and Xbox 360 projects (which usually run at 60 fps).

We can put this simple addition to the Sprite class to the test. Included with this hour is an example called Sprite Movement. Open the project and take it for a spin. You’ll see that a funny-looking spaceship is moving over a background image, as shown in Figure 7.4, wrapping from right to left as it reaches the edge of the screen. Two asset files are required to run this project: craters800x480.tga and fatship256.tga, also included with the project. The source code is found in Listing 7.2.

Testing velocity with sprite motion.
FIGURE 7.4 Testing velocity with sprite motion.

LISTING 7.2 Test project for the new and improved Sprite class with movement capability.

[code]
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Sprite background;
Sprite ship;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = “Content”;
TargetElapsedTime = TimeSpan.FromTicks(333333);
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
background = new Sprite(Content, spriteBatch);
background.Load(“craters800x480”);
ship = new Sprite(Content, spriteBatch);
ship.Load(“fatship256”);
ship.position = new Vector2(0, 240 – ship.image.Height / 2);
ship.velocity = new Vector2(8.0f, 4.0f);
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
ButtonState.Pressed)
this.Exit();
//move the sprite
ship.Move();
//rebound from top and bottom
if (ship.position.Y < -70 | ship.position.Y > 480 – 185)
{
ship.velocity.Y *= -1;
}
//wrap around right to left
if (ship.position.X > 800)
ship.position.X = -256;
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
//draw the background
background.Draw();
//draw the sprite
ship.Draw();
spriteBatch.End();
base.Draw(gameTime);
}
}
[/code]

Moving Sprites in a Circle

We’re going to use the sun and planet artwork introduced in the preceding hour to make a simple solar system simulation. The calculations will not be meaningful to an astronomy fan; we just want the planet sprites to rotate around the sun, with no relation to our actual solar system. For this project, we need several planet bitmaps with alpha channel transparency, but because the background will just be black, if the planets have a black background that will suffice.

The sun needs to be positioned at the center of the screen, with the planets moving around it in concentric circular orbits. To make a sprite move in a circle, we need to use basic trigonometry—sine and cosine functions.

To make the code in our Solar System Demo a little cleaner, I’ve created a quick subclass of Sprite called Planet that adds three properties: radius, angle, and velocity. Notice that the Planet constructor, Planet(), calls the Sprite constructor using the base() call, passing ContentManager and SpriteBatch as the two required parameters to Sprite. The syntax for inheritance from a base class is shown in the class definition that follows. This is very helpful as we can count on Sprite to initialize itself.

[code]
public class Planet : Sprite
{
public float radius;
public float angle;
public float velocity;
public Planet(ContentManager content, SpriteBatch spriteBatch)
: base(content, spriteBatch)
{
radius = 0.0f;
angle = 0.0f;
velocity = 0.0f;
}
}
[/code]

The rest of the source code for the Solar System Demo is up next. There are just four planets, but it is still efficient to use a List container to store them rather than using global variables. We’re slowly seeing a little more code added to our WP7 “programmer’s toolbox” with each new hour and each new example, which is a good thing. There’s a lot more code in the initialization part of ContentLoad() than in either Update() or Draw(), but that is to be expected in a program with so few sprites on the screen. Figure 7.5 shows the program running, and the source code is found in Listing 7.3.

Solar System demo.
FIGURE 7.5 Solar System demo.

LISTING 7.3 The Solar System Demo project code.

[code]
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Viewport screen;
Sprite sun;
List<Planet> planets;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = “Content”;
TargetElapsedTime = TimeSpan.FromTicks(333333);
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
screen = GraphicsDevice.Viewport;
//create sun sprite
sun = new Sprite(Content, spriteBatch);
sun.Load(“sun”);
sun.position = new Vector2((screen.Width – sun.image.Width)/2,
(screen.Height – sun.image.Height)/2);
//create planet sprites
planets = new List<Planet>();
//add 1st planet
Planet planet = new Planet(Content, spriteBatch);
planet.Load(“planet1”);
planet.velocity = 0.2f;
planet.radius = 100;
planet.angle = MathHelper.ToRadians(30);
planets.Add(planet);
//add 2nd planet
planet = new Planet(Content, spriteBatch);
planet.Load(“planet2”);
planet.velocity = 0.16f;
planet.radius = 140;
planet.angle = MathHelper.ToRadians(60);
planets.Add(planet);
//add 3rd planet
planet = new Planet(Content, spriteBatch);
planet.Load(“planet3”);
planet.velocity = 0.06f;
planet.radius = 195;
planet.angle = MathHelper.ToRadians(90);
planets.Add(planet);
//add 4th planet
planet = new Planet(Content, spriteBatch);
planet.Load(“planet4”);
planet.velocity = 0.01f;
planet.radius = 260;
planet.angle = MathHelper.ToRadians(120);
planets.Add(planet);
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
ButtonState.Pressed)
this.Exit();
//update planet orbits
foreach (Planet planet in planets)
{
//update planet’s orbit position
planet.angle += planet.velocity;
//calculate position around sun with sine and cosine
float orbitx = (float)(Math.Cos(planet.angle) * planet.radius);
float orbity = (float)(Math.Sin(planet.angle) * planet.radius);
//center the planet so it orbits around the sun
float x = (screen.Width – planet.image.Width) / 2 + orbitx;
float y = (screen.Height – planet.image.Height) / 2 + orbity;
//save new position
planet.position = new Vector2(x, y);
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
//draw the sun
sun.Draw();
//draw the planets
foreach (Planet planet in planets)
{
planet.Draw();
}
spriteBatch.End();
base.Draw(gameTime);
}
}
[/code]

Did you find the code in LoadContent() a bit disconcerting, given that the same planet variable was used over and over again for each planet sprite? The important thing is that each one was added to the planets list, which became a “group” of sprites. You should do this whenever possible with related sprites, because it greatly simplifies everything about your code!

We can also use these sine() and cosine() functions to fire projectiles (such as a missile) from a launcher toward any target at any position on the screen.

The Sprite Movement Demo showed how a sprite can be moved automatically based on its velocity property, although we still have to step in and manage the event when the sprite reaches a screen edge. Although rotation is a subject of the next hour, we did explore moving a sprite around in a circle. This is different from sprite rotation, which involves actually changing the image. In our Solar System Demo, the sprite images were not rotated; they remained in a fixed rotation but followed a circular path around the sun using trigonometry.

Treating Bitmaps as Sprites

Bringing Bitmaps to Life

A sprite is a bitmap with benefits. XNA provides the SpriteBatch class to draw bitmaps with the SpriteBatch.Draw() method, of which there are several overloaded variants. But despite the name, SpriteBatch does not give us “sprite” capabilities from the gameplay perspective. SpriteBatch is a rendering class, used solely for drawing, not “managing” game entities traditionally known as “sprites.” The difference might seem a subtle one, but it’s actually quite a distinction. SpriteBatch might have been a somewhat incorrect name for the class. The “Batch” part of the name refers to the way in which “bitmaps” (not sprites) are drawn—in a batch. That is, all bitmap drawing via SpriteBatch.Draw() is put into a queue and then all the drawing is done quickly when SpriteBatch.End() is called. It’s faster to perform many draw calls at once in this manner, since the video card is going to be switching state only once. Every time SpriteBatch.Begin() and SpriteBatch.End() are called, that involves a state change (which is very slow in terms of rendering). The fewer state changes that happen, the better!

SpriteBatch.Draw() can handle animation, rotation, scaling, and translation (movement). But, without properties, we have to write custom code to do all of these things with just global variables. It can get tedious! We will see how tedious by writing an example using all global variables. Then, for comparison, we’ll write a simple Sprite class and convert the program. This is not just an illustration of how useful object-oriented programming can be (which is true) but to show why we need a Sprite class for gameplay.

SpriteBatch.Draw() works fine. We don’t need an alternative replacement because it can do anything we need. But the code to draw sprites is very specific. If we don’t want to manually draw every character, or vehicle, or avatar in the game, we have to automate the process in some manner. The key to making this work is via properties. A property is a trait or an attribute that partially describes something. In the case of a person, one property would be gender (male or female); other properties include race, height, weight, and age. No single property fully describes a person, but when all (or most) of the properties are considered, it gives you a pretty good idea of what that person looks like.

The simplest and most common property for a sprite is its position on the screen. In the previous hour, we used a variable called Vector2 shipPos to represent the position of the bitmap, and a variable called Texture2D shipImage to represent the image. These two variables were properties for a game object—a spaceship to be used in a sci-fi game. Wouldn’t it be easier to manage both of these properties inside a Sprite class? Before we do that, let’s see whether it’s really that big of a deal to keep track of properties with global variables.

Drawing Lots of Bitmaps

Let’s create a short example. Now, working with just one bitmap is a piece of cake, because there’s only one call to SpriteBatch.Draw(), only one position variable, and only one image variable. When things get messy is when about five or more bitmaps need to be manipulated and drawn. Up to that point, managing variables for four or so images isn’t so bad, but as the number climbs, the amount of manual code grows and becomes unwieldy (like a giant two-handed sword).

[code]
Vector2 position1, position2, position3, position4, position5;
Texture2D image1, image2, image3, image4, image5;
[/code]

It’s not just declaring and using the variables that can be a problem. It’s the ability to use any more than this practically in a game’s sources. But let’s give it a try anyway for the sake of the argument. Figure 6.1 shows the output for this short program, which is a prototype for a solar system simulation we’ll be creating in this hour. The source code is found in Listing 6.1.

The Many Bitmaps Demo program.
FIGURE 6.1 The Many Bitmaps Demo program.

LISTING 6.1 Source code for the Many Bitmaps Demo program, a precursor to a larger project.

[code]
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Vector2 position1, position2, position3, position4, position5;
Texture2D image1, image2, image3, image4, image5;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = “Content”;
TargetElapsedTime = TimeSpan.FromTicks(333333);
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
image1 = Content.Load<Texture2D>(“sun”);
image2 = Content.Load<Texture2D>(“planet1”);
image3 = Content.Load<Texture2D>(“planet3”);
image4 = Content.Load<Texture2D>(“planet2”);
image5 = Content.Load<Texture2D>(“planet4”);
position1 = new Vector2(100, 240-64);
position2 = new Vector2(300, 240-32);
position3 = new Vector2(400, 240-32);
position4 = new Vector2(500, 240-16);
position5 = new Vector2(600, 240-16);
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
ButtonState.Pressed)
this.Exit();
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
spriteBatch.Draw(image1, position1, Color.White);
spriteBatch.Draw(image2, position2, Color.White);
spriteBatch.Draw(image3, position3, Color.White);
spriteBatch.Draw(image4, position4, Color.White);
spriteBatch.Draw(image5, position5, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
}
[/code]

Running into Limits with Global Variables

The Many Bitmaps Demo program wasn’t too difficult to deal with, was it? I mean, there were only five bitmaps to draw, so we needed five position variables. But what if there were 20, or 50, or 100? With so many game objects, it would be impossible to manage them all with global variables. Furthermore, that’s bad programming style when there are better ways to do it. Obviously, I’m talking about arrays and collections. But not only is it a quantity issue with regard to the global variables, but if we want to add another property to each object, we’re talking about adding another 20, 50, or 100 variables for that new property!

Let’s rewrite the program using an array. Later in the hour, we’ll work with a list, which is a container class, but for this next step, an array is a little easier to follow. Here is a new version using arrays. The new source code is found in Listing 6.2.

LISTING 6.2 New source code for the program rewritten to more efficiently store the planet bitmaps and vectors in arrays.

[code]
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D[] images;
Vector2[] positions;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = “Content”;
TargetElapsedTime = TimeSpan.FromTicks(333333);
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
images = new Texture2D[5];
images[0] = Content.Load<Texture2D>(“sun”);
images[1] = Content.Load<Texture2D>(“planet1”);
images[2] = Content.Load<Texture2D>(“planet3”);
images[3] = Content.Load<Texture2D>(“planet2”);
images[4] = Content.Load<Texture2D>(“planet4”);
positions = new Vector2[5];
positions[0] = new Vector2(100, 240-64);
positions[1] = new Vector2(300, 240-32);
positions[2] = new Vector2(400, 240-32);
positions[3] = new Vector2(500, 240-16);
positions[4] = new Vector2(600, 240-16);
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
ButtonState.Pressed)
this.Exit();
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
for (int n = 0; n < 5; n++)
{
spriteBatch.Draw(images[n], positions[n], Color.White);
}
spriteBatch.End();
base.Draw(gameTime);
}
}
[/code]

As you examine the code in this version of the program, what stands out? I notice that in ContentLoad(), the initialization code is actually a bit more complicated than it was previously, but the code in Draw() is shorter. We’re not counting lines of code, but in Draw(), the for loop could accommodate 100 or 1,000 objects with the same amount of code. This is the most significant difference between this and the previous program—we now can handle any arbitrary number of objects.

We could shorten the code in LoadContent() even further by building the filename for each planet. The key is to make the asset names consistent. So, if we rename ”sun” to ”planet0”, then loading the assets becomes a simple for loop. Here is an improvement:

[code]
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
images = new Texture2D[5];
positions = new Vector2[5];
for (int n = 0; n < 5; n++)
{
string filename = “planet” + n.ToString();
images[n] = Content.Load<Texture2D>(filename);
}
positions[0] = new Vector2(100, 240 – 64);
positions[1] = new Vector2(300, 240 – 32);
positions[2] = new Vector2(400, 240 – 32);
positions[3] = new Vector2(500, 240 – 16);
positions[4] = new Vector2(600, 240 – 16);
}
[/code]

The positions array must still be set manually due to the differences in the sizes of the planet images. But I think we could automate that as well by looking at the width and height of each image. The point is not just to make the code shorter, but to find ways to improve the code, make it more versatile, more reusable, and easier to modify. Using a consistent naming convention for asset files will go a long way toward that end.

Creating a Simple Sprite Class

Now let’s experiment with some code that actually does something interesting besides drawing fixed images. We’ll start by creating a simple class to encapsulate a sprite, and then add some features to make the Sprite class useful. This will be a hands-on section where we build the Sprite class in stages. If you are an experienced programmer, you may skip this section.

Creating the Sprite Class

Let’s begin with a new project. The project type will be, as usual, Windows Phone (4.0), and the name is Sprite Demo. In the Game1.cs file, which is the main source code file for the game, we’re going to add the new Sprite class to the top of the file, above the Game1 class. After a while, the Sprite class can be moved into its own file called Sprite.cs. I find this more convenient while working on a new class. See Listing 6.3 for the complete source code.

Classes do not need to be stored in unique source code files; that’s a practice to keep a large project tidy and easier to maintain. But it is acceptable (and often practical) to define a new class inside an existing file. This is especially true when several classes are closely related and you want to better organize the project. The best practice, though, for large classes, is to define each class in its own file.

LISTING 6.3 Source code for the new project with included Sprite class.

[code]
public class Sprite
{
public Texture2D image;
public Vector2 position;
public Color color;
}
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Viewport viewport;
Sprite sun;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = “Content”;
TargetElapsedTime = TimeSpan.FromTicks(333333);
}protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
//get screen dimensions
viewport = GraphicsDevice.Viewport;
//create sun sprite
sun = new Sprite();
sun.image = Content.Load<Texture2D>(“sun”);
//center sun sprite on screen
float x = (viewport.Width – sun.image.Width) / 2;
float y = (viewport.Height – sun.image.Height) / 2;
sun.position = new Vector2(x,y);
//set color
sun.color = Color.White;
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
ButtonState.Pressed)
this.Exit();
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
//draw the sun
spriteBatch.Draw(sun.image, sun.position, sun.color);
spriteBatch.End();
base.Draw(gameTime);
}
}
[/code]

The code listings in this book omit the using statements at the beginning of every XNA project, as well as the namespace line and surrounding brackets, to focus on just the functional part of a program, only when that code is generated by Visual Studio. You should assume that those lines are required to compile every example listed herein.

Scope and Clarity

This is how most classes begin life, with just some public properties that could have been equally well defined in a struct. In fact, if you just want to create a quick container for a few variables and don’t want to deal with a class, go ahead and do it. Structures (defined as struct) can even have constructor methods to initialize their properties, as well as normal methods. But in a struct, there is no scope; everything is public. In contrast, everything in a class is private by default. This would work, for example:

[code]
struct Sprite
{
Texture2D image;
Vector2 position;
Color color;
}
[/code]

But one limitation with a struct is room for growth; with a class, we can make changes to scope (which means changing whether individual properties and methods are visible outside of the class). At this simplistic level, there’s very little difference between a class and a struct. Some programmers differentiate between them by using structs only as property containers, with no methods, reserving methods only for defined classes. It’s ultimately up to you!

An OOP (object-oriented programming) “purist” would demand that our image, position, and color properties be defined with private scope, and accessed via property methods. The difference between property variables and property methods is fuzzy in C#, whereas they are very clear-cut in a more highly precise language such as C++. Let’s see what the class will look like when the three variables (image, position, and color) are converted into private properties with public accessor methods.

[code]
public class Sprite2 //”good” OOP version
{
Texture2D p_image;
Vector2 p_position;
Color p_color;
public Texture2D image
{
get { return p_image; }
set { p_image = value; }
}
public Vector2 position
{
get { return p_position; }
set { p_position = value; }
}
public Color color
{
get { return p_color; }
set { p_color = value; }
}
}
[/code]

In general, I prefer to not hide property variables (such as public Texture2D image in the Sprite class), because it just requires extra code to access the property later. This is, again, a matter of preference, and might be dependent on the coding standards of your team or employer. If it’s up to you, just focus on writing clean, tight code, and don’t worry about making your code “OOP safe” for others.

Initializing the Sprite Class with a Constructor

Compared to the original Sprite class defined in Game1.cs, what do you think of the “safe” version (renamed to Sprite2 to avoid confusion)? The three variable names have had “p_” added (to reflect that they are now private in scope), and now in their place are three “properly defined” properties. Each property now has an accessor method (get) and a mutator method (set). If you prefer this more highly structured form of object-oriented C#, I encourage you to continue doing what works best for you. But for the sake of clarity, I will use the original version of Sprite with the simpler public access property variables.

Let’s give the Sprite class more capabilities. Currently, it’s just a container for three variables. A constructor is a method that runs automatically when an object is created at runtime with the new operator, for example:

[code]
Sprite sun = new Sprite();
[/code]

The term Sprite(), with the parentheses, denotes a method—the default method since it requires no parameters. Here is ours:

[code]
public class Sprite
{
public Texture2D image;
public Vector2 position;
public Color color;
public Sprite()
{
image = null;
position = Vector2.Zero;
color = Color.White;
}
}
[/code]

Here, we have a new constructor that initializes the class’s properties to some initial values. This is meant to avoid problems later if one forgets to initialize them manually. In our program now, since color is automatically set to Color.White, we no longer need to manually set it, which cleans up the code in LoadContent() a bit.

[code]
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
//get screen dimensions
viewport = GraphicsDevice.Viewport;
//create sun sprite
sun = new Sprite();
sun.image = Content.Load<Texture2D>(“sun”);
//center sun sprite on screen
float x = (viewport.Width – sun.image.Width) / 2;
float y = (viewport.Height – sun.image.Height) / 2;
sun.position = new Vector2(x,y);
}
[/code]

Writing Reusable Code with Abstraction

A usual goal for an important base game class like Sprite is to abstract the XNA code, at least somewhat, to make the class stand on its own as much as possible. This becomes a priority when you find yourself writing games on several platforms. Within the XNA family, we have Windows, Xbox 360, and Windows Phone. But on a larger scale, it’s fairly common to port games to other systems. After you have rewritten your Sprite class a few times for different platforms (and even languages, believe it or not!), you begin to see similarities among the different systems, and begin to take those similarities into account when writing game classes.

There are two aspects that I want to abstract in the Sprite class. First, there’s loading the image. This occurs in LoadContent() when we simply expose the image property to Content.Load(). Second, there’s drawing the sprite. This occurs in Draw(), also when we expose the image property. To properly abstract the class away from XNA, we need our own Load() and Draw() methods within Sprite itself. To do this, the Sprite class must have access to both ContentManager and SpriteBatch. We can do this by passing those necessary runtime objects to the Sprite class constructor. Listing 6.4 contains the new source code for the Sprite class.

LISTING 6.4 Source code for the expanded Sprite class.

[code]
public class Sprite
{
private ContentManager p_content;
private SpriteBatch p_spriteBatch;
public Texture2D image;
public Vector2 position;
public Color color;
public Sprite(ContentManager content, SpriteBatch spriteBatch)
{
p_content = content;
p_spriteBatch = spriteBatch;
image = null;
position = Vector2.Zero;
color = Color.White;
}
public bool Load(string assetName)
{
try
{
image = p_content.Load<Texture2D>(assetName);
}
catch (Exception) { return false; }
return true;
}
public void Draw()
{
p_spriteBatch.Draw(image, position, color);
}
}
[/code]

Putting our new changes into action (in Listing 6.5) reveals some very clean-looking code in LoadContent() and Draw(), with the output shown in Figure 6.2.

LISTING 6.5 Modifications to the project to support the new Sprite features.

[code]
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
viewport = GraphicsDevice.Viewport;
//create sun sprite
sun = new Sprite(Content, spriteBatch);
sun.Load(“sun”);
//center sun sprite on screen
float x = (viewport.Width – sun.image.Width) / 2;
float y = (viewport.Height – sun.image.Height) / 2;
sun.position = new Vector2(x, y);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
sun.Draw();
spriteBatch.End();
base.Draw(gameTime);
}
[/code]

Demonstrating the Sprite class.
FIGURE 6.2 Demonstrating the Sprite class.

Error Handling

The Sprite.Load() method has error handling built in via a try…catch block. Inside the try block is a call to Content.Load(). If the passed asset name is not found, XNA generates an exception error, as shown in Figure 6.3. We don’t want the end user to ever see an exception error, and in a very large game project, it is fairly common for asset files to be renamed and generate errors like this—it’s all part of the development process to track down and fix such common bugs.

To assist, the code in Sprite.Load() returns false if an asset is not found—rather than crashing with an exception error. The problem is, we can’t exit on an error condition from within LoadContent(); XNA is just not in a state that will allow the program to terminate at that point. What we need to do is set a flag and look for it after LoadContent() is finished running.

I have an idea. What if we add a feature to display an optional error message in a pre-shutdown process in the game loop? All it needs to do is check for this error state and then print whatever is in the global errorMessage variable. The user would then read the message and manually shut down the program by closing the window. Let’s just try it out; this won’t be a permanent fixture in future chapters, but you may continue to use it if you want to. First, we need some new variables.

 An exception error occurs when an asset cannot be found.
FIGURE 6.3 An exception error occurs when an asset cannot be found.

[code]
//experimental error handling variables
bool errorState;
string errorMessage;
SpriteFont ErrorFont;
Next, we initialize them.
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = “Content”;
TargetElapsedTime = TimeSpan.FromTicks(333333);
errorMessage = ““;
errorState = false;
}
[/code]

And in LoadContent(), we need to trap the exception error (note that ”sun” was temporarily renamed to ”sun1” to demonstrate the exception error).

[code]
//create sun sprite
sun = new Sprite(Content, spriteBatch);
if (!sun.Load(“sun1”))
{
errorState = true;
errorMessage = “Asset file ‘sun’ not found.”;
return;
}
[/code]

Draw() is where the error handling process comes into play.

[code]
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
//experimental error handler
if (errorState)
{
spriteBatch.DrawString(ErrorFont, “CRITICAL ERROR”,
Vector2.Zero, Color.Red);
spriteBatch.DrawString(ErrorFont, errorMessage,
new Vector2(0,100), Color.Red);
}
else
{
sun.Draw();
}
spriteBatch.End();
base.Draw(gameTime);
}
[/code]

When run again with the new error handling code in place, the previous exception error now becomes a nice in-game notification, as shown in Figure 6.4.

The exception error has been handled nicely.
FIGURE 6.4 The exception error has been handled nicely.

We’ve just scratched the surface of what will be possible with the new Sprite class in this hour. Over the next several chapters, the class will be enhanced significantly, making it possible—with properties and methods—to perform transformations

Drawing Bitmaps

A bitmap is a 2D image, also known as a texture in the realm of 3D programming. Bitmap images can be created in memory, but they are more often loaded from an existing bitmap file. The most common file types are BMP, PNG, and JPG. For games, we want high-quality bitmaps without lossy compression (in which pixels are changed to shrink the file size). The BMP format does work well but it has fallen out of favor in recent years due to format incompatibilities (such as some BMP files supporting an alpha channel, and some not). A good compromise is the PNG format, which offers decent file size and 32-bit ARGB color. For the sake of discussion, images will just be referred to as “bitmaps” regardless of the file format. In this hour, we’ll learn to add bitmap files to the Content project, and then load and draw them. This is the basis for most games!

Adding a Bitmap File to an XNA Project

Bitmap programming is the precursor to sprite programming, which is where we really want to go in order to draw game characters, ships, or whatever the game calls for. We can’t draw a 3D shape with just a bitmap, although bitmaps are used as textures when rendering in 3D. A texture is an image wrapped around the 3D shape, such as a tree, a car, or a human figure. Texturing can get pretty complex! Fortunately, we’ll start with the basics of 3D in a later hour and learn how to draw with lights and special effects. But first we need to learn 2D or bitmap programming.

There’s so much we can do with bitmaps in XNA! I’m eager to jump right into the advanced rendering stuff, but we’ll start off at a steady pace and first learn the basics. In the next hour, we’ll do some really fascinating things with bitmaps, like scaling and rotation.

We need to create a new project for our explorations of bitmap programming. Let’s begin with a new project.

Adding a Bitmap Asset to an XNA Project

Follow these steps to create the Bitmap Demo project that we will use as an example while learning how to load and draw bitmaps in this hour:

  1. Create a new Windows Phone (4.0) project called Bitmap Demo.
  2. Create or find a bitmap file containing an image you want to draw. The example uses an image of a spaceship that you can borrow. The bitmap file is a 32-bit PNG with a transparent region (via alpha channel).
  3. Right-click the Content project in Solution Explorer (it is called Bitmap Demo- Content (Content)), as shown in Figure 5.1. From the context menu that comes up, choose Add, Existing Item.

    Adding an existing bitmap file to the project.
    FIGURE 5.1 Adding an existing bitmap file to the project.
  4. The Add Existing Item dialog should come up. Select the bitmap file you want to add to the project. The file is added to the project as shown in Figure 5.2.

    The ship.png file has been added.
    FIGURE 5.2 The ship.png file has been added.
  5. If the Properties window is not visible, open it with View, Properties Window. When you select an asset file in the Content project, the file’s properties will be shown. Note the properties in the case of ship.png. Both the Content Importer and the Content Processor properties are automatically set to Texture – XNA Framework. This means that XNA recognized the file type and will process it as a texture. If you look at the other items in the drop-down list, you should find the following:
    • Effect – XNA Framework
    • Autodesk FBX – XNA Framework
    • Sprite Font Description – XNA Framework
    • WAV Audio File – XNA Framework
    • WMA Audio File – XNA Framework
    • WMV Video File – XNA Framework
    • XACT Project – XNA Framework
    • X File – XNA Framework
    • XML Content – XNA Framework

    These are the types of asset files XNA recognizes automatically. You can also create your own custom content importer to convert data files of any type to work with XNA—although you still have to write the code to read the file.

Loading a Bitmap File as an Asset

The name of the bitmap file added to the project is important, but the extension is not. The ship.png file added in the preceding Try It Yourself tutorial will be converted into a file called ship.xnb when compiled, and will be recognized by the name “ship” in the content manager. Let’s see how to load the bitmap.

Writing Code to Load a Bitmap File

Follow these steps to write the code to load a bitmap file:

  1. Add two new variables near the top of the Game1 class definition (where other global variables are found) as shown. The first variable is of type Texture2D, which is a class for bitmaps. The second is a Vector2 to keep track of the position of the bitmap on the screen.
    [code]
    public class Game1 : Microsoft.Xna.Framework.Game
    {
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;
    //texture variable
    Texture2D shipImage;
    //position variable
    Vector2 shipPos;
    [/code]
  2. Load the “ship” bitmap in the LoadContent() method and set the position to (0,0).
    [code]
    protected override void LoadContent()
    {
    spriteBatch = new SpriteBatch(GraphicsDevice);
    //load the ship bitmap
    shipImage = Content.Load<Texture2D>(“ship”);
    shipPos = Vector2.Zero;
    }
    [/code]

If XNA cannot find an asset file that your code tries to load with Content.Load() in the program’s main LoadContent() method, an exception error will be generated!

Drawing a Bitmap with SpriteBatch

After adding the bitmap file as an asset item to the Content project, adding the variables, and loading the bitmap as a Texture2D, we can now draw the bitmap onto the screen. For that, we need to jump down to the Draw() method. Here is the easiest way to draw a bitmap:

[code]
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
//begin drawing
spriteBatch.Begin();
//draw the bitmap
spriteBatch.Draw(shipImage, shipPos, Color.White);
//finish drawing
spriteBatch.End();
base.Draw(gameTime);
}
[/code]

The output from this program is shown in Figure 5.3.

SpriteBatch is an XNA core class that can draw both bitmaps and text, with many variations of the Draw() and DrawString() methods. In the next hour, we will work with SpriteBatch.Draw() extensively, exploring all of its capabilities. Let’s look at all the overloaded variations of Draw() here to get a feel for these capabilities. We will study these in more detail in the next hour.

The first parameter is always a Texture2D. The second parameter is always the destination— either a Rectangle or a Vector2. Beyond those two are a variety of parameters that make it possible to perform the following actions:

  • Rotate
  • Scale
  • Animate
  • Flip horizontally
  • Flip vertically
The Bitmap Demo program.
FIGURE 5.3 The Bitmap Demo program.

We will learn to use these special effects in upcoming chapters, as well as take a peek under the hood of the SpriteBatch.Begin() and SpriteBatch.End() methods that have been so mysterious up to this point. We have used only the second version, although the first one is easy enough to follow as well. Overloads 3 to 7 will be examined in the next hour and others following in depth, so think of this as just a sneak peek! Some of this code might be hard to read, because these are the definitions of the methods. I’m only going into this much detail now for Draw() because we will use it a lot!

[code]
public void Draw(Texture2D texture, Rectangle destinationRectangle,
Color color);
public void Draw(Texture2D texture, Vector2 position, Color color);
public void Draw(Texture2D texture, Rectangle destinationRectangle,
Rectangle? sourceRectangle, Color color);
public void Draw(Texture2D texture, Vector2 position,
Rectangle? sourceRectangle, Color color);
public void Draw(Texture2D texture, Rectangle destinationRectangle,
Rectangle? sourceRectangle, Color color, float rotation,
Vector2 origin, SpriteEffects effects, float layerDepth);
public void Draw(Texture2D texture, Vector2 position,
Rectangle? sourceRectangle, Color color, float rotation,
Vector2 origin, float scale, SpriteEffects effects, float layerDepth);
public void Draw(Texture2D texture, Vector2 position,
Rectangle? sourceRectangle, Color color, float rotation,
Vector2 origin, Vector2 scale, SpriteEffects effects,
float layerDepth);
[/code]

Drawing Bitmaps with Transparency

The ship.png file used in the example in this hour has an alpha channel and draws without showing the background pixels around the shape of the actual ship inside the bitmap. Let’s learn how to create an alpha channel. I’ll use Gimp because it’s a free graphics editor and it has many features similar to the uber-powerful Adobe Photoshop (download Gimp from www.gimp.org).

XNA will draw an image transparently if it has an alpha channel, and we don’t need to do anything extra for this to happen. This really makes life easier for an XNA game programmer. If you supply XNA with a bitmap file containing transparent pixels, it will draw that image with transparency.

I have used many graphics editors, including Photoshop, PaintShop Pro, Corel- DRAW, and Gimp. Although each behaves somewhat differently, they all share a similar toolset, including the ability to work with alpha channels. The instructions here for Gimp will be basically similar for other graphics editors.

Creating an Alpha Channel

Let’s take a look at how to create an alpha channel. To add a transparency layer to an image, you need to locate the Magic Wand tool available in most graphics editors.

  1. After selecting the Magic Wand tool, click somewhere in the background (which should be a solid color). The Magic Wand locates the edges of the game object and highlights everything around it, as shown in Figure 5.4. Another, more precise way to select a background is with the Color Picker or the Select by Color tool.
  2. Now that you have a selection available, you can create a layer mask. In Gimp, open the Layer menu and choose Mask, Add Layer Mask, as shown in Figure 5.5.
  3. The Add Layer Mask dialog shown in Figure 5.6 comes up. Choose the Selection option and check the Invert Mask option; then click the Add button.
    Finding the edges of the game object with the Magic Wand tool.
    FIGURE 5.4 Finding the edges of the game object with the Magic Wand tool.
    Preparing to add a layer mask.
    FIGURE 5.5 Preparing to add a layer mask.

    The Add Layer Mask dialog is used to choose options for a layer mask.
    FIGURE 5.6 The Add Layer Mask dialog is used to choose options for a layer mask.
  4. The next step is to apply the layer mask. You can tell Gimp to apply the mask using the Layer menu, then Mask, Apply Layer Mask, as shown in Figure 5.7.

    Applying the new layer mask makes it permanent.
    FIGURE 5.7 Applying the new layer mask makes it permanent.
  5. In the final figure, shown in Figure 5.8, the alpha channel has been created based on the masked selection. The checkerboard background behind the asteroid image shows the transparent region. The result looks very nice! You can load this image into your XNA project, and it will draw with transparency so that the outer edges of the image (where the solid background is located) will not overwrite the background of the screen. Just be sure to save the file using a bitmap file format that supports transparency and works with XNA. The most common formats are PNG and TGA.

    The asteroid image now has a masked transparency layer.
    FIGURE 5.8 The asteroid image now has a masked transparency layer.

This hour began a journey that will last for essentially the rest of the book. Most of our attention will be on 2D graphics in upcoming chapters, with all the variations of the SpriteBatch.Draw() method and the special effects that are possible with it. Being able to add a bitmap file to the project (with transparency information), and then load and draw it, is essential for every XNA programmer.

Getting User Input

Exploring Windows Phone Touchscreen Input

Programming a game’s input system really does require a lot of design consideration ahead of time because all we really can use is the touchscreen! Oh, there is an accelerometer that can be used for input, but it is a rare and often niche game that uses the accelerometer to read the phone’s orientation (the angle and position at which it is being held). The touchscreen, for all practical purposes, is treated like mouse input without a visible mouse cursor. Windows programmers will have a slightly harder time adjusting than someone who has been working with the Xbox 360 or another console, which already requires an adjustment in one’s assumptions about user input. Windows games are a cakewalk, with 100-plus keyboard keys, the mouse, and an optional controller! That’s a lot of input! On the Windows Phone, though, all we have is the touchscreen. So we need to make the most of it, being mindful that a user’s finger is rather large compared to the precise input of a mouse cursor.

For the purpose of detecting input for a game, the most obvious method might be to just detect the touch location and convert that to an average coordinate—like mouse input. But the Windows Phone touchscreen is capable of multitouch, not just single-touch input. Although the screen is very small compared to a tablet or PC screen, it is still capable of detecting input from up to four fingers at once. To develop a multitouch game, you will need the actual Windows Phone hardware— either a real phone or an unlocked development model (with no phone service).

Multitouch is a significant feature for the new phone! For our purposes here, however, we will just be concerned with single “tap” input from one finger, simulated with mouse motion in the emulator. We can do a single tap with a mouse click, or a drag operation by touching the screen and moving the finger across the screen. Again, this will have to be done with your mouse for development on the emulator (unless your Windows system uses a touchscreen!).

You will not be able to test multitouch in the emulator without a multitouch screen on your Windows development system or an actual Windows Phone device.

Simulating Touch Input

The key to touch input with a WP7 device with XNA is a class called TouchPanel. The XNA services for mouse, keyboard, and Xbox 360 controller input are not available in a WP7 project. So, for most XNA programmers, TouchPanel will be a new experience. Not to worry; it’s similar to the mouse code if we don’t tap into the multitouch capabilities.

The TouchPanel class includes one very interesting property that we can parse— MaximumTouchCount represents the number of touch inputs that the device can handle at a time.

The second class that we need to use for touch input is called TouchCollection. As the name implies, this is a collection that will be filled when we parse the TouchPanel while the game is running. TouchCollection has a State property that is similar to MouseState, with the enumerated values Pressed, Moved, and Released.

The Touch Demo Project, Step by Step

Let’s create a sample project to try out some of this code. I’ll skip the usual new project instructions at this point since the steps should be familiar by now. Just create a new project and add a font so that we can print something on the screen. I’ve used Moire Bold 24 as the font in this example.

  1. Add some needed variables for working with text output. (The code for the touchscreen input system will be added shortly.)
    [code]
    public class Game1 : Microsoft.Xna.Framework.Game
    {
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;
    SpriteFont MoireBold24;
    Vector2 position;
    Vector2 size;
    string text = “Touch Screen Demo”;
    [/code]
  2. Initialize the font and variables used in the program.
    [code]
    protected override void LoadContent()
    {
    spriteBatch = new SpriteBatch(GraphicsDevice);
    MoireBold24 = Content.Load<SpriteFont>(“MoireBold24”);
    size = MoireBold24.MeasureString(text);
    Viewport screen = GraphicsDevice.Viewport;
    position = new Vector2((screen.Width – size.X) / 2,
    (screen.Height – size.Y) / 2);
    }
    [/code]
  3. Write the update code to get touch input.
    [code]
    protected override void Update(GameTime gameTime)
    {
    if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
    ButtonState.Pressed)
    this.Exit();
    //get state of touch inputs
    TouchCollection touchInput = TouchPanel.GetState();
    //look at all touch points (usually 1)
    foreach(TouchLocation touch in touchInput)
    {
    position = new Vector2(touch.Position.X – size.X / 2,
    touch.Position.Y – size.Y / 2);
    }
    base.Update(gameTime);
    }
    [/code]
  4. Print the message on the screen at the touch coordinates.
    [code]
    protected override void Draw(GameTime gameTime)
    {
    GraphicsDevice.Clear(Color.CornflowerBlue);
    spriteBatch.Begin();
    spriteBatch.DrawString(MoireBold24, text, position, Color.White);
    spriteBatch.End();
    base.Draw(gameTime);
    }
    [/code]

Figure 4.1 shows the output of the program running in the WP7 emulator. Although the static figure doesn’t show movement, the message “Touch Screen Demo” moves on the screen with touch input! On a real Windows Phone device, this would work with finger input on the screen.

The Windows Phone hardware specification calls for at least four simultaneous touchscreen inputs!

The text message moves based on touch input.
FIGURE 4.1 The text message moves based on touch input.

How to Simulate More Inputs

What if you really do have a great design for a multitouch game but have no way to test it (that is, you do not have a WP7 device or a touchscreen for your Windows PC)? One alternative is to develop and test your game with simulated inputs for each finger, and then test each input separately in the emulator. This actually works quite well! Suppose your game requires one input to move a ship left or right on the screen, and another input to fire a weapon. The easy approach is to just leave the ship’s movement alone (presumably at the center of the screen without movement) to test the firing input. When that is working satisfactorily, then you might test leftto- right movement input with random or regular shooting intervals. This is how most developers will approach the problem.

Using Gestures on the Touchscreen

A related capability of the TouchPanel class is a gesture interface. This is potentially a really great feature for WP7 games, so don’t pass it up! A gesture is essentially nonverbal communication with your hands. A gesture on a touchscreen might be to flick an object across the screen rather than dragging to the exact location where you want it to go. Instead of manually moving something one direction or another, one might just flick it in the general direction. So, it’s up to the game to interpret the gestures based on the game’s user interface design.

Since gesture input can be used in place of touch input, you might want to just use gesture input instead. If all you need for your game is single-touch input, a tap gesture would work in place of the mouselike touch code seen previously. The main difference between the two methods is that gesture input does not support multitouch.

XNA supports gestures with the same TouchPanel class used for touch input. Here are the contents of the GestureType enumeration:

  • None
  • FreeDrag
  • Tap
  • Pinch
  • DoubleTap
  • Flick
  • Hold
  • DragComplete
  • HorizontalDrag
  • PinchComplete
  • VerticalDrag

Gesture-based input does not support multitouch. Only a single gesture (with one finger) can be used at a time.

To use gesture input, we have to enable it, because gesture input is not automatic. There are obvious problems with input if all gestures are enabled by default, because the game will behave erratically with flick gestures in a game using a lot of “drag”-style input to move and interact with the game. Even the simplest of arcadestyle or puzzle games will involve some dragging of the finger across the screen for input, and this could be misinterpreted as a flick if the intended movement is too fast. To enable both tap and flick gestures, add this line to the constructor, Game1(), or LoadContent():

[code]
TouchPanel.EnabledGestures = GestureType.Tap | GestureType.Flick;
[/code]

A simple example can be added to the Update() method of the Touch Demo to see how it works. Note that the first if condition is required. Trying to read a gesture when none is available will cause an exception!

[code]
if (TouchPanel.IsGestureAvailable)
{
GestureSample gesture = TouchPanel.ReadGesture();
if (gesture.GestureType == GestureType.Tap)
{
position = new Vector2(gesture.Position.X – size.X / 2,
gesture.Position.Y – size.Y / 2);
}
}
[/code]

Running this code, you might be surprised to find that the tap gesture is more like a mouse click-and-release event. Click-dragging does not produce the gesture! You can see for yourself by commenting out the touch input code in the Touch Demo program, and running just the gesture code instead. If you’re really interested in gesture input, a real WP7 device is a must-have! The emulator just doesn’t satisfy. If you are doing WP7 development for profit, a dev device or an actual Windows Phone device (with carrier subscription) is a good investment.

Touch input can be quite satisfying to a game designer after working with the traditional keyboard and mouse, because it offers the potential for very creative forms of input in a game. Although regular multitouch input will be the norm for most Windows Phone games, the ability to use gestures also might prove to be fun.

Printing Text

Creating the Font Demo Project

A font in XNA is nothing more than a text file—at least, from the programmer’s point of view. When the project is compiled, XNA uses the text file to create a bitmap font on a memory texture and use that for printing text on the screen.

This is a time-consuming process, which is why the font is created at program startup rather than while it’s running. Let’s create a new project and add a font to it.

Creating a New XNA Project

Follow these steps to create a new XNA project in Visual C# 2010:

  1. Start up Visual Studio 2010 Express for Windows Phone (or whichever edition of Visual Studio 2010 you are using).
  2. Bring up the New Project dialog, shown in Figure 3.1, from either the Start Page or the File menu.

    Creating the Font Demo project.
    FIGURE 3.1 Creating the Font Demo project.
  3. Choose Windows Phone Game (4.0) from the list of project templates.
  4. Type in a name for the new project (the example is called Font Demo).
  5. Choose the location for the project by clicking the Browse button, or by typing the folder name directly.
  6. Click OK to create the new project.

The new project is generated by Visual Studio and should look similar to the project shown in Figure 3.2.

The newly generated Font Demo project.
FIGURE 3.2 The newly generated Font Demo project.

Adding a New Font to the Content Project

At this point, you can go ahead and run the project by pressing F5, but all you will see in the Windows Phone emulator is a blue screen. That is because we haven’t written any code yet to draw anything. Before we can print text on the screen, we have to create a font, which is added to the Content project.

In XNA 4.0, most game assets are added to the Content project within the Solution, where they are compiled or converted into a format that XNA uses. We might use the general term “project” when referring to a Windows Phone game developed with XNA, but there might be more than one project in the Solution. The “main project” will be the one containing source code for a game. Some assets, however, might be located just within the source code project, depending on how the code accesses those assets. Think of the Content project as a container for “managed” assets.

A Visual Studio “Solution” is the overall wrapper or container for a game project, and should not be confused with “projects” that it contains, including the Content project containing game assets (bitmap files, audio files, 3D mesh files, and so on).

In this example, both the Solution and the main project are called “Font Demo,” because Visual Studio uses the same name for both when a new Solution is generated. Now, let’s add a new font to the Content project. Remember that the Content project is where all game assets are located.

  1. Select the Content project in Solution Explorer to highlight it, as shown in Figure 3.3.

    Highlighting the Content project.
    FIGURE 3.3 Highlighting the Content project.
  2. Open the Project menu and choose Add New Item. Optionally, you can right-click the Content project in Solution Explorer (Font DemoContent (Content)) to bring up the context menu, and choose Add, New Item.
  3. The Add New Item dialog, shown in Figure 3.4, appears. Choose Sprite Font from the list. Leave the name as is (SpriteFont1.spritefont).

    Adding a new Sprite Font.
    FIGURE 3.4 Adding a new Sprite Font.

A new .spritefont file has been added to the Content project, as shown in Figure 3.5. Visual Studio opens the new file right away so that you can make any changes you want to the font details. The default font name is Segoe UI Mono, which is a monospaced font. This means each character of the font has the same width (takes up the same amount of horizontal space). Some fonts are proportional, which means each character has a different width (in which case, “W” and “I” are spaced quite differently, for instance).

A new Sprite Font has been added to the Content project.
FIGURE 3.5 A new Sprite Font has been added to the Content project.

The SpriteFont1.spritefont file is just a text file, like a .CS source code file, but it is formatted in the XML (Extensible Markup Language) format. You can experiment with the font options in the .spritefont descriptor file, but usually the only fields you will need to change are FontName and Size. Here is what the font file looks like with all comments removed:

[code]
<?xml version=”1.0” encoding=”utf-8”?>
<XnaContent xmlns:Graphics =
“Microsoft.Xna.Framework.Content.Pipeline.Graphics”>
<Asset Type=”Graphics:FontDescription”>
<FontName>Segoe UI Mono</FontName>
<Size>14</Size>
<Spacing>0</Spacing>
<UseKerning>true</UseKerning>
<Style>Regular</Style>
<CharacterRegions>
<CharacterRegion>
<Start>&#32;</Start>
<End>&#126;</End>
</CharacterRegion>
</CharacterRegions>
</Asset>
</XnaContent>
[/code]

Visual Studio Solution (.sln) and project (.csproj) files also contain XML-formatted information!

Table 3.1 shows the royalty-free fonts included with XNA 4.0. Note that some fonts come with italic and bold versions even though the SpriteFont description also allows for these modifiers.

XNA Fonts

Learning to Use the SpriteFont Class

We can create as many fonts as we want in an XNA project and use them at any time to print text with different styles. For each font you want to use in a project, create a new .spritefont file. The name of the file is used to load the font, as you’ll see next. Even if you want to use the same font style with a different point size, you must create a separate .spritefont file (although we will learn how to scale a font as a rendering option).

Loading the SpriteFont Asset

To use a SpriteFont asset, first add a variable at the top of the program. Let’s go over the steps:

  1. Add a new variable called SpriteFont1. You can give this variable a different name if you want. It is given the same name as the asset here only for illustration, to associate one thing with another.
    [code]
    public class Game1 : Microsoft.Xna.Framework.Game
    {
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;
    //create new font variable
    SpriteFont SpriteFont1;
    [/code]
  2. Create (instantiate) a new object using the SpriteFont1 variable, and simultaneously load the font with the Content.Load() method. Note the class name in brackets, <SpriteFont>. If you aren’t familiar with template programming, this can look a bit strange. This type of coding makes the code cleaner, because the Content.Load() method has the same call no matter what type of object you tell it to load.
    [code]
    protected override void LoadContent()
    {
    // Create a new SpriteBatch, which can be used to draw textures.
    spriteBatch = new SpriteBatch(GraphicsDevice);
    // TODO: use this.Content to load your game content here
    SpriteFont1 = Content.Load<SpriteFont>(“SpriteFont1”);
    }
    [/code]

If the Content class did not use a templated Load() method, we would need to call a different method for every type of game asset, such as Content.LoadSpriteFont(), Content.LoadTexture2D(), or Content.LoadSoundEffect().

There is another important reason for using a template form of Load() here: We can create our own custom content loader to load our own asset files! XNA is very extendable with this capability. Suppose you want to load a data file saved by your own custom level editor tool. Instead of manually converting the level file into text or XML, which XNA can already read, you could instead just write your own custom content loader, and then load it with code such as this: Content.Load<Level>(“level1”)

The ability to write code like this is powerful, and reflects a concept similar to “late binding.” This means the C# compiler might not know exactly what type of object a particular line of code is referring to at compile time, but the issue is sorted out later while the program is running. That’s not exactly what’s happening here, but it is a similar concept, and the easiest illustration of template programming I can think of.

These are just possibilities. Let’s get back to the SpriteFont code at hand!

Printing Text

Now that we have loaded the .spritefont asset file, and XNA has created a bitmap font in memory after running the code in LoadContent(), the font is available for use. We can use the SpriteFont1 object to print text on the screen using SpriteBatch.DrawString(). Just be sure to always have a matching pair of SpriteBatch.Begin() and SpriteBatch.End() statements around any drawing code.

Here are the steps you may follow to print some text onto the screen using the new font we have created:

  1. Scroll down to the Draw() method in the code listing.
  2. Add the code shown in bold.
    [code]
    protected override void Draw(GameTime gameTime)
    {
    GraphicsDevice.Clear(Color.CornflowerBlue);
    // TODO: Add your drawing code here
    string text = “This is the Segoe UI Mono font”;
    Vector2 position = new Vector2(20, 20);
    spriteBatch.Begin();
    spriteBatch.DrawString(SpriteFont1, text, position, Color.White);
    spriteBatch.End();
    base.Draw(gameTime);
    }
    [/code]

Run the program by pressing F5. The WP7 emulator comes up, as shown in Figure 3.6.

Printing text in the Font Demo program.
FIGURE 3.6 Printing text in the Font Demo program.

The version of SpriteBatch.DrawString() used here is the simplest version of the method, but other overloaded versions of the method are available. An overloaded method is a method such as DrawString() that has two or more different sets of parameters to make it more useful to the programmer. There are actually six versions of DrawString(). Here is an example using the sixth and most complex version. When run, the changes to the text output are dramatic, as shown in Figure 3.7!

[code]
float rotation = MathHelper.ToRadians(15.0f);
Vector2 origin = Vector2.Zero;
Vector2 scale = new Vector2(1.3f, 5.0f);
spriteBatch.DrawString(SpriteFont1, text, position, Color.White,
rotation, origin, scale, SpriteEffects.None, 0.0f);
[/code]

Experimenting with different DrawString() options.
FIGURE 3.7 Experimenting with different DrawString() options.

As you have learned in this hour, the font support in XNA takes a little time to set up, but after a font has been added, some very useful and versatile text printing capabilities are available. We can print text via the SpriteFont.DrawString() method, with many options available such as font scaling and different colors.

Getting Started with Visual C# 2010 for Windows Phone

Visual C# 2010 Express

At the time of this writing, the current version of the development tool for Windows Phone 7 is Visual Studio 2010. To make development simple for newcomers to the Windows Mobile platform, Microsoft has set up a package that will install everything you need to develop, compile, and run code in the emulator or on a physical Windows Phone device—for free. The download URL at this time is http://www.microsoft. com/express/Phone. If you are using a licensed copy of Visual Studio 2010, such as the Professional, Premium, or Ultimate edition, then you will find XNA Game Studio 4.0 and related tools at http://create.msdn.com (the App Hub website). The App Hub website, shown in Figure 2.1, also contains links to the development tools.

The App Hub website has download links to the development tools.
FIGURE 2.1 The App Hub website has download links to the development tools.

The most common Windows Phone developer will be using the free version of Visual C# 2010, called the Express edition. This continues the wonderful gift Microsoft first began giving developers with the release of Visual Studio 2005. At that time, the usual “professional” versions of Visual Studio were still available, of course, and I would be remiss if I failed to point out that a licensed copy of Visual Studio is required by any person or organization building software for business activities (including both for-profit and nonprofit). The usual freelance developer will also need one of the professional editions of Visual Studio, if it is used for profit. But any single person who is just learning, or any organization that just wants to evaluate Visual Studio for a short time, prior to buying a full license, can take advantage of the free Express editions. I speak of “editions” because each language is treated as a separate product. The professional editions include all the languages, but the free Express editions, listed here, are each installed separately:

  • Visual C# 2010 Express
  • Visual Basic 2010 Express
  • Visual C++ 2010 Express

The version of Visual Studio we will be using is called Visual Studio 2010 Express for Windows Phone. This is a “package” with the Windows Phone SDK already prepackaged with Visual C# 2010 Express. (Despite the name, “Visual Studio” here supports only the C# language.) It’s a nice package that makes it very easy to get started doing Windows Phone development. But if you are using Visual Studio 2010 Professional (or one of the other editions) along with the Windows Phone SDK, you will see a lot more project templates in the New Project dialog, shown in Figure 2.2.

The New Project dialog in Visual C# 2010 Express.
FIGURE 2.2 The New Project dialog in Visual C# 2010 Express.
  • Windows Phone Application (Visual C#)
  • Windows Phone Databound Application (Visual C#)
  • Windows Phone Class Library (Visual C#)
  • Windows Phone Panorama Application (Visual C#)
  • Windows Phone Pivot Application (Visual C#)
  • Windows Phone Game (4.0) (Visual C#)
  • Windows Phone Game Library (4.0) (Visual C#)
  • Windows Game (4.0) (Visual C#)
  • Windows Game Library (4.0) (Visual C#)
  • Xbox 360 Game (4.0) (Visual C#)
  • Xbox 360 Game Library (4.0) (Visual C#)
  • Content Pipeline Extension Library (4.0)
  • Empty Content Project (4.0) (Visual C#)

As you can see, even in this limited version of Visual Studio 2010, all the XNA Game Studio 4.0 project templates are included—not just those limited to Windows Phone. The project templates with “(4.0)” in the name come from the XNA Game Studio SDK, which is what we will be primarily using to build Windows Phone games. The first five project templates come with the Silverlight SDK. That’s all we get with this version of Visual Studio 2010. It’s not even possible to build a basic Windows application here—only Windows Phone (games or apps), Windows (game only), and Xbox 360 (obviously, game only). The first five project templates are covered in the next section, “Using Silverlight for WP7.”

Did you notice that all of these project templates are based on the C# language? Unfortunately for Visual Basic fans, we cannot use Basic to program games or apps for Windows Phone using Visual C# 2010 Express. You can install Visual Basic 2010 Express with Silverlight and then use that to make WP7 applications. XNA, however, supports only C#.

We don’t look at Xbox 360 development in this book at all. If you’re interested in the subject, see my complementary book XNA Game Studio 4.0 for Xbox 360 Developers [Cengage, 2011].

Using Silverlight for WP7

Microsoft Silverlight is a web browser plug-in “runtime.” Silverlight is not, strictly speaking, a development tool. It might be compared to DirectX, in that it is like a library, but for rich-content web apps. It’s similar to ASP.NET in that Silverlight applications run in a web browser, but it is more capable for building consumer applications (while ASP.NET is primarily for business apps). But the way Silverlight applications are built is quite different from ASP.NET—it’s more of a design tool with an editing environment called Expression Blend. The design goal of Silverlight is to produce web applications that are rich in media support, and it supports all standard web browsers (not just Internet Explorer, which is a pleasant surprise!), including Firefox and Safari on Mac.

Using Expression Blend to Build Silverlight Projects

Microsoft Expression Blend 4 is a free tool installed with the Windows Phone package that makes it easier to design Silverlight-powered web pages with rich media content support. Blend can be used to design and create engaging user experiences for Silverlight pages. Windows application support is possible with the WPF (Windows Presentation Foundation) library. A key feature of Blend is that it separates design from programming. As you can see in Figure 2.3, the New Project dialog in Blend lists the same project types found in Visual C# 2010 Express.

Expression Blend is a Silverlight development tool for web designers.
FIGURE 2.3 Expression Blend is a Silverlight development tool for web designers.

Let’s create a quick Expression Blend project to see how it works. While working on this quick first project, keep in mind that we’re not building a “Blend” project, but a “Silverlight” project—using Blend. Blend is a whole new Silverlight design and development tool, not affiliated with Visual Studio (but probably based on it). The Silverlight library is already installed on the Windows Phone emulator and actual phones.

Here’s how to create the project:

  1. Create a Windows Phone Application project using the New Project dialog. Click File, New Project.
  2. Blend creates a standard project for Windows Phone, complete with an application title and opening page for the app.
  3. Run the project with Project, Run Project, or by pressing F5. The running program is shown in Figure 2.4.
Our first project with Expression Blend.
FIGURE 2.4 Our first project with Expression Blend.

This is a useless app, but it shows the steps needed to create a new project and run it in the Windows Phone emulator. Did you notice how large the emulator window appears? That’s full size with respect to the screen resolution of WP7. As you’ll recall from the first hour, the resolution is 480×800. That is enough pixels to support 480p DVD movies, but not the 740p or 1080p HD standards. Still, DVD quality is great for a phone! And when rotated to landscape mode, 800×480 is a lot of screen real estate for a game too.

You can make quick and easy changes to the labels at the top and experiment with the design controls in the toolbox on the left. Here you can see that the application title and page title have been renamed, and some images and shapes have been added to the page. Pressing F5 again brings it up in the emulator, shown in Figure 2.5.

Now that you’ve seen what’s possible with Expression Blend’s more designer-friendly editor, let’s take a look at the same Silverlight project in Visual Studio 2010.

Silverlight Projects

The Silverlight runtime for WP7 supports some impressive media types with many different audio and video codecs, vector graphics, bitmap graphics, and animation. That should trigger the perimeter alert of any game developer worth their salt! Silverlight brings some highly interactive input mechanisms to the Web, including accelerometer motion detection, multitouch input (for devices that support it),camera, microphone input, and various phone-type features (like accessing an address book and dialing).

Making quick changes to the page is easy with Expression Blend.
FIGURE 2.5 Making quick changes to the page is easy with Expression Blend.

To find out whether your preferred web browser supports Silverlight, visit the installer web page at http://www.microsoft.com/getsilverlight/get-started/install.

The Visual Studio 2010 project templates specific to Silverlight are highlighted in bold in the list below. These are the same project templates shown in Expression Blend!

  • Windows Phone Application (Visual C#)
  • Windows Phone Databound Application (Visual C#)
  • Windows Phone Class Library (Visual C#)
  • Windows Phone Panorama Application (Visual C#)
  • Windows Phone Pivot Application (Visual C#)
  • Windows Phone Game (4.0) (Visual C#)
  • Windows Phone Game Library (4.0) (Visual C#)
  • Windows Game (4.0) (Visual C#)
  • Windows Game Library (4.0) (Visual C#)
  • Xbox 360 Game (4.0) (Visual C#)
  • Xbox 360 Game Library (4.0) (Visual C#)
  • Content Pipeline Extension Library (4.0)
  • Empty Content Project (4.0) (Visual C#)

Let’s create a quick project in Visual Studio in order to compare it with Expression Blend. You’ll note right away that it is not the same rich design environment, but is more programmer oriented.

Comparing Visual Studio with Expression Blend

Let’s create a new project in Visual C# 2010 in order to compare it with Expression Blend. Follow these steps:

  1. Open the New Project dialog with File, New Project.
  2. Next, in the New Project dialog, choose the target folder for the project and type in a project name, as shown in Figure 2.6.

    Creating a new Silverlight project in Visual C# 2010 Express.
    FIGURE 2.6 Creating a new Silverlight project in Visual C# 2010 Express.
  3. Click the OK button to generate the new project shown in the figure. Not very user-friendly, is it? First of all, double-clicking a label does not make it editable, among other limitations (compared to Expression Blend). Where are the control properties? Oh, yes, in the Properties window in Visual Studio. See Figure 2.7. This is also very data-centric, which programmers love and designers loathe. The view on the left is how the page appears on the device (or emulator); the view on the right is the HTML-like source code behind the page, which can be edited.
  4. Bring up the Properties window (if not already visible) by using the View menu. Select a control on the page, such as the application title. Scroll down in the Properties to the Text property, where you can change the label’s text, as shown in Figure 2.8. Play around with the various properties to change the horizontal alignment, the color of the text, and so on. Open the Toolbox (located on the left side of Visual Studio) to gain access to new controls such as the Ellipse control shown here.
    The new Silverlight project has been created.
    FIGURE 2.7 The new Silverlight project has been created.

    Adding content to the Silverlight page.
    FIGURE 2.8 Adding content to the Silverlight page.

XNA Game Studio

XNA Game Studio 4.0 was released in the fall of 2010. (From now on, let’s just shorten this to “XNA” or “XNA 4.0”, even though “Game Studio” is the name of the SDK, and “XNA” is the overall product name.) XNA 4.0 saw several new improvements to the graphics system, but due to the hardware of the Xbox 360, XNA is still based on Direct3D 9 (not the newer versions, Direct3D 10 or 11). This is actually very good news for a beginner, since Direct3D 9 is much easier to learn than 10 or 11. Although XNA abstracts the C++-based DirectX libraries into the C#-based XNA Framework, there is still much DirectX-ish code that you have to know in order to build a capable graphics engine in XNA. While XNA 4.0 added WP7 support, it simultaneously dropped support for Zune (the portable multimedia and music player).

I have a Zune HD, and it’s a nice device! It can play 720p HD movies and even export them to an HDTV via an adapter and HDMI cable. It plays music well too. But, like many consumers, I just did not have much incentive to go online and download games for the Zune. This is, of course, purely a subjective matter of opinion, but it’s disappointing for game developers who put effort into making games for Zune. Fortunately, the code base is largely the same (thanks to XNA and C#), so those Zune games can be easily ported to WP7 now.

Rendering states, enumerations, return values, and so forth are the same in XNA as they are in Direct3D, so it could be helpful to study a Direct3D book to improve your skills as an XNA programmer!

The project templates for Windows Phone might surprise you—there are only two! We can build a Windows Phone game or a game library. All the other templates are related to the other platforms supported by XNA.

  • Windows Phone Application (Visual C#)
  • Windows Phone Databound Application (Visual C#)
  • Windows Phone Class Library (Visual C#)
  • Windows Phone Panorama Application (Visual C#)
  • Windows Phone Pivot Application (Visual C#)
  • Windows Phone Game (4.0) (Visual C#)
  • Windows Phone Game Library (4.0) (Visual C#)
  • Windows Game (4.0) (Visual C#)
  • Windows Game Library (4.0) (Visual C#)
  • Xbox 360 Game (4.0) (Visual C#)
  • Xbox 360 Game Library (4.0) (Visual C#)
  • Content Pipeline Extension Library (4.0)
  • Empty Content Project (4.0) (Visual C#)

Let’s build a quick XNA project for Windows Phone to see what it looks like. We’ll definitely be doing a lot of this in upcoming chapters since XNA is our primary focus (the coverage of Silverlight was only for the curious—grab a full-blown Silverlight or Expression Blend book for more complete and in-depth coverage.

Creating Your First XNA 4.0 Project

Let’s create a new XNA 4.0 project in Visual C# 2010, so we can use this as a comparison with the previous project created with Expression Blend. Follow these steps:

  1. Create a new project. We’ll be basing these tutorials around Visual Studio 2010 Express for Windows Phone. The processes will be similar to using the Professional version, but you will see many more project templates in the New Project dialog. Open the File menu and choose New Project. The New Project dialog is shown in Figure 2.9.

    Creating a new XNA 4.0 project.
    FIGURE 2.9 Creating a new XNA 4.0 project.
  2. The new project has been created. Note, from Figure 2.10, the code that has been automatically generated for the XNA project. If you have ever worked with XNA before, this will be no surprise—the code looks exactly like the generated code for Windows and Xbox 360 projects!

    The new XNA 4.0 project has been created.
    FIGURE 2.10 The new XNA 4.0 project has been created.
  3. Run the project with Build, Run, or by pressing F5. The emulator will come up, as shown in Figure 2.11. Doesn’t look like much—just a blue screen! That’s exactly what we want to see, because we haven’t written any game code yet.
  4. Add a SpriteFont to the Content project. Right-click the content project, called XNA ExampleContent(Content) in the Solution Explorer. Choose Add, New Item, as shown in Figure 2.12.
    Running the XNA project in the Windows Phone emulator.
    FIGURE 2.11 Running the XNA project in the Windows Phone emulator.

    Adding a new item to the content project.
    FIGURE 2.12 Adding a new item to the content project.
  5. In the Add Item dialog, choose the Sprite Font item from the list, as shown in Figure 2.13, and leave the filename as SpriteFont1.spritefont.

    Adding a new SpriteFont content item to the project.
    FIGURE 2.13 Adding a new SpriteFont content item to the project.
  6. Create the font variable. The comments in the code listing have been removed to make the code easier to read. We’ll dig into the purpose of all this code in the next hour, so don’t be concerned with understanding all the code yet. Type in the two new bold lines of code shown here to add the font variable.
    [code]
    public class Game1 : Microsoft.Xna.Framework.Game
    {
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;
    //new font variable
    SpriteFont font;
    public Game1()
    {
    graphics = new GraphicsDeviceManager(this);
    Content.RootDirectory = “Content”;
    TargetElapsedTime = TimeSpan.FromTicks(333333);
    }
    [/code]
  7. Load the font. Enter the two new lines shown in bold in the LoadContent method.
    [code]
    protected override void Initialize()
    {
    base.Initialize();
    }
    protected override void LoadContent()
    {
    spriteBatch = new SpriteBatch(GraphicsDevice);
    //load the font
    font = Content.Load<SpriteFont>(“SpriteFont1”);
    }
    protected override void UnloadContent()
    {
    }
    [/code]
  8. Print a message on the screen. Using the SpriteBatch and SpriteFont objects, we can print any text message. This is done from the Draw method—add the code highlighted in bold.
    [code]
    protected override void Update(GameTime gameTime)
    {
    if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
    ButtonState.Pressed)
    this.Exit();
    base.Update(gameTime);
    }
    protected override void Draw(GameTime gameTime)
    {
    GraphicsDevice.Clear(Color.CornflowerBlue);
    //print a message
    spriteBatch.Begin();
    string text = “HELLO FROM XNA!”;
    Vector2 pos = font.MeasureString(text);
    spriteBatch.DrawString(font, text, pos, Color.White);
    spriteBatch.End();
    base.Draw(gameTime);
    }
    }
    [/code]
  9. Run the program using Debug, Start Debugging, or by pressing F5. The program will come up in the emulator, shown in Figure 2.14. Now there’s just one big problem: The font is too small, and the screen needs to be rotated to landscape mode so we can read it!
  10. Click the emulator window to cause the little control menu to appear at the upper right. There are two icons that will rotate the window left or right, allowing us to switch from portrait to landscape mode. All XNA projects will default to portrait mode by default. Landscape mode is shown in Figure 2.15.
    The text message is displayed in the emulator— sideways!
    FIGURE 2.14 The text message is displayed in the emulator— sideways!

    Rotating the emulator window to landscape mode for XNA projects.
    FIGURE 2.15 Rotating the emulator window to landscape mode for XNA projects.
  11. Enlarge the font. We’re almost done; there’s just one final thing I want to show you how to do here. Open the font file you created, SpriteFont1. spritefont. Change the size value from 14 to 36. Now rerun the project by pressing F5. The new, large font is shown in Figure 2.16.

    Enlarging the font to make it more readable.
    FIGURE 2.16 Enlarging the font to make it more readable.

XNA or Silverlight: What’s the Verdict?

We have now seen two projects developed with the two different, and somewhat competing tools: XNA and Silverlight. Which should we choose? This is really a matter of preference when it comes to developing a game. Although XNA is far more capable due to its rendering capabilities, Silverlight can be used to make a game as well, with form-based control programming. For portable, touchscreen applications, it’s a given: Silverlight. But for serious game development, XNA is the only serious option.

We covered quite a bit of information regarding Visual Studio 2010, the project templates available for Windows Phone, and the value-added tool Expression Blend. A sample project was presented using Expression Blend with a corresponding Silverlight project in Visual Studio, as well as an XNA project. We’re off to a good start and already writing quite a bit of code! In the next hour, you will create your first Windows Phone game.