Interacting with Players

Applies to: Windows Phone 7

Published: August 2011

Author: Rod Stephens

WROX Tech Editors for Windows Phone 7 Articles

Wrox Windows Phone 7 Books

This topic contains the following sections.

Summary: This article explains how to interact with the user. It explains how to display output generated at runtime such as scores or informational messages. It also explains how to receive input from the user in the form of taps.

Earlier articles in this series explained how to create and display realistic-looking scenes containing textured objects. They also explained how to transform objects and view scenes from different positions, but all of the examples so far worked without any feedback from the user. They may have shown objects spinning, but they did not give the user any control.

This article explains how a program can interact with the user. The two ways you might want to interact with the user are by displaying output and by receiving input from the user.

By definition a 3-D game displays some sort of 3-D graphics, but it is also often useful to display other information to the user. For example, you might want to display small pictures or icons to show the player’s status. You might also want to display textual information such as a score or messages telling the player that something has happened.

Sometimes you might want to display this information two-dimensionally, either in front of or behind the 3-D scene. At other times you might want to display information on a three-dimensional object within the scene.

Figure 1 shows each of these approaches. The smiley faces in the lower-right corner are icons displayed behind and in front of the 3-D scene. The yellow and white text lines on the left display scores behind and in front of the scene. Finally, the yellow text on the purple background in the middle of the scene displays information three-dimensionally within the scene.

Figure 1. You can display graphical and textual information behind, in front of, or within a 3-D scene.

Referenced Image

The Marvelous Maze game described at the end of this series of articles displays both two- and three-dimensional data. It displays two-dimensional images in its lower right corner to show which gems the user has found, and it displays a sign on one of the maze’s three-dimensional walls.

The following two sections briefly summarize how you can display 2-D and 3-D information.

Displaying Information in 2-D

This series assumes that you know the basics of displaying two-dimensional graphics in Windows Phone. If you need a refresher, see “How to: Create Your First XNA Framework Application for Windows Phone” at http://msdn.microsoft.com/library/ff472340.aspx.

The basic approach is to load a texture holding the image and then use the SpriteBatch object’s Draw method to display the image. Displaying two-dimensional graphics such as the smiley faces and the score text on the left in Figure 1 is almost as easy, with one huge catch: the SpriteBatch object modifies some program settings and interferes with the 3-D drawing.

You can easily fix this problem by adding the following code to the program’s Draw method just before it starts drawing three-dimensional objects:

// Restore states modified by SpriteBatch drawing.
GraphicsDevice.BlendState = BlendState.Opaque;
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
GraphicsDevice.SamplerStates[0] = SamplerState.LinearClamp;

This code resets the parameters that are modified by the SpriteBatch so the 3-D graphics works properly.

Determining whether an icon appears behind or in front of the 3-D scene is as simple as drawing it before or after drawing the 3-D objects.

In addition to providing a method to display textures, the SpriteBatch class provides a DrawString method that can draw text directly on the phone’s screen. Calling the method is easy, but before you can do so, you must create a SpriteFont object to use while drawing the text.

To create a SpriteFont object, right-click the content project in Solution Explorer, open the Add menu, and select New Item. Select the SpriteFont template, give it a good name, such as ScoreFont.spritefont, and click Add. Initially, Visual Studio opens the spritefont’s file so that you can edit its properties. For example, you can change the font’s name and size. The example shown in Figure 1 uses the default 14 point Segoe UI Mono font.

Note that many fonts have licensing issues that prevent you from including them in your game unless you buy the correct license. Microsoft has made a set of license-free fonts available for use in XNA programs. These are installed automatically when you install Game Studio 4.0. Learn more or download the fonts at http://create.msdn.com/en-US/education/catalog/utility/font_pack.

You can find other free fonts scattered around the Internet. For example, the forum discussion at http://forums.create.msdn.com/forums/t/1378.aspx has information about several websites where you can look for free fonts.

Having added the font to the project, your LoadContent method can use code similar to the following to load it into a SpriteFont object.

scoreFont = Content.Load<SpriteFont>("ScoreFont");

Now you can use the font to draw on the screen. The following code shows the Draw method used by the Scores example program shown in Figure 1 to display the smiley face images and draw text on the screen.

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);

    // TODO: Add your drawing code here.


    // ***************************
    // 2-D drawing behind the scene
    // ***************************
    spriteBatch.Begin();
    spriteBatch.Draw(smileyTexture, backRect, Color.White);
    string scoreText = "Score: " + Score;
    spriteBatch.DrawString(scoreFont, scoreText, backOrigin,
        Color.Yellow, 0, new Vector2(0, 0),
        2, SpriteEffects.None, 0);
    spriteBatch.End();

    // **********
    // 3-D drawing
    // **********
    // Restore states modified by SpriteBatch drawing.
    GraphicsDevice.BlendState = BlendState.Opaque;
    GraphicsDevice.DepthStencilState = DepthStencilState.Default;
    GraphicsDevice.SamplerStates[0] = SamplerState.LinearClamp; 

    // Rotate around the Y axis.
    worldMatrix = Matrix.CreateRotationY(angle);

    // Draw.
    drawables.Draw(worldMatrix, viewMatrix, projectionMatrix,
        FillMode.Solid, CullMode.CullCounterClockwiseFace);

    // ********************************
    // 2-D drawing in front of the scene
    // ********************************
    spriteBatch.Begin();
    spriteBatch.Draw(smileyTexture, frontRect, Color.White);

    // spriteBatch.DrawString(scoreFont, "Score: 10000", frontOrigin, Color.Yellow);
    spriteBatch.DrawString(scoreFont, scoreText, frontOrigin,
        Color.White, 0, new Vector2(0, 0),
        2, SpriteEffects.None, 0);
    spriteBatch.End();


    base.Draw(gameTime);
}

