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:
Working with a current position (mouse-pointer metaphor)
Working with convenient coordinate system (verifying results from XGA-300 discussion).
Separating graphics from game-logic: 2D array functionality.
Pre-Requisite:
It is assumed that you have read the following
documents:
The XNA Installation Guide: guides you download and install the XNA SDK and Game Studio Express IDE.
The XnaAssignmentBase Guide: describes how to work with the XnaAssignmentBase class.
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:
Constants and constructor.
InitializeWorld()
UpdateWorld()
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 applications 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).
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); } |
The PlayBoard class
is a simple wrpper over the drawing of nxn squares.
|
class PlayBoard { private int m_Resolution; private const float SIZE =
0.95f; public
PlayBoard(int res){ m_Resolution = res;} public void DrawBoard(){ Vector2
pos = new Vector2(); for (int i = 0; i < m_Resolution; i++) { // all horizontal cells pos.X = i + 0.5f; for (int j = 0; j < m_Resolution; j++) { // all vertical cells pos.Y = j + 0.5f; // vertical center of cells XnaAssignmentBase.DrawRectangle(pos,
);
|
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.