XNA Game-Themed Assignment (XGA)

XGA-400: Implementation Guide


Kelvin Sung
Computing and Software Systems
University of Washington, Bothell
ksung@u.washington.edu
Michael Panitz
Software Programming
Cascadia Community College
mpanitz@cascadia.ctc.edu

Topics Covered:

This document explains some of the implementation details found in the example solution to the XGA-400 assignment module.  Please follow this link to find out more about XGA-400 assignment module and all other XGA assignments. Topics covered in this implementation guide include:

 

Pre-Requisite:

It is assumed that you have read the following documents:

  1. The XNA Installation Guide: guides you download and install the XNA SDK and Game Studio Express IDE.

  2. The XnaAssignmentBase Guide: describes how to work with the XnaAssignmentBase class.

  3. If you have not already done so,  you may wish to look at implementation guides to earlier assignments: XGA-100, XGA-200, and XGA-300.


Please download the XNA_Tutorial zip file and unzip this file. You should open the project in the Game Studio Express IDE and follow the source code while reading the rest of this lab manual.

 

2. Implementation:

Compile and run the Othello_Guide application. When the application first starts, you will see a 6x6 square grid cells with a single red-dot resting at upper-right center cell of the 6x6 grid:

Notice that (note: this is the controller keyboard mapping):

Left ThumbStick: moves the red-dot.

A-Button: creates/draws a white circle in the grid cell that contains the red-dot.

B-Button: creates/draws a black circle in the grid cell that contains the red-dot.

As you adjust the thumbstick, the red-dot does not move smoothly from grid cell to grid cell, rather, it jumps from center of grid cell to center of grid cell. In addition, notice that you cannot draw the white/black circle outside of the grid region.

 

This application does not utilize any texture images and thus the Content/Resources/Textures folder is empty. The rest of this document explains how these functionality are implemented.

 


3. Coordinate and design of the Game Board:

Please refer to the above figure, similar to the game board design of XGA-300, here the world coordinate system is defined according to the position of the game board and size of each game cell. For aesthetic reasons,  we want the game board to appear somewhere closer to the middle of the application window. For this reason, we defined the lower-left corner of the application window to be (-8,-1) so that the (0,0) position will appear somewhere closer to the middle of the application window. As in XGS-300, unit size in the world is governed by the size of each game cell (1x1). In this way, game cell index and its geometric drawing position is related by a simple (0.5,0.5) offset. For example, the above diagram shows the the red-dot current position is located at game board index (3,3), while the geometric position for drawing the 0.9x0.9 rectangle is simple (3.5, 3.5).

 


4. The Sousrce Code Structure:

The source code folder structure and file names are similar to that of the TemplateAssignment project discussed in the XnaAssignmentBase guide. The only difference is that, in this case, the program source code is located in the OthelloGuide.cs file (instead of the Assignment.cs file). In addition,  the game board drawing functionality is abstracted in the PlayBoard.cs file. We will examine each of these files in detailed.

 


5. The Othello_Guide.cs:

This is the main file that subclass from the XnaAssignmentBase class. The main responsibility of this class includes interaction with the users. The important implementation details include:

  1. Constants and constructor.

  2. InitializeWorld()

  3. UpdateWorld()

  4. DrawWorld()

 

5a. Main Class Declaration and Constrution

In the beginning of Othello_Guide.cs file we can see:

public class Othello_Guide : XnaAssignmentBase {

    private const float WORLD_MIN_X = -8.0f;  

    private const float WORLD_MIN_Y = -1.0f; // lower left corner (-8, -1)

    private const float WORLD_WIDTH = 30.0f; // width is 30.0

 

    // Label A: Application state: circle and red-dot positions

private const int BOARD_RESOLUTION = 6;  // any even number will do

    private Vector2 m_Position;              // red-dot position

private Vector2 m_WhiteCircle;          // white circle position

private Vector2 m_BlackCircle;          // black circle position

private PlayBoard m_Board;              // the 6x6 grid

 

// Label B: Support input and output

    private Vector2 m_DeltaMove;            // accumulate thumbstick input

    private String m_Status;                // bottom status string

    private String m_TopStatus;             // top status string

