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 XNACS1Lib Guide: describes how to work with the XNACS1Lib class.

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


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 discussed in the XNACS1Lib guide. The only difference is that, in this case, the program source code is located in the OthelloGuide.cs file (instead of the Game1.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 XNACS1Base class. The main responsibility of this class includes interaction with the users. The important implementation details include:

  1. Constants and Instance Variables.

  2. InitializeWorld()

  3. UpdateWorld()

 

5a. Main Class Declaration and Constants

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

public class Othello_Guide : XNACS1Base {

    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 XNACS1Circle m_Pointer;          // red-dot position

private XNACS1Circle m_WhitePiece        // white circle position

private XNACS1Circle m_BlackPiece        // 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 initialization 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() {

    World.SetWorldCoordinate(new Vector2(WORLD_MIN_X, WORLD_MIN_Y), WORLD_WIDTH);

    World.SetBackgroundColor(Color.LightSkyBlue);
 

    // Label A: Create the board

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

           

    // Label B: Create the white and black pieces

    m_BlackPiece = new XNACS1Circle(new Vector2(0.5f, 0.5f), 0.5f);

    m_BlackPiece.CenterColor = m_BlackPiece.OutsideColor = Color.Black;

    m_WhitePiece = new XNACS1Circle(new Vector2(1.5f, 1.5f), 0.5f);

    m_WhitePiece.CenterColor = m_WhitePiece.OutsideColor = Color.White;

 

    // Label C: red current position indicator

    m_Pointer = new XNACS1Circle(new Vector2(...), 0.1f);

    m_Pointer.CenterColor = Color.Red;

    m_Pointer.OutsideColor = Color.LightSalmon;

 

    // Label D: Initial input state

    m_DeltaMove = new Vector2();

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

    m_DeltaMove.Y = 0.0f;

 

…

As shown above, here we initialize the instance. 

·       Label A: Allocate memory for the game board. Please refer to the play board constructor for more details.

·       Label B: The initial position of black and white pieces. Notice that to be located in cell (0,0), the BlackPiece location is initialized to (0.5, 0.5). Center of cell location is always 0.5 offset from the corresponding cell index. E.g., WhitePiece is located in cell (1,1), that is why the circle location is (1.5, 1.5).

·       Label C: The initial position of the red-dot (m_Pointer) is set to around the center of the grid.

·       Label D: 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). To help us understand this function, we have separated the discussion of the UpdateWorld() function into two parts. 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 pieces' centers.  

protected override void UpdateWorld() {

   // Label A: Compute thumbstick movement

   Vector2 delta = XNACS1Base.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 = XNACS1Base.GamePad.ButtonAClicked();

   bool bClicked = XNACS1Base.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 UpdateWorld() function, where code fragment under Label A and B are shown above. In the following list, we examine the computation for White and Black pieces' centers:

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_Pointer.CenterX;

   int col = (int)m_Pointer.CenterY;

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

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

     if (aClicked) {                               // Button A clicked

        m_WhitePiece.CenterX = row+0.5f;

        m_WhitePiece.CenterY = col+0.5f;

     } else {                                     // Button B clicked

        m_BlackPiece.CenterX = row+0.5f;

        m_BlackPiece.CenterY = 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_Pointer) 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).

 


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;                       // resolution of 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

             XNACS1Rectangle cell = new XNACS1Rectangle(pos, …); // define the cell rectangle

             …

As can be seen from the above code, the only functionality is the looping over of the grids and defining the 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 InitializeWorld(), m_Board is defined first, followed by the White and Black pieces, and the red-dot is drawn last.  This order ensures the black and white pieces 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 generates 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.