XNA Game-Themed Assignment (XGA)
XGA-100: 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 |
1. Topics Covered:
This document explains some of the implementation details found in the example solution to the XGA-100 assignment module. Please follow this link to find out more about XGA-100 assignment module and all other XGA assignments. Topics covered in this implementation guide include:
Controlling object movements based on controller trigger.
Using texture maps to show left/right facing.
Changing object color during run time.
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.
Please download the XNA_Tutorial zip file and unzip this file. It is best to open the project in the Game Studio Express IDE and follow the source code while reading the rest of this implementation guide.
2. Implementation:
Now, compile and run the XNA_Tutorial application. You should see:
Notice that (note: this is the controller keyboard mapping):
Left Trigger: When depressed, the
chameleon turns and face left and the circle
will move left.
Right
Trigger:
When depressed, the chameleon turns to face right and the circle will move right.
In addition, notice that the color at the center of the circle reflects the
region it is located in. This color will change as the circle center is moved across the boundary between the blue/pink regions. In this application, the
two color regions are defined by two constant color rectangles.
The rest of this document explains how these functionality are implemented.
3. Images in the Content/Resources/Textures Folder:
The Resources/Textures folder (in the IDE this can be found under the Content folder) contains two files, l_chameleon.png and r_chameleon.png. The two files represent the left (l__) facing and right (r_) facing version of the chameleon.
The above figure shows that depending on which of the left or right trigger is depressed the corresponding chameleon image will be mapped onto a square. The textured square will then overlap on top of a circle to create the effect of a "chameleon in a circle". The implementation for texture selection is explained in UpdateWorld, and the drawing operation is explained in DrawWorld.
4. The Source 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 SwitchAndMod_Guide.cs file (instead of the Assignment.cs file). We are going to examine the content of this file in detailed.
Recall that when working with the XnaAssignmentBase class, we need to pay attention to the following 6 specific items:
- Declaring and using libraries.
- Resources folders (Fonts and Textures) and files.
- Subclass from XnaAssignmentBase and proper calling of the base class constructor.
- Override the three functions:
- InitializeWorld()
- UpdateWorld()
- DrawWorld()
- Understand and know how to use the drawing functions defined in the XnaAssignmentBase class.
- Know how to call the input functions to work with the XBOX controller.
Since the provided source code is a working program, the above items has all been properly taken cared for. The functionality we are interested in are implemented in items 2, 3 and 4. We will examine these functions.
Main
Class Declaration and Construction
In the beginning of
SwitchAndMode_Guide.cs
file we can see:
|
public class
SwitchAndMode_Guide
: XnaAssignmentBase { private const float WORLD_MIN_X = 0.0f; // lower left
corner (0, 0) private const float WORLD_MIN_Y = 0.0f; private const float WORLD_WIDTH = 20.0f; // width is 20.0
Vector2 m_Position; // Position of
the circle Color m_UseColor; // Color for the circle center String m_Chameleon; // texture name for the chameleon square
float m_RectangleWidth; // width of the two constant color
rectangles float m_RectangleHeight; // Height of the two constant color
rectangles public SwitchAndMode_Guide() : base(new Vector2(WORLD_MIN_X,
WORLD_MIN_Y), WORLD_WIDTH) { // Notice: memory allocation and initialization should be performed // in InitializedWorld(). The constructor should be empty.
} |
Notice that in this case, we have
defined an intuitive coordinate system where the lower-left corner is the
origin (0,0), and we have defined the width of the drawing area to be 20 units.
4a. The InitializeWorld()
function:
|
protected override void
InitializeWorld() { // initialize the application state m_Position.X =
5.0f; m_Position.Y =
6.0f; m_RectangleHeight = WorldMax.Y - WorldMin.Y; // rectangle covers the height m_RectangleWidth = WORLD_WIDTH / 2.0f; // width is half of area
m_Chameleon = "l_chameleon"; // init: look left } |
As shown above, we initialize the initial circle
position and the width/height of the two constant color rectangles (the pink and
blue rectangles in the background). Notice that the rectangle height is the
entire height of the drawing area and the width of each rectangle is half that
of the drawing area width. In this way, the two rectangles will cover the entire
drawing area.
4b. 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 Triggers to
move the circle:
|
protected override void
UpdateWorld() { // Label A: Poll the left/right triggers float deltaL = XanAssignmentBase.GamePad.Triggers.Left; float deltaR = XanAssignmentBase.GamePad.Triggers.Right; m_Position.X +=
deltaR - deltaL; // Label B: choose the left/right chameleon texture if (deltaR > 0.0f) m_Chameleon = "r_chameleon"; else if (deltaL > 0.0f) m_Chameleon = "l_chameleon";
// Label
C: determine the color to use for the circle center iff (m_Position.X < m_RectangleWidth) m_UseColor = Color.LightBlue; else m_UseColor = Color.Pink; … |
·
Label A:
We poll the left and right triggers’ states and use those values to update
the circle’s center position. Notice that the deltaL value is subtracted from the m_Position.X. This means deltaL
(left trigger state) will decrease m_Position.X,
or it will move the circle towards the left.
· Label B: We use the values of deltaL/R as a hint to determine which of the left/right chameleon texture to use. For example, a value of greater than zero signifies the right trigger is depressed and thus we should use the right-facing chameleon image.
· Label C: We check the current circle position to determine the color to use for drawing the center of the circle.
Note: Very importantly, we did not issue any drawing commands! Drawing should only be performed in the DrawWorld() function.
4c. The DrawWorld() function:
With the instance variables properly updated in UpdateWorld(), when DrawWorld() is invoked, all we need to ensure is that the circle is
properly drawn:
|
protected override void
DrawWorld() { Vector2 rCenter = new
Vector2(5.0f, m_RectangleHeight/2.0f); // Label A: Draws the two rectangle
DrawRectangle(rCenter, …, …, …, Color.LightBlue,
Color.LightBlue, null); rCenter.X +=
m_RectangleWidth;
DrawRectangle(rCenter, …, …, …, Color.Pink,
Color.Pink, null); // Label B: draws circle with m_UseColor center and the textured rectangle DrawCircle(m_Position, 2.0f, 0.0f, Color.White, m_UseColor, null); DrawRectangle(m_Position, 3.0f, 3.0f, …, …, …, m_Chameleon); } |
·
Label A:
We compute rCenter, the center
position of the left rectangle, draws the left rectangle covering the entire
left half of the drawing area. We then update the rCenter value to reflect the center of the right rectangle and do
the same for the entire right side of the drawing area.
· Label B: We draw the circle at m_Position. Notice that the color we use for the center of the circle is the m_UseColor that we have computed in UpdateWorld(). The rectangle is drawn with the texture m_Chameleon string after the circle. In this way, the textured rectangle will cover the circle and thus creating the "chameleon in circle" effect.
Note: Very importantly, we did not change any instance variables in the DrawWorld() function. Instance variables should only be changed in the UpdateWorld() function.
5. Important Observations:
The above implementation demonstrates:
· User control of object: notice that the position of the circle is controlled by the user. Values from the input devices (user’s choice) are used to modify the current values of the circle.
· Update vs Draw: once again, notice the clear separation between UpdateWorld() and DrawWorld(), one function changes the instance variables without drawing while the other function draws without changing any instance variables!
6. Exercises:
1. Instead of WORLD_WIDTH of 20, change it 30. Now modify all necessary values (including rectangle width) such that the look and behavior of this program remains the same. The important lesson from this exercise is that we can implement the same functionality with different coordinate dimensions. Depending on the graphics and interaction of your applications (assignments) you should design the coordinate range/dimension accordingly.
2. Add a third color region (e.g., LightGreen) by drawing a third rectangle covering the center third of the drawing area. Change the width of the two existing rectangles such that they only cover the left and right one-third of the drawing area. Now, in UpdateWorld(), include the logic to compute m_UseColor for the circle such that its center color will properly reflect that of the three region as user moves the circle left/right.
7. Start the Assignment:
Here is the starter project where the students should start their work to complete the assignment. We have isolated the logic for computing color into the BackgroundStrip.cs file such that students only need to work on this file. In addition, we have clearly identified regions where students should add in their code: it's between the note that says "STUDENTS: YOU NEED TO WRITE YOUR CODE BELOW:", and "STUDENTS: YOU NEED TO WRITE YOUR CODE ABOVE:" with . In this way students do not need to change anything except for the code that they add in order to complete the assignment.
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.