    …                

Notice that in this case, the lower-left corner is defined to be (-8,-1). This coordinate is defined to allow each 1x1 grid-cell to be of reasonable size in the application window. The m_Board object would draw grid cells from 0 to BOARD_RESOLUTON-1 (e.g., 0 to 5). In this way, the drawing position and the grid-cell index are the same, i.e., cell[2][2] is located at coordinate position (2.0, 2.0). This overlapping of coordinate location with cell index allows easier implementation of the Othello program. In addition, with the defined coordinate system, the grid region will appear around the center of the application window.

·       Label A: declare variables for each of the objects we observe in the application window. We have seen the design of the game board and will examine the drawing of the PlayBoard  later.

·       Label B: m_DeltaMove is defined to support user interaction. We will examine these variables in detailed in UpdateWorld().

 

5b. The InitializeWorld() function:

protected override void InitializeWorld() {

    // Label A: Initial circles locations

    m_Position.X = 0.5f * BOARD_RESOLUTION + 0.5f; // Init red-dot position

    m_Position.Y = 0.5f * BOARD_RESOLUTION + 0.5f;

    m_WhiteCircle = new Vector2(1.5f, 1.5f);       // Init circle positions

    m_BlackCircle = new Vector2(0.5f, 0.5f);

    m_Board = new PlayBoard(BOARD_RESOLUTION);     // to draw the grid cells

 

    // Label B: Initial input state

    m_DeltaMove.X = 0.0f;      // no initial thumbStick movements

    m_DeltaMove.Y = 0.0f;

…

As shown above, here we initialize the instance. 

·       Label A: The initial position of the red-dot (m_Position) is set to around the center of the grid. We also initialize the white and black circle locations. Notice that to be located in cell (0,0), the BlackCircle location is initialized to (0.5, 0.5). Center of cell location is always 0.5 offset from the corresponding cell index. E.g., WhiteCircle is located in cell (1,1), that is why the circle location is (1.5, 1.5).

·       Label B: DeltaMove are initialized to reflect the initial state of the game controller (thumbstick has no movement).

 

5c. The UpdateWorld() function:

Recall that the UpdateWorld() function is responsible for keeping the application’s state up-to-date. In this case, we need to check the ThumbStick (moving the circle) and the Buttons  (move black or white circle). We have separate the discussion of the UpdateWorld() function into two parts to help us understand this funciton. The following listing details (Label A and B) discusses the handling of user input, while the next listing (Label C) discusses the details of computing for the white and black circle center.  

protected override void UpdateWorld() {

   // Label A: Compute thumbstick movement

   Vector2 delta = XnaAssignmentBase.GamePad.ThumbSticks.Left;

   m_DeltaMove += (0.4f * delta);   // Accumulates thumbStick movement

   int dx = (int)m_DeltaMove.X;     // Taking the integer component

   int dy = (int)m_DeltaMove.Y;     // of the thumbstick movement

   if ((dx != 0) || (dy != 0))  {

       m_DeltaMove.X = 0.0f;        // if movment registered, reset

       m_DeltaMove.Y = 0.0f;        // thumb stick movements

   }

   m_Position.X += dx;

   m_Position.Y += dy;

 

   // Label B: Draws black/white circle with A/B button clicks

   bool aClicked = XnaAssignmentBase.GamePad.ButtonAClicked();

   bool bClicked = XnaAssignmentBase.GamePad.ButtonBClicked();

   if ( aClicked || bClicked ) {

… // Label C: Compute Black/White circle position

   }

·       Label A: Poll the left ThumbStick state for the current movement (delta). When the accumulated delta exceeds 1.0f, the values are registered in dx and dy and DeltaMove is reset to 0.0. m_Position is updated with dx and dy to capture the accumulated thumbstick movement.

·       Label B: Test the conditions for drawing circle only if A or B button is pressed.

·       Label C: For clarity of presentation, code fragment here is presented separately in the following listing.

 

The following listing is a continuation of the above DrawWorld() function, where code fragment under Label A and B are shown above. In the following list, we examine the computation for White and Black circle center:

protected override void UpdateWorld() {

 // Label A: … compute thumbstick movements: detailed in above listing

 // Label B: … ensure one cirlce/button pressed: detailed in above listing

…

  // Label C: Compute Black/White circle position

   int row = (int)m_Position.X;

   int col = (int)m_Position.Y;

   if ((row >= 0) && (row < BOARD_RESOLUTION) &&  // red-dot in grid bound

       (col >= 0) && (col < BOARD_RESOLUTION)) {

     if (aClicked) {                               // Button A clicked

        m_WhiteCircle.X = row+0.5f;

        m_WhiteCircle.Y = col+0.5f;

     } else {                                     // Button B clicked

        m_BlackCircle.X = row+0.5f;

        m_BlackCircle.Y = col+0.5f;

     }

     m_Status = "White at:" + … + "    Black at:" + …;

  } else {

     m_Status = "Position: (" … ") is outside the game board. Try again!";

…

·       Label C: We first check to ensure that the red-dot position (m_Position) is within the bounds of the grid cell range. After which we offset the row and column index by 0.5 and either the change the white circle center (if button A clicked), or the black circle center (if button B clicked).

 

5d. The DrawWorld() function:

With the instance variables properly updated in UpdateWorld(), when DrawWorld() is invoked, all we need to ensure proper drawing functions are called:

protected override void DrawWorld() {

   m_Board.DrawBoard();                                // 6x6 grid cells

   DrawCircle(m_Position, 0.1f,… ,Color.Red, …);       // the red-dot

   DrawCircle(m_WhiteCircle, 0.5f, …, Color.White, …); // the white circle

   DrawCircle(m_BlackCircle, 0.5f, …, Color.Black, …); // the black circle

 

   EchoToTopStatus(m_TopStatus + @"    Current Position:" + m_Position);

   EchoToBottomStatus(m_Status);

}

 


6 The PlayBoard.cs file:

The PlayBoard class is a simple wrpper over the drawing of nxn squares.

class PlayBoard {

   private int m_Resolution;                     // resolution of the playboard

   private const float SIZE = 0.95f;             // cell size (boundary=0.1)

   

   public PlayBoard(int res){  m_Resolution = res;} // access resolution

   public void DrawBoard(){                         // draws the board

        Vector2 pos = new Vector2();

        for (int i = 0; i < m_Resolution; i++) {     // all horizontal cells

            pos.X = i + 0.5f;                        // horizontal center cells

            for (int j = 0; j < m_Resolution; j++) { // all vertical cells

                pos.Y = j + 0.5f;                    // vertical center of cells

                XnaAssignmentBase.DrawRectangle(pos, …); // draw rectangle

             …

As can be seen from the above code, for now, besides the construction the only other functionality is the looping over of the grids and drawing squares.  In the actual Othello game implementation, it is natural to implement the in-game logic in this class.

 


7. Important Observations:

The above implementation demonstrates:

·       Coordinate system: As mentioned before, it is important to note that the correspondence between coordinate position and grid cell index. In this case, we have designed the drawing coordinate very carefully to be between (-8,-1) to (22, 15.875) such that Othello boards with resolutions of 6x6 to 15x15 can be drawn. In general, when designing interactive graphics applications, it is important to identify and define a convenient drawing coordinate.

·        Representation of the board: The Othello playing board is drawn as a series of squares over background color. In PlayBoard, we have drawn each squares to be slightly smaller than 1.0 such that the background color is visible through the edges.

·       Current position: We have used the red-dot as an indication for current position. This is a simple simulation of the mouse pointer. Except that, in our case, the red-dot is restricted to occupy only center-positions of grid cells.

·       Drawing order: Notice that in the DrawWorld() function, m_Board is drawn first, followed by the White and Black circles, and the red-dot is drawn last.  This order ensures the black and white circles to be on top of the green grid cells. Because it is drawn last, the red-dot is always on-top of everything else.

 


8. Exercises:

1.     Instead of drawing black and white circles based on A and B buttons, modify UpdateWorld() such that the A-button directs black and white circle alternately. I.e., first A-button press will be directed to the white circle, the next A-button press will be directed to the black circle, and following one will be directed to the white circle again, etc.

 

2.     Instead of direct button pressed to the same circles, modify the given program such that each button press geneates a new circle.

 

3.     Following to Exercise 2, modify your program such that before inserting a new circle, you check to make sure the grid cell location does not already occupy an existing circle.

 


This document and the related materials are developed with support from Microsoft Research Computer Gaming Initiative under the Computer Gaming Curriculum in Computer Science RFP, Award Number 15871.