After clearing the screen, this code calls the SpriteBatch object’s Begin method. It then draws the smileyTexture shown behind the scene in Figure 1. It uses the SpriteBatch object’s DrawString method to draw the text behind the scene (Score is a module-level variable holding the user’s current score) and then calls the SpriteBatch object’s End method.

The code then restores state information that is modified by the SpriteBatch object before drawing the three-dimensional objects.

The code finishes by drawing another smiley face and more text in a manner similar to the way it printed the first ones.

Displaying Information in 3-D

Displaying information in 2-D on the screen is useful, but sometimes it would be nice to display information within the 3-D scene. You can display information in 3-D by placing it in a texture and then using that texture to draw a 3-D object using methods you learned in the previous article.

The Marvelous Maze game displays a sign that says “You found the secret room!” The sign is part of the texture the program uses for the walls. The code simply draws a rectangle on a wall using the appropriate part of the texture.

With only a little extra work, you can also display three-dimensional information generated at runtime. Simply create a RenderTarget2D object on which to draw. This is simply an object that can receive rendered output such as the result displayed by a 3-D scene or text. Set the GraphicsDevice object’s RenderTarget value to this object so that future output generated by the device goes there and draw whatever information you want to display. Then use the resulting texture to draw a three-dimensional object.

There are many ways you might let the user interact with your game. The simplest of these is to look for taps on different areas on the screen.

You can detect taps in three ways: First, you can examine the phone’s touch state to see whether the user is touching the screen. Second, you can similarly use Mouse.GetState to get information about the “mouse,” which for the phone really means the touch panel. Third, you can look at the program’s current gestures to see whether the user has made a tap.

The first and second methods detect only finger touches, so they consider pressing and dragging a finger to be the same as a tap. The third method requires the user to briefly press and then release the screen.

The Scores program shown in Figure 1 uses the first method, which is described here, to increase and decrease the user’s score by 1,000 points when the user taps one of the smiley faces.

The Scores program’s Update method calls UpdateScore, which uses the following code to see whether the user tapped a smiley face:

// Update the score if the user tapped a smiley face.
private void UpdateScore()
{
    int touched = TargetTapped(frontRect, backRect);
    switch (touched)
    {
        case 0:     // In front. +1000.
            score += 1000;
            break;
        case 1:     // in back. -1000.
            score -= 1000;
            break;
    }
}

This code calls the TargetTapped method to see whether the user tapped inside either of the rectangles frontRect or backRect that give the smiley faces’ positions. If the user tapped one of these, the method updates the Score variable accordingly.

The following code shows the TargetTapped method.

// Return the index of the tapped target or -1 if none was tapped.
private bool isTouching = false;
private int TargetTapped(params Rectangle[] targets)
{
    // See whether the user tapped anything.
    TouchCollection touches = TouchPanel.GetState();
    if (!isTouching && touches.Count > 0)
    {
        // This is a tap.
        isTouching = true;

        // See whether this is over a target.
        TouchLocation touch = touches.First();
        Point touchPoint = new Point(
            (int)touch.Position.X,
            (int)touch.Position.Y);
        for (int i = 0; i < targets.Length; i++)
        {
            Rectangle target = targets[i];
            if (target.Contains(touchPoint)) return i;
        }
    }

    if (touches.Count == 0) isTouching = false;
    return -1;
}

This method calls TouchPanel.GetState to get a list of current screen touches. If touches have occurred and the screen was not previously touched, a new touch has begun. In that case, the program records the fact that a touch is occurring for the next time it is called. It then loops through the target rectangles it was passed as a parameter. If the first touch’s position lies within a rectangle, the method returns that rectangle’s index.

You can look at touch positions other than the first one in the touches collection. Then, for example, you can take action if the user has swiped a finger through a rectangle, rather than only looking for a tap there.

The Marvelous Maze game looks for taps and finger presses to the sides of a ship’s wheel icon. Taps to the left and right of the wheel make the player turn left or right. Taps above and below the wheel make the player move forward or backward.

In addition to providing access to touches, the TouchPad object provides a ReadGesture method that returns information about a gesture at a higher conceptual level. For example, it can give the program information about whether the user tapped, double-tapped, pinched (moved two fingers closer together or farther apart), or flicked (dragged a finger and lifted it off the screen while it was still moving).

For more information on handling gestures, see “Gesture Support for Windows Phone” at http://msdn.microsoft.com/library/ff967546.aspx and “Detecting Gestures on a Multitouch Screen (Windows Phone)” at http://msdn.microsoft.com/library/ff827740.aspx.

This article explains several ways a game can interact with the player. It explains how to display images and text on the phone’s two-dimensional screen and how to display text as part of a texture displayed on a three-dimensional object in the scene. It also explains how to tell when the user taps on a two-dimensional area.

So far, the articles in this series have focused on fundamental mechanics. They have explained how to create and display simple objects by using triangles. It is easy enough to display a box made out of a handful of triangles that remain locked rigidly together. If the box moves, all of its pieces move.

The same techniques do not work as well for more complicated objects such as robots, castles, and starships composed of hundreds or even thousands of triangles. While you could in theory build complex extremely objects in code, in practice that would be tedious and time-consuming.

The next article in this series explains how you can use external tools to interactively build complex models. It also explains how your program can load those models to provide a rich environment without writing a lot of code.

Previous Article: Textures

Continue on to the Next Article: Models and Meshes

Show: