XNA Game Studio
XNA Game Studio
By:
R.G. (Dick) Baldwin
XNA Game Studio
By:
R.G. (Dick) Baldwin
Online:
< http://cnx.org/content/col11634/1.6/ >
OpenStax-CNX
This selection and arrangement of content as a collection is copyrighted by R.G. (Dick) Baldwin. It is licensed under
the Creative Commons Attribution License 4.0 (http://creativecommons.org/licenses/by/4.0/).
Collection structure revised: February 28, 2014
PDF generated: May 10, 2016
For copyright and attribution information for the modules contained in this collection, see p. 321.
Table of Contents
1 Xna0095-Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
2 Xna0100-Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
3 Xna0102-What is C and Why Should You Care . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
4 Xna0104-What is OOP and Why Should You Care? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
5 Xna0106-Encapsulation in C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
6 Xna0108-Inheritance in C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
7 Xna0110-Polymorphism Based on Overloaded Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
8 Xna0112-Type Conversion, Casting, and Assignment Compatibility . . . . . .. . . . . . . . . . . . . . . . . . 75
9 Xna0114-Runtime Polymorphism through Class Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
10 Xna0116-Runtime Polymorphism and the Object Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
11 Xna0118-The XNA Framework and the Game Class . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . 111
12 Xna0120-Moving Your Sprite and using the Debug Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
13 Xna0122-Frame Animation using a Sprite Sheet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
14 Xna0124-Using Background Images and Color Key Transparency . . . . . . .. . . . . . . . . . . . . . . . . 181
15 Xna0126-Using OOP - A Simple Sprite Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . 209
16 Xna0128-Improving the Sprite Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . 229
17 Xna0130-Collision Detection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . 257
18 Xna0132-A Simple Game Program with On-Screen Text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
Attributions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .321
iv
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
Chapter 1
Xna0095-Preface
1
Revised: Wed May 04 09:58:08 CDT 2016
This page is part of a Book titled XNA Game Studio
2
.
1.1 Preface to XNA Game Studio Collection
This module is the rst in a collection of modules designed primarily for teaching GAME 1343 Game and
Simulation Programming I at Austin Community College in Austin, TX. These modules are intended
to supplement and not to replace the textbook.
The modules were originally published for use with XNA 3.1 , were upgraded for use with XNA 4.0 ,
and as of May 2016 are being upgraded for use with XNA 4.0 Refresh .
-end-
1 This content is available online at <http://cnx.org/content/m49466/1.2/>.
2 http://cnx.org/contents/GY804-eY
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
1
2
CHAPTER 1. XNA0095-PREFACE
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
Chapter 2
Xna0100-Getting Started
Revised: Wed May 04 10:15:51 CDT 2016
This page is part of a Book titled XNA Game Studio
2
1
.
2.1 Table of Contents
• Table of Contents (p. 3)
• Preface (p. 3)
· Viewing tip (p. 4)
* Figures (p. 4)
• Preview (p. 4)
• Download and installation instructions (p. 4)
·
·
·
·
·
·
·
·
Introduction (p. 4)
Software upgrade (p. 4)
XNA installation instructions (p. 5)
Cleaning o the old software (p. 5)
Downloading Microsoft Visual C# 2010 Express Edition (p. 5)
Preparing the ISO le for software installation (p. 5)
Installing Microsoft Visual C# 2010 Express Edition (p. 6)
Downloading and installing XNA 4.0 Refresh (p. 6)
• Test your installation (p. 6)
· Skeleton code (p. 7)
· Run your program (p. 7)
· Run your program outside the IDE (p. 8)
• Run my program (p. 9)
• Miscellaneous (p. 9)
2.2 Preface
This module is one in a collection of modules designed primarily for teaching GAME 1343 Game and
Simulation Programming I at Austin Community College in Austin, TX. These modules are intended
to supplement and not to replace the textbook.
1 This content is available online at <http://cnx.org/content/m49467/1.4/>.
2 http://cnx.org/contents/GY804-eY
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
3
4
CHAPTER 2. XNA0100-GETTING STARTED
The modules were originally published for use with XNA 3.1 , were upgraded for use with XNA 4.0 ,
and as of May 2016 are being upgraded for use with XNA 4.0 Refresh .
2.2.1 Viewing tip
I recommend that you open another copy of this module in a separate browser window and use the following
links to easily nd and view the Figures while you are reading about them.
2.2.1.1 Figures
• Figure 1 (p. 6) . XNA 4.0 IDE.
• Figure 2 (p. 7) . XNA game window.
• Figure 3 (p. 8) . Project le structure.
2.3 Preview
According to Microsoft, "Microsoft XNA Game Studio 4.0 makes it easier than ever to create great video
games for Windows-based PCs, Xbox 360 consoles, and Windows Phone."
The purpose of this module is to help you get started programming using the XNA Game Studio.
2.4 Download and installation instructions
The download and installation information regarding the Microsoft website changes frequently. The information in this section is current as of May 2016.
2.4.1 Introduction
I typically teach this course once each year in the Summer session via Distance Learning. For the past several
years, students have reported that it has become increasingly dicult to nd and download the necessary
Microsoft software for the course. In preparation for the Summer 2016 session, I decided to do the following:
• Clean the old software o of my computer.
• Download and install new software.
• Document the process for the benet of students who may need to do the same.
2.4.2 Software upgrade
I also decided to upgrade from XNA 4.0 to a newer product named Microsoft XNA Game Studio 4.0 Refresh
3 . Here is what Microsoft has to say about the newer product:
Microsoft XNA Game Studio 4.0 Refresh updates XNA Game Studio 4.0 to x bugs and add
support for developing games that target Windows Phone OS 7.1 and developing games in Visual
Basic.
This course doesn't address Visual Basic or Windows Phone, but bug xes are always welcome.
3 https://www.microsoft.com/en-us/download/details.aspx?id=27599
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
5
2.4.3 XNA installation instructions
Microsoft's installation instructions
1.
2.
3.
4.
4
for the new software read as follows:
Install Microsoft Visual Studio 2010.
Obtain the latest updates for Visual Studio from Microsoft Update.
Download and run the Microsoft XNA Game Studio 4.0 Refresh installer.
Follow the setup instructions.
As you can see from the installation instructions, Microsoft Visual Studio 2010 is required. This has been
a big part of the student's problems in downloading and installing the software in previous semesters.
However, a dierent page 5 indicates that Microsoft Visual C# 2010 Express Edition will suce in
place of Microsoft Visual Studio 2010 . That page reads as follows:
XNA Game Studio 4.0 Refresh works with any of the following Microsoft Visual Studio 2010 products.
•
•
•
Microsoft Visual Studio 2010 Express for Windows Phone
Microsoft Visual C# 2010 Express Edition
Microsoft Visual Studio 2010 Professional Edition
I will have more to say about this later.
2.4.4 Cleaning o the old software
I began by using the software removal procedures in the Control Panel to remove three dierent Microsoft
XNA items and one item titled Microsoft Visual C# 2010 Express - ENU. At that point, my computer
had no XNA capability and no Visual C# capability.
2.4.5 Downloading Microsoft Visual C# 2010 Express Edition
A big part of the student's problems in previous semesters has been the diculty of nding and downloading
Microsoft Visual C# 2010 Express Edition . The only way that I have found to download it is by clicking the
following link: Visual Studio 2010 Express All-in-One ISO 6 and saving the le named VS2010Express1.iso
that is downloaded.
This le not only contains Microsoft Visual C# 2010 Express Edition , it also contains the express
editions for several other languages as well. However, it is not an installable product at this point.
2.4.6 Preparing the ISO le for software installation
There is more than one way to install the software in the ISO le. You might benet by doing some online
research into how to install the software in an ISO le. Here is how I did it with Windows 7:
1. Insert a blank DVD in the DVD burner. (A blank CD would probably also work but I didn't have
one.)
2. Right-click on the ISO le.
3. Select Open With...
4. Select Windows Disk Image Burner
5. Follow the disk burning instructions.
6. Remove the DVD from the burner when complete and label it Visual Studio 2010 Express or something
similar.
The resulting DVD contains an installable version of Visual Studio 2010 Express , which in turn contains
Visual C# 2010 Express and some other programs as well.
4 https://www.microsoft.com/en-us/download/details.aspx?id=27599
5 https://msdn.microsoft.com/en-us/library/bb203916.aspx
6 http://download.microsoft.com/download/1/E/5/1E5F1C0A-0D5B-426A-A603-1798B951DDAE/VS2010Express1.iso
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
6
CHAPTER 2. XNA0100-GETTING STARTED
2.4.7 Installing Microsoft Visual C# 2010 Express Edition
Insert the DVD in the DVD reader and select Setup.hta . You should then be given a choice as to which
of the express edition programs you want to install.
Select Visual C# and follow the installation instructions.
When the installation is complete, you should be able to see Microsoft Visual C# 2010 Express if you
open the Start menu and enter 2010 in the search box.
At this point, you can run the program, open the Help menu, and select Check for Updates to satisfy
the second item under XNA installation instructions (p. 5) .
2.4.8 Downloading and installing XNA 4.0 Refresh
Click here 7 to visit a page where you can download the setup le named
XNAGS40_setup.exe for Microsoft XNA Game Studio 4.0 Refresh . Download, save, double click
the le, and follow instructions to install the software.
When the installation is complete, XNA should be merged with Visual C# and you should be able to
begin creating the programs for the course.
I have conrmed that all of the assignment and sample program for this course, which were originally
written using XNA 4.0 execute properly under XNA 4.0 Refresh .
This is the easy part.
2.5 Test your installation
Once you have the software installed, start Visual C# from the Windows Start menu. Pull down the
menu and select New Project . A dialog box will appear. Select "XNA Game Studio 4.0" on the
left side of the dialog and select "Windows Game (4.0)" on the right side of the dialog."
Name your project First Project , specify a location for your new project and click the OK button.
An IDE should appear looking something like Figure 1 (p. 6) . (Note that Figure 1 (p. 6) has been reduced,
which causes the text to be dicult to read.)
File
Figure 1
. XNA 4.0 IDE.
7 https://www.microsoft.com/en-us/download/details.aspx?id=27599
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
7
2.5.1 Skeleton code
Skeleton code for a Windows game project will have been created in the editor portion of the IDE. I will
explain this code in detail in a future module.
2.5.2 Run your program
Run your program by selecting Start Debugging on the Debug menu. If everything is working properly,
the game window shown in Figure 2 (p. 7) should appear on your computer screen.
Figure 2
. XNA game window.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
8
CHAPTER 2. XNA0100-GETTING STARTED
2.5.3 Run your program outside the IDE
Once you have run your program successfully from inside the IDE, your project le structure should look
similar to Figure 3 (p. 8) .
Figure 3
. Project le structure.
At that point, you should be able to double click the exe le highlighted in Figure 3 (p. 8) and cause your
program to run.
Congratulations. You're now ready to start learning how to program using XNA.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
2.6 Run my program
9
to download a zip le containing my version of the program. Extract the folder named First
Project from the zip le and save it somewhere on your disk. Start Visual C# 2010 Express and
select Open Project... from the File menu. Navigate to the project folder and select the le named
First Project.sln . This should cause the project to open and be ready to run or debug as described
Click here
8
above.
Many of the modules in this collection will provide my version of a project that can be downloaded and
run in this manner.
2.7 Miscellaneous
This section contains a variety of miscellaneous information.
Housekeeping material
• Module name: Xna0100-Getting Started
• File: Xna0100.htm
• Published: 02/24/14
Disclaimers: Financial : Although the Connexions site makes it possible for you to download
a PDF le for this module at no charge, and also makes it possible for you to purchase a pre-printed
version of the PDF le, you should be aware that some of the HTML elements in this module may
not translate well into PDF.
I also want you to know that, I receive no nancial compensation from the Connexions website even
if you purchase the PDF version of the module.
In the past, unknown individuals have copied my modules from cnx.org, converted them to Kindle
books, and placed them for sale on Amazon.com showing me as the author. I neither receive
compensation for those sales nor do I know who does receive compensation. If you purchase such
a book, please be aware that it is a copy of a module that is freely available on cnx.org and that it
was made and published without my prior knowledge.
Aliation : I am a professor of Computer Information Technology at Austin Community College
in Austin, TX.
-end-
8 http://cnx.org/content/m49467/latest/FirstProject.zip
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
10
CHAPTER 2. XNA0100-GETTING STARTED
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
Chapter 3
Xna0102-What is C and Why Should
1
You Care
Revised: Wed May 04 14:28:53 CDT 2016
This page is part of a Book titled XNA Game Studio
2
.
3.1 Table of Contents
• Table of Contents (p. 11)
• Preface (p. 11)
· Viewing tip (p. 12)
* Listings (p. 12)
• Technical level of the course (p. 12)
· I will assume... (p. 12)
· I will also assume... (p. 12)
• What is C#? (p. 12)
• Why should you care about C#? (p. 13)
·
·
·
·
·
·
Complex IDE (p. 13)
Is there a simpler way? (p. 13)
Fully object oriented (p. 13)
Massive and complex documentation package (p. 14)
Indirection (p. 14)
C# is the language of XNA (p. 14)
• Run the program (p. 14)
• Miscellaneous (p. 14)
3.2 Preface
This module is one in a collection of modules designed primarily for teaching GAME 1343 Game and
Simulation Programming I at Austin Community College in Austin, TX. These modules are intended
to supplement and not to replace the textbook.
An earlier module titled Getting Started 3 provided information on how to get started programming with
1 This content is available online at <http://cnx.org/content/m49469/1.4/>.
2 http://cnx.org/contents/GY804-eY
3 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
11
12
CHAPTER 3. XNA0102-WHAT IS C AND WHY SHOULD YOU CARE
Microsoft's XNA Game Studio.
3.2.1 Viewing tip
I recommend that you open another copy of this module in a separate browser window and use the following
link to easily nd and view the listing while you are reading about it.
3.2.1.1 Listings
• Listing 1 (p. 13) . Hello World in C#.
3.3 Technical level of the course
This is not a beginning programming course. The ocial prerequisite for this course is a course in programming fundamentals using either C++ or Python. Those courses use C++ or Python strictly as procedural
programming languages. The courses are currently taught using very simple IDEs.
3.3.1 I will assume...
I will assume that students in this course already know how to program using either C++ or Python
as a procedural programming language and that they already understand such fundamental concepts as
programming logic, functions, parameter passing, etc.
3.3.2 I will also assume...
At the same time I will also assume that the students in this course know very little if anything about the
following topics:
• The use of a complex IDE.
• The use of any programming language other than either Python or C++ solely as a procedural programming language.
• Object-oriented programming.
• Pointers or any other form of indirection.
• Large-scale programming documentation.
These are all topics that will be important in learning how to make eective use of the Microsoft XNA Game
Studio software.
3.4 What is C#?
According to Wikipedia,
"C# (pronounced "see sharp") is a multi-paradigm programming language encompassing imperative, functional, generic, object-oriented (class-based), and component-oriented programming
disciplines. It was developed by Microsoft within the .NET initiative and later approved as a standard by Ecma (ECMA-334) and ISO (ISO/IEC 23270). C# is one of the programming languages
designed for the Common Language Infrastructure."
"C# is intended to be a simple, modern, general-purpose, object-oriented programming language.[3] Its development team is led by Anders Hejlsberg, the designer of Borland's Turbo Pascal,
who has said that its object-oriented syntax is based on C++ and other languages.[4] James Gosling,
who created the Java programming language in 1994, called it an 'imitation' of that language.[5]
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
13
The most recent version is C# 3.0, which was released in conjunction with the .NET Framework
3.5 in 2007. The next proposed version, 4.0, is in development."
That is certainly a mouthful.
3.5 Why should you care about C#?
From the viewpoint of students in this course, here is some of what C# really is and why you should care
about C#.
3.5.1 Complex IDE
To begin with, C# was designed to run in a large complex IDE. For example, using the Visual C# IDE to
create the simple Hello World console program shown in Listing 1 (p. 13) generates about thirteen dierent
les in about ten dierent folders.
Listing 1
using
using
using
using
. Hello World in C#.
System;
System.Collections.Generic;
System.Linq;
System.Text;
namespace Hello01{
class Hello01{
static void Main(string[] args){
Console.WriteLine("Hello C# World");
//Press any key to dismiss console screen.
Console.ReadKey();
}//end Main
}//end class
}//end namespace
3.5.2 Is there a simpler way?
While is it possible to develop simple C# programs outside the IDE (see Baldwin's C# programming
tutorials 4 ), there are probably less than a few hundred people worldwide who do it that way.
The transition from the relatively simple IDE used in the prerequisite course to the very complex Visual
C# IDE may be a stretch for some students.
3.5.3 Fully object oriented
C# is an object-oriented programming language. Unlike C++, it is not possible to develop C# projects
without taking the object-oriented nature of C# into account.
Perhaps more important, it is not possible to develop XNA programs without using some of the more
complex aspects of OOP, such as the overriding of virtual methods.
4 http://www.dickbaldwin.com/tocCsharp.htm
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
14
CHAPTER 3. XNA0102-WHAT IS C AND WHY SHOULD YOU CARE
3.5.4 Massive and complex documentation package
The object-oriented nature of C# becomes very apparent when attempting to locate something in the
massive XNA documentation package. The documentation is written from an object-oriented viewpoint.
For example, see the documentation for the Microsoft.Xna.Framework.Graphics namespace here 5
and see the documentation for the SpriteBatch class here 6 .
3.5.5 Indirection
C# makes extensive use of indirection. While the indirection scheme used in C# is not as complicated as
C++ pointers, for someone new to indirection, understanding the concept can sometimes be a challenging
task.
3.5.6 C# is the language of XNA
C# is the programming language of the Microsoft XNA Game Studio. To write XNA programs, you must
know how to write C# programs.
That is why you should care about C#. I won't try to turn you into a C# programming expert in
this course. We have other courses at the college that concentrate solely on C# programming. Instead, I
will simply try to help you learn enough about C# programming that you can do a credible job of writing
programs using the XNA Game Studio.
3.6 Run the program
Click here
7
to download a zip le containing my version of the program. Extract the folder named
ConsoleApplication1 from the zip le and save it somewhere on your disk. Start Visual C# 2010
Express and select Open Project... from the File menu. Navigate to the project folder and select
the le with the extension of .sln . This should cause the project to open and be ready to run or debug
as described in the earlier module titled Getting Started
8
.
3.7 Miscellaneous
This section contains a variety of miscellaneous information.
Housekeeping material
• Module name: Xna0102-What is C# and Why Should You Care
• File: Xna0102.htm
• Published: 02/24/14
Disclaimers: Financial : Although the Connexions site makes it possible for you to download
a PDF le for this module at no charge, and also makes it possible for you to purchase a pre-printed
version of the PDF le, you should be aware that some of the HTML elements in this module may
not translate well into PDF.
I also want you to know that, I receive no nancial compensation from the Connexions website even
if you purchase the PDF version of the module.
5 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.aspx
6 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.spritebatch_members.aspx
7 http://cnx.org/content/m49469/latest/ConsoleApplication1.zip
8 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
15
In the past, unknown individuals have copied my modules from cnx.org, converted them to Kindle
books, and placed them for sale on Amazon.com showing me as the author. I neither receive
compensation for those sales nor do I know who does receive compensation. If you purchase such
a book, please be aware that it is a copy of a module that is freely available on cnx.org and that it
was made and published without my prior knowledge.
Aliation : I am a professor of Computer Information Technology at Austin Community College
in Austin, TX.
-end-
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
16
CHAPTER 3. XNA0102-WHAT IS C AND WHY SHOULD YOU CARE
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
Chapter 4
Xna0104-What is OOP and Why Should
1
You Care?
Revised: Wed May 04 17:44:09 CDT 2016
This page is part of a Book titled XNA Game Studio
2
.
4.1 Table of Contents
• Table of Contents (p. 17)
• Preface (p. 18)
· Viewing tip (p. 18)
* Figures (p. 18)
* Listings (p. 19)
• What is OOP? (p. 19)
·
·
·
·
·
·
·
·
·
·
·
What is object-oriented programming (OOP)? (p. 19)
My answer (p. 19)
An anecdotal description (p. 19)
Atomic and non-atomic objects (p. 19)
Your job - assemble the objects (p. 19)
Objects working together (p. 20)
Creating a model (p. 20)
Objects must be designed and manufactured (p. 20)
A class is analogous to manufacturing drawings (p. 20)
A large library of classes (p. 20)
Classes of your own design (p. 20)
• Why should you care about OOP? (p. 20)
· The XNA documentation package is huge (p. 21)
· The good news (p. 21)
• Three important concepts (p. 21)
· Encapsulation example (p. 21)
* Only the interface is exposed (p. 21)
* How is it implemented? (p. 21)
1 This content is available online at <http://cnx.org/content/m49475/1.3/>.
2 http://cnx.org/contents/GY804-eY
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
17
18
CHAPTER 4. XNA0104-WHAT IS OOP AND WHY SHOULD YOU CARE?
* Expose the interface and hide the implementation (p. 22)
· Inheritance example (p. 22)
* OOP lingo (p. 22)
* Extending the Game class (p. 22)
* Reuse, don't reinvent (p. 22)
· Polymorphism example (p. 22)
* Select Drive to go forward (p. 23)
• Object-oriented programming vocabulary (p. 23)
• Discussion and sample code (p. 23)
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
•
•
•
•
You can learn a lot... (p. 24)
A new class denition (p. 24)
The Main method (p. 24)
Methods versus functions (p. 24)
Denition of the Main method (p. 24)
The WriteLine method (p. 24)
The Console class (p. 24)
The System namespace (p. 25)
Members of the Console class (p. 25)
The dot operator (p. 25)
Namespaces (p. 25)
The "using" declaration (p. 26)
Not much help in this case (p. 26)
Dening your own namespace (p. 26)
The project le structure (p. 26)
The Hello01 namespace (p. 28)
The call to the ReadKey method (p. 28)
Run the program (p. 28)
Run my program (p. 28)
Summary (p. 28)
Miscellaneous (p. 28)
4.2 Preface
This module is one in a collection of modules designed primarily for teaching GAME 1343 Game and
at Austin Community College in Austin, TX. These modules are intended
to supplement and not to replace the textbook.
An earlier module titled Getting Started 3 provided information on how to get started programming with
Microsoft's XNA Game Studio.
Simulation Programming I
4.2.1 Viewing tip
I recommend that you open another copy of this module in a separate browser window and use the following
links to easily nd and view the Figures and Listings while you are reading about them.
4.2.1.1 Figures
• Figure 1 (p. 26) . Project le structure.
3 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
19
4.2.1.2 Listings
• Listing 1 (p. 22) . Extending the Game class.
• Listing 2 (p. 23) . Hello World in C#.
4.3 What is OOP?
4.3.1 What is object-oriented programming (OOP)?
If you Google this question, you will get hundreds of answers. Here is one of those answers.
According to Wikipedia,
"Object-oriented programming (OOP) is a programming paradigm that uses "objects" - data structures consisting of data elds and methods together with their interactions - to design applications
and computer programs. Programming techniques may include features such as information hiding,
data abstraction, encapsulation, modularity, polymorphism, and inheritance."
4.3.2 My answer
Here is my answer along with an anecdotal description. Unlike earlier programming styles, object-oriented
programming is a programming style that mimics the way most people think and work.
An OOP solution to a problem should resemble the problem, and observers of the solution should be able
to recognize the problem without necessarily knowing about it in advance.
For example, an OO program that deals with banking transactions should be recognizable on the basis
of the objects that it uses, such as deposit objects, withdrawal objects, account objects, etc.
4.3.3 An anecdotal description
If you have ever assembled a playscape in your back yard, this should sound familiar to you.
When you opened the large boxes containing the playscape, hundreds of objects spilled onto the ground.
Those objects may have consisted of braces, chains, swing seats, slides, screws, nuts, bolts, washers, climbing
ropes, ladder rungs, and other assorted objects.
4.3.4 Atomic and non-atomic objects
I will refer to (most of) the kinds of object that I have described in the above list as atomic objects.
What I mean by that is that they can't be easily subdivided into smaller objects without destroying their
functionality.
If you were lucky, some of the objects in the box may not have been atomic objects. Instead they may
have been pre-assembled arrangements of atomic objects such as an assembly of seats and braces representing
a object on which two children can swing together.
4.3.5 Your job - assemble the objects
Your job was to assemble those hundreds of atomic and non-atomic objects into a nal object which you
proudly referred to as "The Playscape."
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
20
CHAPTER 4. XNA0104-WHAT IS OOP AND WHY SHOULD YOU CARE?
4.3.6 Objects working together
It has been said that a successful object-oriented program consists of a bunch of cooperating software objects
working together to achieve a specied behavior.
The overall behavior of the program is the combination of behaviors of the individual objects. For
example, some objects may acquire input data, other objects may compute and produce output data, while
other objects may display the output data.
It could also be said that a playscape consists of a bunch of hardware objects working together to achieve
a specied behavior. The overall behavior of the playscape is the combination of behaviors of the individual
objects. For example, the behavior of some of the braces is to stand strong and not bend, while the behavior
of a swing chain is to be exible and move in a prescribed way.
4.3.7 Creating a model
One of the tasks often faced by an object-oriented programmer is to assemble software objects into a model
that represents something that exists in the real world. As a very visual example, you might be asked to
create an advertising web page showing an animated software model of the playscape that you assembled in
your back yard. With the playscape, you were simply required to assemble the existing objects. However,
in the object-oriented programming world, you must do more than just assemble objects.
4.3.8 Objects must be designed and manufactured
Getting back to the playscape, each of the objects for the playscape was manufactured before being shipped
to you. Even before that, each object was designed by someone and a set of manufacturing drawings was
probably created so that the object could be mass produced.
4.3.9 A class is analogous to manufacturing drawings
In OOP, there is a direct analogy to the manufacturing drawings of the hardware world. We call it a class
. A class documents the specications for the construction of a particular type of software object. For
example, there is probably a set of classes that describe the specications for each of the button objects and
menu objects at the top of the browser in which you are currently viewing this module.
4.3.10 A large library of classes
As an object-oriented programmer, you will typically have access to a large library of existing classes from
which you can construct dierent types of software objects, such as buttons, sliders, etc. For example, you
will nd links to the various XNA classes here 4 .
4.3.11 Classes of your own design
In addition, you will often need to design and dene new classes from which you can construct new types of
objects.
4.4 Why should you care about OOP?
You need to care about OOP because the language of XNA is C# and C# is an object-oriented programming
language. It is not possible to write a C# program without dealing with the object-oriented nature of the
language.
For most humans, it is also not possible to write credible XNA programs without frequent reference to the
XNA documentation. Although the use of the XNA framework will shield you from some of the diculties
4 http://msdn.microsoft.com/en-us/library/bb203940.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
21
of object-oriented programming, you will still have both feet in the OOP sandbox as soon as you consult the
documentation.
4.4.1 The XNA documentation package is huge
For example, the XNA Framework Class Library 5 lists about a dozen namespaces (over and above the
namespaces in the standard C# library) .
One of those namespaces is named Microsoft.Xna.Framework.Graphics . That namespace lists
about 175 dierent classes.
One of those classes is the SpriteBatch class. The SpriteBatch class lists several members including
one constructor, four public properties, nine public methods, two protected methods, and one event.
The SpriteBatch class is probably one of the most commonly used classes in the XNA framework so
you will need to be able to understand the documentation for all or at least most of the members of that
class.
4.4.2 The good news
The good news is that the documentation also provides numerous code samples and explanatory notes to
help you use the documentation to write your code correctly.
4.5 Three important concepts
Any object-oriented language must support three very important concepts:
• Encapsulation
• Inheritance
• Polymorphism
We use these three concepts extensively as we attempt to model the real-world problems that we are trying
to solve with our object-oriented programs. I will provide brief descriptions of these concepts in this module
and then explain each concept in detail in future modules.
4.5.1 Encapsulation example
Consider the steering mechanism of a car as a real-world example of encapsulation. During the past eighty
years or so, the steering mechanism for the automobile has evolved into an object in the OOP sense.
4.5.1.1 Only the interface is exposed
In particular, most of us know how to use the steering mechanism of an automobile without having any idea
whatsoever how it is implemented. All most of us care about is the interface, which we refer to as a steering
wheel. We know that if we turn the steering wheel clockwise, the car will turn to the right, and if we turn
it counterclockwise, the car will turn to the left.
4.5.1.2 How is it implemented?
Most of us don't know, and don't really care, how the steering mechanism is actually implemented "under
the hood." In fact, there are probably a number of dierent implementations for various brands and models
of automobiles. Regardless of the brand and model, however, the human interface is pretty much the same.
Clockwise turns to the right, counterclockwise turns to the left.
5 http://msdn.microsoft.com/en-us/library/bb203940.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
22
CHAPTER 4. XNA0104-WHAT IS OOP AND WHY SHOULD YOU CARE?
4.5.1.3 Expose the interface and hide the implementation
As in the steering mechanism for a car, a common approach in OOP is to "hide the implementation" and
to "expose the interface" through encapsulation.
4.5.2 Inheritance example
Another important aspect of OOP is inheritance. Let's form an analogy with the teenager who is building
a hotrod. That teenager doesn't normally start with a large chunk of steel and carve an engine out of it.
Rather, the teenager will usually start with an existing engine and make improvements to it.
4.5.2.1 OOP lingo
In OOP lingo, that teenager extends the existing engine, derives from the existing engine, inherits from
the existing engine, or subclasses the existing engine (depending on which author is describing the process)
.
Just like in "souping up" an engine for a hotrod, a very common practice in OOP is to create new
improved classes and objects by extending existing class denitions.
4.5.2.2 Extending the Game class
When you use Visual C# to create a new XNA game project, the IDE creates skeleton code for a new class.
As you will learn later in this course, it is then up to you to put some meat on the skeleton and turn it into
a game. The rst executable statement in the skeleton code is shown in Listing 1 (p. 22) .
Listing 1
. Extending the Game class.
public class Game1 : Microsoft.Xna.Framework.Game
The code in Listing 1 (p. 22) is the rst executable statement in the denition of a new class named Game1
(or whatever name you give the new project) . The denition of the new class extends an existing class
named Game , which resides in the Microsoft.Xna.Framework namespace. The existing class named
Game , is the main controller for your new game. I will have much more to say about the Game class
in a future module.
4.5.2.3 Reuse, don't reinvent
One of the major arguments in favor of OOP is that it provides a formal mechanism that encourages the
reuse of existing programming elements. One of the mottos of OOP is "reuse, don't reinvent."
4.5.3 Polymorphism example
A third important aspect of OOP is polymorphism. This is a Greek word meaning something like one name,
many forms . This is a little more dicult to explain in non-programming terminology. However, we will
stretch our imagination a little and say that polymorphism is somewhat akin to the automatic transmission
in your car. In my Honda, for example, the automatic transmission has four dierent methods or functions
known collectively as Drive (in addition to the functions of Reverse, Park, and Neutral) .
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
23
4.5.3.1 Select Drive to go forward
As an operator of the automobile, I simply select Drive (meaning go forward) . Depending on various
conditions at runtime, the automatic transmission system decides which version of the Drive function to use
in every specic situation. The specic version of the function that is used is based on the current conditions
(speed, incline, etc.) . This is somewhat analogous to what we will refer to in a subsequent tutorial module
as runtime polymorphism.
4.6 Object-oriented programming vocabulary
OOP involves a whole new vocabulary (or jargon) which is dierent from or supplemental to the vocabulary
of procedural programming.
For example the object-oriented programmer denes an abstract data type by encapsulating its implementation and its interface in a class.
One or more instances of the class can then be created or instantiated .
An instance of a class is known as an object .
Every object has state and behavior where the state is determined by the current values stored in
the object's instance variables and the behavior is determined by the instance methods belonging to the
object.
Inherited abstract data types are derived classes or subclasses of base classes or super classes. We
extend super classes to create subclasses.
Within the program the code instantiates objects (creates instances of classes) and sends messages
to the objects by calling the class's methods (or member functions ).
If a program is "object oriented", it uses encapsulation , inheritance , and polymorphism . It denes
abstract data types, encapsulates those abstract data types into classes, instantiates objects from the classes,
and sends messages to the objects.
The members of a class fall generally into one of the following categories:
•
•
•
•
Constructors
Properties
Methods
Events
The individual members of a class can be public , private , or protected .
To make things even more confusing, almost every item or action used in the OOP jargon has evolved to
be described by several dierent terms. For example, we can cause an object to change its state by sending
it a message, calling its methods, or calling its member functions. The term being used often depends on
the author who wrote the specic book that you happen to be reading at the time.
Hopefully most of this terminology will become clear as we pursue these modules
4.7 Discussion and sample code
I usually try to provide some code in each module that you can compile and execute. Listing 2 (p. 23)
contains the C# code for a simple program that will display the words "Hello C# World" on the system
console screen when you compile and run it. (The system console screen will probably appear as a black
window when you run the program.)
Listing 2
. Hello World in C#.
//File Program.cs
using System;
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
24
CHAPTER 4. XNA0104-WHAT IS OOP AND WHY SHOULD YOU CARE?
namespace Hello01{
class Hello01{
static void Main(string[] args){
Console.WriteLine("Hello C# World");
//Press any key to dismiss console screen.
Console.ReadKey();
}//end Main
}//end class definition
}//end namespace
4.7.1 You can learn a lot...
You may be surprised at how much you can learn about the structure and syntax of an object-oriented
program in C# by taking this simple program apart and examining the elements of the program. Note,
however, that this program is strictly class based. It does not instantiate any objects .
4.7.2 A new class denition
The central block of code beginning with the word
class
denes a new class named
Hello01
.
4.7.3 The Main method
You probably learned that every C++ program requires a function named main . Execution of the C++
program begins and ends in the main function.
The same is true in C#. Every C# program requires a method named Main (note the upper-case
"M") . However, unlike in C++, the Main method in C# must be dened inside of a class denition.
4.7.4 Methods versus functions
Methods in C# are analogous to the functions dened inside a class in C++. However, they are called
methods instead of functions in C#.
4.7.5 Denition of the Main method
The Main method in Listing 2 (p. 23) begins with the keyword
Main".
static
and ends with the comment "end
4.7.6 The WriteLine method
As you may have guessed already, the call to the WriteLine method inside the
the WriteLine method's argument to be displayed on the system console.
Main
method causes
4.7.7 The Console class
Console 6 is the name of a static class in the System 7 namespace. The signicance of the class being static
is that "You do not need to declare an instance of a static class in order to access its members." Therefore,
6 http://msdn.microsoft.com/en-us/library/system.console.aspx
7 http://msdn.microsoft.com/en-us/library/system.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
25
as I mentioned earlier, this program doesn't purposely instantiate any new objects.
According to the documentation 8 , the Console class "Represents the standard input, output, and
error streams for console applications. This class cannot be inherited."
4.7.8 The System namespace
The System 9 namespace actually belongs to the Microsoft .NET framework and the classes in the namespace
are available for use by several dierent programming languages that also belong to the framework such as
C#, VB.NET, etc. (See the online .NET Framework Class Library
here 10 .)
4.7.9 Members of the Console class
The Console class has many members, about nineteen of which are overloaded versions of the method
named WriteLine 11 . (Method overloading means that the same method name can be used two or more
times in the same scope as long as the argument list diers from one version to the next.)
The version of the WriteLine method that is called in Listing 2 (p. 23) requires a string object as
an incoming parameter. According to the documentation, the behavior of this version of the method is:
"Writes the specied string value, followed by the current line terminator, to the standard output
stream."
4.7.10 The dot operator
Note the period that joins the name of the class and the name of the method in Console.WriteLine .
When used in this way, the period is often referred to as the dot operator . The dot operator is used in a
variety of similar but dierent ways in C#. In this case, it tells the compiler to look in the Console class
for a method named WriteLine that requires an incoming parameter of type string . (It is actually
the type of parameter being passed to the method,
string , that species which version of the
WriteLine method will be executed.)
4.7.11 Namespaces
Briey, namespaces provide a way to partition the class library and the new classes dened in a program
so that it is possible to reference two classes with the same name in the same scope as long as they are
in dierent namespaces. Therefore, whenever you reference a class name in your code, you must tell the
compiler which namespace it resides in. This can be done in two ways. One way is to write the namespace
in front of the class name joined by the dot operator as in
System.Console
or
Microsoft.Xna.Framework.Graphics.SpriteBatch
8 http://msdn.microsoft.com/en-us/library/system.console.aspx
9 http://msdn.microsoft.com/en-us/library/system.aspx
10 http://msdn.microsoft.com/en-us/library/ms229335.aspx
11 http://msdn.microsoft.com/en-us/library/system.console.writeline.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
26
CHAPTER 4. XNA0104-WHAT IS OOP AND WHY SHOULD YOU CARE?
4.7.12 The "using" declaration
The second way to tell the compiler which namespace the class resides in is through the use of a "using"
declaration.
As long as it won't create name conicts, you can tell the compiler that you are "using" the namespace
as shown by the rst line following the initial comment in Listing 2 (p. 23) . Then you don't need to refer
to the namespace when you reference a class belonging to that namespace in your code.
4.7.13 Not much help in this case
In this case, since the Console class was referenced only once, it would have been simpler to join the
class name with the namespace name using the dot operator and to omit the "using" declaration. However,
in those cases where the class name is referenced more than once in the code, it is simpler to declare the
namespace at the beginning to avoid having to type it more than once. This is particularly true in the case
of the SpriteBatch class shown above where the namespace name contains several levels separated by
periods.
4.7.14 Dening your own namespace
When you dene a new class, you should always dene the namespace in which it resides. (I believe this is
a requirement when developing projects using Visual C#.)
4.7.15 The project le structure
Figure 1 (p. 26) shows the project le structure for the Hello01 project. ( Figure 1 (p. 26) was captured
from the Solution Explorer
window in the Visual C# IDE.)
Figure 1
. Project le structure.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
27
The class denition shown in Listing 2 (p. 23) is contained in the le named Program.cs at the bottom
of the tree in Figure 1 (p. 26) . Note that this le is contained in the folder named Hello01 . Also note
that the folder named Hello01 is a child of another folder named Hello01 at the top of the project tree.
(There are two folders named Hello01
in the project tree.)
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
28
CHAPTER 4. XNA0104-WHAT IS OOP AND WHY SHOULD YOU CARE?
4.7.16 The Hello01 namespace
The second line following the initial comment in Listing 2 (p. 23) denes the namespace in which the new
class resides. Note that it is the name of the folder in which the le resides. Note also that it is specied
relative to the top of the project tree.
If the folder containing the class happened to be more than one level down in the project tree, as is the
case of the Graphics folder that contains the SpriteBatch class, it would be necessary to use periods
to trace down the tree.
4.7.17 The call to the ReadKey method
Listing 2 (p. 23) also contains a call to a method named ReadKey , which also belongs to the Console
class. The purpose of this call is to cause the execution of the program to block and wait until the user
presses any key. Otherwise, the console window would appear on the computer screen momentarily and then
disappear almost as soon as it appears.
4.8 Run the program
I encourage you to copy the code from Listing 2 (p. 23) . Use that code to create a Visual C# Console
Application project. Build and run the project. Experiment with the code, making changes, and observing
the results of your changes. Make certain that you can explain why your changes behave as they do.
4.9 Run my program
Click here 12 to download a zip le containing my version of the program. Extract the folder named Hello01
from the zip le and save it somewhere on your disk. Start Visual C# 2010 Express and select Open
Project... from the File menu. Navigate to the project folder and select the le with the extension
of .sln . This should cause the project to open and be ready to run or debug as described in the earlier
module titled Getting Started 13 .
4.10 Summary
You learned something about object-oriented programming in general in this module. You also learned quite
a bit about the structure and syntax of an object-oriented C# program by taking a simple program apart
and examining the elements of the program.
4.11 Miscellaneous
This section contains a variety of miscellaneous information.
Housekeeping material
• Module name: Xna0104-What is OOP and Why Should You Care?
• File: Xna0104.htm
• Published: 02/24/14
12 http://cnx.org/content/m49475/latest/Hello01.zip
13 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
29
Disclaimers: Financial : Although the Connexions site makes it possible for you to download
a PDF le for this module at no charge, and also makes it possible for you to purchase a pre-printed
version of the PDF le, you should be aware that some of the HTML elements in this module may
not translate well into PDF.
I also want you to know that, I receive no nancial compensation from the Connexions website even
if you purchase the PDF version of the module.
In the past, unknown individuals have copied my modules from cnx.org, converted them to Kindle
books, and placed them for sale on Amazon.com showing me as the author. I neither receive
compensation for those sales nor do I know who does receive compensation. If you purchase such
a book, please be aware that it is a copy of a module that is freely available on cnx.org and that it
was made and published without my prior knowledge.
Aliation : I am a professor of Computer Information Technology at Austin Community College
in Austin, TX.
-end-
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
30
CHAPTER 4. XNA0104-WHAT IS OOP AND WHY SHOULD YOU CARE?
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
Chapter 5
Xna0106-Encapsulation in C
Revised: Thu May 05 12:44:19 CDT 2016
This page is part of a Book titled XNA Game Studio
2
1
.
5.1 Table of Contents
• Table of Contents (p. 31)
• Preface (p. 33)
· The three main characteristics of an object-oriented program (p. 33)
· Viewing tip (p. 33)
* Figures (p. 33)
* Listings (p. 33)
• General background information (p. 34)
· Abstraction (p. 34)
* How does abstraction relate to encapsulation? (p. 34)
* Some analogies (p. 34)
* A new type (p. 34)
* Already known to the compiler (p. 34)
* Not known to the compiler (p. 34)
· Dene data representation and behavior in a class (p. 34)
* Create instances of the new type (p. 35)
* Objects have state and behavior (p. 35)
* The state and behavior of a GUI Button object (p. 35)
· A C# class named Button (p. 35)
· The state of Button objects (p. 35)
· The behavior of a Button object (p. 35)
· Encapsulation (p. 35)
* Expose the interface and hide the implementation (p. 36)
* Should be able to change the implementation later (p. 36)
* Class member access control (p. 36)
· Five levels of access control (p. 36)
· A dierent interpretation (p. 36)
· What is an assembly? (p. 36)
1 This content is available online at <http://cnx.org/content/m49472/1.2/>.
2 http://cnx.org/contents/GY804-eY
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
31
32
CHAPTER 5. XNA0106-ENCAPSULATION IN C
*
*
*
*
*
· Public, private, and protected (p. 37)
A public user interface (p. 37)
A set Accessor method (p. 37)
A get Accessor method (p. 37)
Not a good design by default (p. 37)
Not bound to the implementation (p. 38)
• Preview (p. 38)
• Discussion and sample code (p. 38)
·
·
·
·
·
·
·
·
·
·
·
Will explain in fragments (p. 38)
Two classes (p. 38)
Will switch between classes (p. 38)
The declarations (p. 39)
Beginning of the class named Props01 (p. 39)
* Access a public instance variable in the object (p. 39)
* Store a string value in the object's instance variable (p. 39)
* Get and display the value in the object's instance variable (p. 39)
Beginning of the class named TargetClass (p. 40)
* Declare a public instance variable (p. 40)
Dierent kinds of variables (p. 40)
* Instance variables (p. 40)
· The public access modier (p. 40)
· The private access modier (p. 41)
· The protected access modier (p. 41)
* Local variables (p. 41)
* Class variables (p. 41)
· More bad programming practice (p. 41)
* Reference variables (p. 41)
* Primitive variables (p. 41)
· An analogy (p. 41)
Call the object's public accessor methods (p. 42)
* The setColor method (p. 42)
* The getColor method (p. 42)
Would be a property in Java (p. 42)
The denition of setColor and getColor in TargetClass (p. 42)
* Typical method denitions (p. 43)
* Data validation (p. 43)
· What do you do with invalid data (p. 43)
· Program output (p. 43)
* Set, get, and display a property value with a valid value (p. 43)
· Looks can be deceiving (p. 44)
· A set Accessor method (p. 44)
· A get Accessor method (p. 44)
· The set Accessor and get Accessor methods for the property named height (p. 44)
· A hidden parameter named value (p. 45)
· Validating code (p. 45)
· A read-only property (p. 45)
· Little more to say about this (p. 45)
Call manipulator method and display results (p. 45)
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
33
* A manipulator method named doubleHeight (p. 45)
· Set and get the property with an invalid value (p. 46)
· Pause until the user presses any key (p. 46)
· The end of the program (p. 46)
•
•
•
•
•
Run the program (p. 46)
Run my program (p. 47)
Summary (p. 47)
Miscellaneous (p. 47)
Complete program listing (p. 47)
5.2 Preface
This module is one in a collection of modules designed primarily for teaching GAME 1343 Game and
Simulation Programming I at Austin Community College in Austin, TX. These modules are intended
to supplement and not to replace the textbook.
An earlier module titled Getting Started 3 provided information on how to get started programming with
Microsoft's XNA Game Studio.
5.2.1 The three main characteristics of an object-oriented program
Object-oriented programs exhibit three main characteristics:
• Encapsulation
• Inheritance
• Polymorphism
In this and the next two modules, I will explain and illustrate those three characteristics plus some related
topics.
5.2.2 Viewing tip
I recommend that you open another copy of this module in a separate browser window and use the following
links to easily nd and view the Figures and Listings while you are reading about them.
5.2.2.1 Figures
• Figure 1 (p. 38) . Output from the program named Props01.
5.2.2.2 Listings
•
•
•
•
•
•
•
•
•
•
•
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
1 (p. 38) . Beginning of the class named Props01.
2 (p. 40) . Beginning of the class named TargetClass.
3 (p. 42) . Call the object's public accessor methods.
4 (p. 42) . Denition of setColor and getColor methods in TargetClass.
5 (p. 44) . Set, get, and display a property value with a valid value.
6 (p. 44) . The set Accessor and get Accessor methods for the property named height.
7 (p. 45) . Call manipulator method and display results.
8 (p. 45) . A simple manipulator method named doubleHeight.
9 (p. 46) . Set and get the property named height with an invalid value.
10 (p. 46) . Pause until the user presses any key.
11 (p. 47) . The program named Props01.
3 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
34
CHAPTER 5. XNA0106-ENCAPSULATION IN C
5.3 General background information
In addition to the three explicit characteristics of encapsulation, inheritance, and polymorphism, an objectoriented program also has an implicit characteristic of abstraction .
5.3.1 Abstraction
Abstraction is the process by which we specify a new data type, often referred to as an abstract data type
or ADT. This includes a specication of the type's data representation and its behavior. In particular,
• What kind of data can be stored in an entity of the new type?
• What are all the ways that the data can be manipulated?
5.3.1.1 How does abstraction relate to encapsulation?
Encapsulation is the process of gathering an ADT's data representation and behavior into one encapsulated
entity. In other words, encapsulation converts from the abstract to the concrete .
5.3.1.2 Some analogies
You might think of this as being similar to converting an idea for an invention into a set of blueprints from
which it can be built, or converting a set of written specications for a widget into a set of drawings that
can be used by the machine shop to build the widget.
Automotive engineers encapsulated the specications for the steering mechanism of my car into a set of
manufacturing drawings. Then manufacturing personnel used those drawings to produce an object where
they exposed the interface (steering wheel) and hid the implementation (levers, bolts, etc.).
In all likelihood, the steering mechanism contains a number of other more-specialized embedded objects,
each of which has state and behavior and each of which also has an interface and an implementation.
The interfaces for those embedded objects aren't exposed to me, but they are exposed to the other parts
of the steering mechanism that use them.
5.3.1.3 A new type
For our purposes, an abstract data type is a new type (not intrinsic to the C# language). It is not one of
the primitive data types that are built into the programming language such as sbyte , short , int ,
long , oat , double , etc.
5.3.1.4 Already known to the compiler
The distinction in the previous paragraph is very important. The data representation and behavior of
the intrinsic or primitive types is already known to the compiler and cannot normally be modied by the
programmer.
5.3.1.5 Not known to the compiler
The representation and behavior of an abstract type is not known to the compiler until it is dened by the
programmer and presented to the compiler in an appropriate manner.
5.3.2 Dene data representation and behavior in a class
C# programmers dene the data representation and the behavior of a new type (present the specication
to the compiler) using the keyword class . In other words, the keyword class is used to convert the
specication of a new type into something that the compiler can work with; a set of plans as it were. To
dene a class is to go from the abstract to the concrete.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
35
5.3.2.1 Create instances of the new type
Once the new type (class) is dened, one or more objects of that type can be brought into being (instantiated,
caused to occupy memory).
5.3.2.2 Objects have state and behavior
Once instantiated, the object is said to have state and behavior . The state of an object is determined
by the current values of the data that it contains. The behavior of an object is determined by its methods.
5.3.2.3 The state and behavior of a GUI Button object
For example, if we think of a GUI Button as an object, it is fairly easy to visualize the object's state and
behavior.
A GUI Button can usually manifest many dierent states based on size, position, depressed image,
not depressed image, label, etc. Each of these states is determined by data stored in the instance variables
of the Button object at any given point in time. (The combination of one or more instance variables that
determine a particular state is often referred to as a property of the object.)
Similarly, it is not too dicult to visualize the behavior of a GUI Button. When you click it with the
mouse, some specic action usually occurs.
5.3.2.3.1 A C# class named Button
If you dig deeply enough into the C# class library 4 , you will nd that there is a class named Button 5 in
the System.Windows.Forms namespace. Each individual Button object in a C# Windows Forms
Application is an instance of the C# class named Button . A Button object has a single constructor,
dozens of methods, dozens of properties, and dozens of events.
5.3.2.3.2 The state of Button objects
Each Button object has instance variables, which it does not share with other Button objects. The
values of the instance variables dene the state of the button at any given time. Other Button objects in
the same scope can have dierent values in their instance variables. Hence every Button object can have
a dierent state.
5.3.2.3.3 The behavior of a Button object
Each Button object also has certain fundamental behaviors such as responding to a mouse Click event or
responding to a GotFocus event.
The C# programmer has control over the code that is executed in response to the event. However, the
C# programmer has no control over the fact that a Button object will respond to such an event. The
fact that a Button will respond to certain event types is an inherent part of the type specication for the
Button class and can only be modied by modifying the source code for the Button class.
5.3.3 Encapsulation
If abstraction is the design or specication of a new type, then encapsulation is its denition and implementation.
A programmer denes the data representation and the behavior of an abstract data type into a class,
thereby dening its implementation and its interface. That data representation and behavior is then encapsulated in objects that are instantiated from the class.
4 http://msdn.microsoft.com/en-us/library/ms229335.aspx
5 http://msdn.microsoft.com/en-us/library/system.windows.forms.button.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
36
CHAPTER 5. XNA0106-ENCAPSULATION IN C
5.3.3.1 Expose the interface and hide the implementation
According to good object-oriented programming practice, an encapsulated design usually exposes the interface and hides the implementation. This is accomplished in dierent ways with dierent languages.
Just as most of us don't usually need to care about how the steering mechanism of a car is implemented,
a user of a class should not need to care about the details of implementation for that class.
The user of the class (the using programmer) should only need to care that it works as advertised. Of
course this assumes that the user of the class has access to good documentation describing the interface and
the behavior of objects instantiated from the class.
5.3.3.2 Should be able to change the implementation later
For a properly designed class, the class designer should be able to come back later and change the implementation, perhaps changing the type of data structure used to store data in the object, and the using programs
should not be aected by the change.
5.3.3.3 Class member access control
Object-oriented programming languages usually provide the ability to control access to the members of a
class. For example, C#, C++ and Java all use the keywords public , private , and protected to
control access to the individual members of a class. In addition, Java adds a fourth level of access control,
which is called package-private . C# adds two levels of access control that are not included in Java or
C++ (see the next section).
5.3.3.3.1 Five levels of access control
According to the C# specications 6 , "Each member of a class has an associated accessibility, which controls
the regions of program text that are able to access the member." There are ve levels of access control in
C#:
•
•
•
•
•
public - Access not limited
protected - Access limited to this class or classes derived from this class
internal - Access limited to this program
protected internal - Access limited to this program or classes derived from this class
private - Access limited to this class
5.3.3.3.2 A dierent interpretation
Another online source
7
provides a dierent interpretation for two of the access levels:
• The internal modier declares that a member is known throughout all les in an assembly, but
unknown outside that assembly.
• The protected internal access level can be given only to class members.
• A member declared with protected internal access is accessible within its own assembly or to
derived types.
5.3.3.3.3 What is an assembly?
You can learn more about an assembly here 8 . Frankly, I'm not absolutely certain at this time how to
interpret the access levels of internal and protected internal . However, I believe that an assembly in C#
is similar to a package in Java, and if so, then I do know how to interpret these two access levels.
6 http://msdn.microsoft.com/en-us/vcsharp/aa336809.aspx
7 http://www.java2s.com/Tutorial/CSharp/0140__Class/CsAccessSpeciers.htm
8 http://www.codeguru.com/columns/csharp_learning/article.php/c5845/
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
37
5.3.3.3.4 Public, private, and protected
To a rst approximation, you can probably guess what public and private mean. Public members
are accessible by all code that has access to an object of the class. Private members are accessible only
by members belonging to the class.
The protected keyword is used to provide inherited classes with special access to the members of their
base classes.
5.3.3.4 A public user interface
In general, the user interface for a class consists of the
public
methods.
The variables in a class can also be declared public but this is generally considered to be bad
programming practice unless they are actually constants.
For a properly designed class, the class user stores, reads, and modies values in the object's data by calling
the public methods on a specic instance (object) of the class. (This is sometimes referred to as sending
a message to the object asking it to change its state).
Normally, if the class is properly designed and the implementation is hidden, the user cannot modify the
values contained in the private instance variables of the object without going through the prescribed public
methods in the interface.
5.3.3.5 A set Accessor method
C# has a special form of method, often called a set Accessor method. The use of this type of method
makes it appear that an assignment is being made to store a value in a private instance variable belonging
to an object when in fact, the assignment operation is automatically converted to a call to a set Accessor
method. I discuss this more fully in my earlier tutorial titled Learning C# and OOP, Properties, Part 1 9 .
I will also show an example of a set Accessor method later.
5.3.3.6 A get Accessor method
C# also has a special form of method often called a get Accessor method that operates like a set Accessor
method but in the reverse direction. A get Accessor method makes it appear that the value of a private
instance variable can be obtained by referencing the name of the object joined to the name of the variable.
In fact, that reference is automatically converted to a call to a get Accessor method.
5.3.3.7 Not a good design by default
An object-oriented design is not a good design by default. In an attempt to produce good designs, experienced
object-oriented programmers generally agree on certain design standards for classes. For example, the data
members (instance variables) are usually private unless they are constants. The user interface usually consists
only of public methods and includes few if any data members.
Of course, there are exceptions to every rule. One exception to this general rule is that data members
that are intended to be used as symbolic constants are made public and dened in such a way that their
values cannot be modied.
The methods in the interface should control access to, or provide a pathway to the private instance
variables.
9 http://www.dickbaldwin.com/csharp/Cs000106a.htm
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
38
CHAPTER 5. XNA0106-ENCAPSULATION IN C
5.3.3.8 Not bound to the implementation
The interface should be generic in that it is not bound to any particular implementation. Hence, the class
author should be able to change the implementation without aecting the using programs so long as the
interface doesn't change.
In practice, this means that the signatures of the interface methods should not change, and that the
interface methods and their arguments should continue to have the same meaning even if the author of the
class changes the internal implementation.
5.4 Preview
I will present and explain a simple C# console program named Probs01 that illustrates encapsulation
and C# properties in the remainder of this module. The output from the program is shown in Figure 1 (p.
38) .
Figure 1
. Output from the program named Props01.
text: Quit
color: Red
color: Bad color
height: 20
double height: 40
height: 0
5.5 Discussion and sample code
5.5.1 Will explain in fragments
I will explain the code in this program in fragments. A complete listing of the program is provided in Listing
11 (p. 47) near the end of the module.
5.5.2 Two classes
The program contains two separate class denitions. One class, named TargetClass , illustrates encapsulation and properties. The other class named Props01 instantiates an object of TargetClass and
exercises its interface. For simplicity, both classes were dened in the same physical le, but that is not a
requirement.
5.5.3 Will switch between classes
In an attempt to help you understand how the program works, I will switch back and forth between the two
classes showing the cause and eect relationship between the code in one class and the code in the other
class.
Listing 1
. Beginning of the class named Props01.
using System;
namespace Props01{
class Props01{
static void Main(string[] args){
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
39
TargetClass obj = new TargetClass();
//Access a public instance variable
obj.text = "Quit";
Console.WriteLine("text: " + obj.text);
5.5.4 The declarations
The rst two statements in Listing 1 (p. 38) apply equally to both classes. The rst statement "uses" the
namespace named System . This eliminates the requirement to qualify every reference to the Console
class with the name of the namespace containing the Console class.
The second statement in Listing 1 (p. 38) establishes the namespace for the new program code, which is
actually the name of the folder containing the source code le.
5.5.5 Beginning of the class named Props01
The class denition for the class named Props01 begins with the keyword class shown in Listing 1 (p.
38) .
As you will see when we get to the end of the explanation, the only thing that is contained in this class is
the Main method. The required signature for the Main method is shown in Listing 1 (p. 38) . I'm not
going to explain all of the ramications for the syntax of the method signature. At this point, I will simply
tell you to memorize the syntax.
The rst statement in the Main method uses the new operator to instantiate an object of the class
named TargetClass and save a reference to that object in a local reference variable named obj . This
reference will be used later to access the object.
5.5.5.1 Access a public instance variable in the object
The last two statements in Listing 1 (p. 38) access a public instance variable belonging to the object. The
rst of the two statements assigns the string value "Quit" to the variable. The second statement retrieves
and displays that value.
5.5.5.2 Store a string value in the object's instance variable
Note the syntax of the rst of these two statements. The value of the reference variable named
joined to the name of the object's public instance variable. (The name of the instance variable is
The assignment operator is used to store a string value in that instance variable.
You can think of this operation as involving the following steps:
obj
text
is
.)
1. Get the object's reference from the variable named obj .
2. Use that reference to locate the object in memory.
3. Knock on the object's door and ask for access to the instance variable named text . (Access will be
granted because the instance variable is declared public as you will see shortly.)
4. Store the string value "Quit" in the instance variable using an assignment operator.
5.5.5.3 Get and display the value in the object's instance variable
The last statement does essentially the same thing in reverse.
1. Get and use the object's reference to gain access to the object's public instance variable.
2. Get the value stored in that instance variable.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
40
CHAPTER 5. XNA0106-ENCAPSULATION IN C
3. Concatenate that value to the literal string value "text".
4. Pass the concatenated string to the WriteLine method of the static
displayed on the standard output device (the black screen).
Console
class to have it
This produces the rst line of output shown in Figure 1 (p. 38) .
5.5.6 Beginning of the class named TargetClass
The class denition for the class named
Listing 2
TargetClass
begins in Listing 2 (p. 40) .
. Beginning of the class named TargetClass.
public class TargetClass{
public string text;
5.5.6.1 Declare a public instance variable
The code in the class begins by declaring a public instance variable. This is considered to be bad programming
practice in most quarters unless the public variable is actually a constant, (which it is not).
This is the variable that is accessed by the last two statements in Listing 1 (p. 38) .
In most cases, instance variables should be declared private and should be made accessible through public
accessor methods or public set and get methods. You will see examples of public access, set, and get methods
later.
5.5.7 Dierent kinds of variables
I will generally refer to three kinds of variables:
Instance variables - declared inside a class but outside of a method or constructor. (All variables
in C# must be declared inside a class. Unlike C++, there are no global variables or global functions
in C#.)
2. Local variables - declared inside a method or constructor.
3. Class variable - declared inside a static class.
1.
Any of these can be further qualied as follows:
1.
2.
Reference variable
Primitive variable
- contains a reference to an object or contains null.
- contains a value of a primitive type (int, oat, double, etc.).
5.5.7.1 Instance variables
An instance variable belongs to a specic object. The lifetime of an instance variable is the same as the
lifetime of the object to which it belongs. The scope of an instance variable depends on its access modier
such as public , private , or protected .
5.5.7.1.1 The public access modier
A public instance variable can be accessed by any code in any method in any object that can obtain a
reference to the object to which the variable belongs. However, you must rst gain access to the object in
order to gain access to the variable. Normally you gain access to the object using a reference to the object
and then gain access to a member of the object such as a variable or method. This is indirection .
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
41
5.5.7.1.2 The private access modier
A private instance variable can be accessed by any code in any method that is dened in the class of the
object to which the variable belongs.
5.5.7.1.3 The protected access modier
A protected instance variable can be accessed by the same methods that can access a private
instance
variable plus methods in subclasses of the class of the object to which the instance variable belongs.
I'm not going to try to explain the scope of internal and protected internal instance variables for
the reasons that I discussed earlier.
5.5.7.2 Local variables
A local variable is declared inside a method or constructor. The lifetime of a local variable is limited to
the time that control remains within the block of code in which the variable is declared. The scope of the
variable is limited to the code block in which it is declared and then only to the statements following the
declaration statement.
5.5.7.3 Class variables
A class variable belongs to a static class. I believe that the lifetime of a class variable is the same as the
lifetime of the program in which the class is loaded.
It is not necessary to instantiate an object of the static class in order to access the variable. Assuming
that you have access rights to the variable, you can access it simply by joining the name of the class to the
name of the variable using the dot operator.
5.5.7.3.1 More bad programming practice
I personally consider it bad programming practice to use class variables in most cases unless the variables are
actually constants. However, there are a few situations where you have no choice but to use a non-constant
class variable.
5.5.7.4 Reference variables
A reference variable contains a reference to an object or contains null. You typically use the value stored in
a reference variable to locate an object in memory. Once you locate the object, you typically use the dot
operator along with the name of a variable or a method to ask the object to do something. This is called
indirection .
5.5.7.5 Primitive variables
Primitive variables contain primitive values. No indirection is required to access the primitive value stored
in a primitive variable.
5.5.7.5.1 An analogy
An analogy that I often use to explain the dierence between a reference variable and a primitive variable
goes as follows.
A primitive variable is analogous to your wallet.
If you get robbed and the robber takes your wallet, he has your money because the wallet contains your
money just like a primitive variable contains a primitive value.
A reference variable is analogous to your check book.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
42
CHAPTER 5. XNA0106-ENCAPSULATION IN C
If the robber takes your checkbook, he doesn't have your money not yet anyway. The checkbook doesn't
contain your money. Instead, it contains a reference to your bank where your money is stored. A reference
variable doesn't contain an object; it contains a reference to an object. Furthermore, two or more reference
variables can contain references to the same object but this is usually not a good idea.
5.5.8 Call the object's public accessor methods
Returning to the Main method in the class named Props01 , Listing 3 (p. 42) calls the object's public
accessor methods named setColor and getColor twice in succession.
Listing 3
. Call the object's public accessor methods.
//Call public accessor methods
obj.setColor("Red");
Console.WriteLine("color: " + obj.getColor());
//Call public accessor methods again with an
// invalid input value.
obj.setColor("Green");
Console.WriteLine("color: " + obj.getColor());
5.5.8.1 The setColor method
The purpose of the method named setColor is to store a string value in the object. The calling code has
no way of knowing how the value is stored because the implementation is hidden behind a public interface
method. This is an example of encapsulation.
The string value to be stored is passed as a parameter to the method each time the method is called. An
invalid string value was purposely passed as a parameter on the second call to the setColor method.
5.5.8.2 The getColor method
The purpose of the getColor method is to retrieve the string value previously stored in the object by the
setColor method. Once again, the calling code has no way of knowing how the value is retrieved and
returned because the implementation is hidden behind a public interface method.
The value that is retrieved by each call to the getColor method is displayed on the standard output
device (the black screen).
5.5.9 Would be a property in Java
If this were a Java program, the combination of these two methods would constitute a property named
color because the pair of methods matches the design pattern for properties in Java. However, that is not
the case in C#. As you will see later, C# uses a dierent approach to identify properties. In C#, these are
simply public accessor methods used for information hiding.
5.5.10 The denition of setColor and getColor in TargetClass
Listing 4 (p. 42) denes the public
.
Listing 4
setColor
and
getColor
methods in the class named
. Denition of setColor and getColor methods in TargetClass.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
TargetClass
43
private string colorData = "";
public void setColor(string data){
//Validating code
if(data.Equals("Red") || data.Equals("Blue")){
//Accept incoming data value
colorData = data;
}else{
//Reject incoming data value
colorData = "Bad color";
}//end if-else
}//end setColor
//--------------------------------------------------//
public string getColor(){
return colorData;
}//end getColor
5.5.10.1 Typical method denitions
These two methods are typical of the method denition syntax in C#. The syntax is not too dierent from
a function denition in C++ so you should have no trouble understanding the syntax.
5.5.10.2 Data validation
One of the reasons for hiding the implementation behind public accessor methods is to assure that only valid
data is stored in the object. Therefore, public accessor methods that store data in the object often contain
code that validates the incoming data (as shown in Listing 4 (p. 42) ) before actually storing the data in
the object.
The validation code in Listing 4 (p. 42) will only accept incoming color data of "Red" or "Blue". If the
incoming string doesn't match one of those two values, the string "Bad color" is stored in the object in place
of the incoming value.
5.5.10.2.1 What do you do with invalid data
It usually isn't too dicult to write code to implement a set of rules to validate the incoming data. The
hard part is guring out what to do if the incoming data is not valid. The choices range all the way from
agging the data as invalid as shown in Listing 4 (p. 42) to throwing an exception which, if not properly
handled, will cause the program to terminate. The circumstances dictate the action to be taken.
5.5.10.2.2 Program output
The code in Listing 3 (p. 42) calls the setColor method twice. The rst call passes a valid string, "Red",
as a parameter. The second call passes an invalid string, "Green", as a parameter. This causes the second
and third lines of text in Figure 1 (p. 38) to be displayed by the program.
5.5.10.3 Set, get, and display a property value with a valid value
As mentioned earlier, C# uses a special approach to identify properties. (You will see the code in TargetClass that accomplishes this shortly.) In the meantime, the code in Listing 5 (p. 44) (which is still part
of the Main method) rst sets, then gets, and nally displays the value of a property named height
belonging to the object referenced by the contents of the variable named obj .
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
44
CHAPTER 5. XNA0106-ENCAPSULATION IN C
Listing 5
. Set, get, and display a property value with a valid value.
obj.height = 20;
Console.WriteLine("height: " + obj.height);
5.5.10.3.1 Looks can be deceiving
If you compare Listing 5 (p. 44) with Listing 1 (p. 38) , you will see that there is essentially no dierence in
the syntax of the code in the two listings. The syntax in both listings suggests that a value is being directly
assigned to a public instance variable belonging to the object. As we already know, that is true for Listing
1 (p. 38) . However, that is not true for Listing 5 (p. 44) .
5.5.10.3.2 A set Accessor method
Although it doesn't look like it, the code in Listing 5 (p. 44) is actually calling a special set Accessor 10
method that hides the implementation behind a public interface. As you will see shortly, this special method
contains validation code similar what you saw in Listing 4 (p. 42) .
5.5.10.3.3 A get Accessor method
Once again, although it doesn't look like it, the last statement in Listing 5 (p. 44) is actually calling a special
get Accessor 11 method belonging to the object to get the current value of the property. That method returns
the value stored in the property. As before, the returned value is concatenated with a literal string and passed
to the WriteLine method of the Console class for display on the black screen.
5.5.10.3.4 The set Accessor and get Accessor methods for the property named height
Returning to the class named TargetClass , the special set Accessor and get Accessor methods for the
property named height are shown in Listing 6 (p. 44) .
Listing 6
. The set Accessor and get Accessor methods for the property named height.
private int heightData;
public int height{
get{
return heightData;
}//end get
set{
//Validating code
if(value < 84){
heightData = value;
}else{
heightData = 0;
}//end else
}//end set
}//end height property
10 http://cnx.org/content/m49472/latest/Xna0106revised.htm#Accessor
11 http://cnx.org/content/m49472/latest/Xna0106revised.htm#Accessor1
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
45
5.5.10.3.5 A hidden parameter named value
The value on the right side of the assignment operator in Listing 5 (p. 44) arrives on the
code in Listing 6 (p. 44) as a hidden parameter named value .
set
side of the
5.5.10.3.6 Validating code
You can write whatever validating code is appropriate before assigning the incoming value to a private
instance variable or perhaps storing it in a private data structure of some other type.
In this case, the value is accepted and stored in the private instance variable named heightData if it
is less than 84 (the height in inches of a person that is seven feet tall). If the incoming value is greater than
83, it is not accepted and instead is agged as invalid by storing 0 in the private instance variable.
5.5.10.3.7 A read-only property
You can omit the code on the set side if you need a read-only property.
5.5.10.3.8 Little more to say about this
There isn't much more that I can say to explain this syntax other than to tell you to memorize it. The code
in Listing 5 (p. 44) causes the fourth line of text shown in Figure 1 (p. 38) to be displayed on the black
screen.
5.5.11 Call manipulator method and display results
In addition to making it possible to set and get property values, objects often provide other public interface
methods of varying complexity that make it possible to manipulate the data stored in the object in a variety
of ways. In those cases, the using programmer needs access to good documentation that explains the behavior
of such manipulator methods.
Returning to the Main method, Listing 7 (p. 45) calls a manipulator method named doubleHeight
(belonging to the object of type TargetClass ) that is designed to double the value of the height
property. Then Listing 7 (p. 45) accesses and displays the new value of the height property.
Listing 7
. Call manipulator method and display results.
obj.doubleHeight();
Console.WriteLine("double height: " + obj.height);
5.5.11.1 A manipulator method named doubleHeight
Listing 8 (p. 45) shows the manipulator method named doubleHeight
TargetClass .
Listing 8
that is dened in the class named
. A simple manipulator method named doubleHeight.
public void doubleHeight(){
heightData *= 2;
}//end doubleHeight
//--------------------------------------------------//
}//end TargetClass
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
46
CHAPTER 5. XNA0106-ENCAPSULATION IN C
The method multiplies the current value in the private instance variable that is used to store the property
named height by a factor of two, stores the modied value back into the same variable, and returns void
.
The code in Listing 7 (p. 45) causes the fth line of text shown in Figure 1 (p. 38) to be displayed.
Listing 8 (p. 45) also signals the end of the class named TargetClass .
5.5.12 Set and get the property with an invalid value
Returning once more to the
property named height .
Listing 9
Main
method, Listing 9 (p. 46) attempts to set an invalid value into the
. Set and get the property named height with an invalid value.
obj.height = 100;
Console.WriteLine("height: " + obj.height);
Then it retrieves and displays the value currently stored in that property.
As you saw in Listing 6 (p. 44) , when an attempt is made to set a value greater than 83 in the property,
a value of 0 is set in the property to ag it as invalid.
The code in Listing 9 (p. 46) causes the last line of text in Figure 1 (p. 38) to be displayed on the black
screen.
5.5.13 Pause until the user presses any key
The last statement in the Main method, shown in Listing 10 (p. 46) , causes the program to block and
wait until the user presses a key. This causes the black screen to remain on the desktop until the user is
nished viewing it.
Listing 10
. Pause until the user presses any key.
Console.ReadKey();
}//end Main
}//end class Props01
5.5.14 The end of the program
Listing 10 (p. 46) signals the end of the Main method, the end of the class named Props01 , and the
end of the program. When the user presses a key, the black screen disappears from the desktop and the
program terminates.
5.6 Run the program
I encourage you to copy the code from Listing 11 (p. 47) . Use that code to create a C# console application.
Build and run the program. Experiment with the code, making changes, and observing the results of your
changes. Make certain that you can explain why your changes behave as they do.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
47
5.7 Run my program
Click here
12
to download a zip le containing my version of the program. Extract the folder named
Props01 from the zip le and save it somewhere on your disk. Start Visual C# 2010 Express and
select Open Project... from the File menu. Navigate to the project folder and select the le with the
extension of .sln . This should cause the project to open and be ready to run or debug as described in the
earlier module titled Getting Started
13
.
5.8 Summary
You learned about abstraction, abstract data types, encapsulation, information hiding, class member access
control, instance variables, local variables, class variables, reference variables, primitive variables, how C#
identies properties, and how to set and get properties in C#.
You also learned about public accessor methods, public manipulator methods, data validation by property
setters and public accessor methods, and a few other things along the way.
5.9 Miscellaneous
This section contains a variety of miscellaneous information.
Housekeeping material
• Module name: Xna0106-Encapsulation in C#
• File: Xna0106.htm
• Published: 02/24/14
Disclaimers: Financial : Although the Connexions site makes it possible for you to download
a PDF le for this module at no charge, and also makes it possible for you to purchase a pre-printed
version of the PDF le, you should be aware that some of the HTML elements in this module may
not translate well into PDF.
I also want you to know that, I receive no nancial compensation from the Connexions website even
if you purchase the PDF version of the module.
In the past, unknown individuals have copied my modules from cnx.org, converted them to Kindle
books, and placed them for sale on Amazon.com showing me as the author. I neither receive
compensation for those sales nor do I know who does receive compensation. If you purchase such
a book, please be aware that it is a copy of a module that is freely available on cnx.org and that it
was made and published without my prior knowledge.
Aliation :: I am a professor of Computer Information Technology at Austin Community College
in Austin, TX.
5.10 Complete program listing
A complete listing of the C# program discussed in this module is provided in Listing 11 (p. 47) .
Listing 11
. The program named Props01.
12 http://cnx.org/content/m49472/latest/Props01.zip
13 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
48
CHAPTER 5. XNA0106-ENCAPSULATION IN C
/*Project Props01
Copyright 2009, R.G.Baldwin
This program is designed to explore and explain the use
of encapsulation and properties in C#.
Program output is:
text: Quit
color: Red
color: Bad color
height: 20
double height: 40
height: 0
*********************************************************/
using System;
namespace Props01{
class Props01{
static void Main(string[] args){
TargetClass obj = new TargetClass();
//Access a public instance variable
obj.text = "Quit";
Console.WriteLine("text: " + obj.text);
//Call public accessor methods
obj.setColor("Red");
Console.WriteLine("color: " + obj.getColor());
//Call public accessor methods again with an
// invalid input value.
obj.setColor("Green");
Console.WriteLine("color: " + obj.getColor());
//Set and get a property
obj.height = 20;
Console.WriteLine("height: " + obj.height);
//Call manipulator method and display results.
obj.doubleHeight();
Console.WriteLine("double height: " + obj.height);
//Set and get the property again with an invalid
// input value.
obj.height = 100;
Console.WriteLine("height: " + obj.height);
//Pause until user presses any key.
Console.ReadKey();
}//end Main
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
49
}//end class Props01
//====================================================//
public class TargetClass{
//A public instance variable - not good practice
public string text;
//This would be a property named color in Java, but
// not in C#
private string colorData = "";
public void setColor(string data){
//Validating code
if(data.Equals("Red") || data.Equals("Blue")){
//Accept incoming data value
colorData = data;
}else{
//Reject incoming data value
colorData = "Bad color";
}//end if-else
}//end setColor
//--------------------------------------------------//
public string getColor(){
return colorData;
}//end getColor
//--------------------------------------------------//
//This is a C# property named height
private int heightData;
public int height{
get{
return heightData;
}//end get
set{
//Validating code
if(value < 84){
heightData = value;
}else{
heightData = 0;
}//end else
}//end set
}//end height property
//--------------------------------------------------//
//This is a manipulator method
public void doubleHeight(){
heightData *= 2;
}//end doubleHeight
//--------------------------------------------------//
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
50
CHAPTER 5. XNA0106-ENCAPSULATION IN C
}//end TargetClass
}//end namespace
-end-
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
Chapter 6
Xna0108-Inheritance in C
Revised: Thu May 05 14:27:52 CDT 2016
This page is part of a Book titled XNA Game Studio
2
1
.
6.1 Table of Contents
• Table of Contents (p. 51)
• Preface (p. 52)
· The three main characteristics of an object-oriented program (p. 52)
· Viewing tip (p. 52)
* Figures (p. 52)
* Listings (p. 53)
• General background information (p. 53)
· A new class can extend an existing class (p. 53)
* What is inherited? (p. 53)
* The superclass remains viable (p. 53)
* A hierarchy of classes (p. 53)
· Members of the System.Object class (p. 53)
* The Button class (p. 54)
* The Object class is the default superclass (p. 54)
* An orderly hierarchy (p. 54)
· The airship hierarchy (p. 54)
* The Balloon class (p. 54)
* The Airplane class (p. 55)
* Three types of objects (p. 55)
* From the general to the specialized (p. 55)
· Single and multiple inheritance (p. 55)
· The ISA relationship (p. 55)
· The HASA relationship (p. 55)
• Preview (p. 55)
· The airship hierarchy (p. 55)
• Discussion and sample code (p. 56)
1 This content is available online at <http://cnx.org/content/m49498/1.2/>.
2 http://cnx.org/contents/GY804-eY
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
51
52
CHAPTER 6. XNA0108-INHERITANCE IN C
· Will explain in fragments (p. 56)
· Four class denitions (p. 56)
* The Airship class (p. 57)
* The Balloon class (p. 57)
· The new material (p. 58)
· The eect of extending a class (p. 58)
* Airship extends Object (p. 58)
* The beginning of the Driver class (p. 58)
· Code common to all four classes (p. 59)
· Instantiate a new Balloon object (p. 59)
· Set properties in the Balloon object (p. 59)
· Get and display property values (p. 59)
· The program output (p. 59)
* The remaining code (p. 60)
* No sharing of properties (p. 60)
•
•
•
•
•
Run the program (p. 60)
Run my program (p. 60)
Summary (p. 60)
Miscellaneous (p. 60)
Complete program listing (p. 61)
6.2 Preface
This module is one in a collection of modules designed primarily for teaching GAME 1343 Game and
Simulation Programming I at Austin Community College in Austin, TX. These modules are intended
to supplement and not to replace the textbook.
An earlier module titled Getting Started 3 provided information on how to get started programming with
Microsoft's XNA Game Studio.
6.2.1 The three main characteristics of an object-oriented program
Object-oriented programs exhibit three main characteristics:
• Encapsulation
• Inheritance
• Polymorphism
I will explain and illustrate inheritance along with some related topics in this module.
6.2.2 Viewing tip
I recommend that you open another copy of this module in a separate browser window and use the following
links to easily nd and view the Figures and Listings while you are reading about them.
6.2.2.1 Figures
• Figure 1 (p. 56) . Output from the program named Airship01.
3 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
53
6.2.2.2 Listings
•
•
•
•
Listing
Listing
Listing
Listing
1
2
3
4
(p.
(p.
(p.
(p.
57)
57)
58)
61)
.
.
.
.
The
The
The
The
Airship class.
Balloon class.
beginning of the Driver class.
program named Airship01.
6.3 General background information
The rst of the three major characteristics of an object-oriented program is encapsulation . I explained
encapsulation in an earlier module. The second of the three is inheritance , followed by polymorphism .
I will explain inheritance in this module and will explain polymorphism in a future module.
6.3.1 A new class can extend an existing class
A class can be dened to inherit the properties, events, and methods of another class. From a syntax
viewpoint, this is accomplished using the : (colon) operator (see Listing 2 (p. 57) ).
The class being extended or inherited from is often called the base class or the superclass . The new
class is often called the derived class or the subclass .
6.3.1.1 What is inherited?
The subclass inherits the data representation and behavior of the superclass (and all of its superclasses)
. However, the subclass can modify the behavior of inherited methods by overriding them, provided that
they were declared virtual by the original author. (That will be one of the topics in a future module on
polymorphism.) The subclass can also add new data representation and behavior that is unique to its own
purposes.
6.3.1.2 The superclass remains viable
A program can instantiate objects of a superclass as well as instantiating objects of its subclasses. From a
practical viewpoint, the superclass doesn't even know that it has been extended.
6.3.1.3 A hierarchy of classes
Inheritance is hierarchical. By that, I mean that a class may be the subclass of one (and only one) other
class and may be the superclass of one or more other classes.
The overall inheritance hierarchy has a single root in the System.Object 4 class. In other words, the
System.Object 5 class is the common ancestor for every other class.
6.3.2 Members of the System.Object class
The
•
System.object
class denes the following eight methods
6
, which are inherited by every other class:
Equals - Overloaded. Determines whether two Object instances are equal.
· Equals(Object) - Determines whether the specied Object is equal to the current Object.
· Equals(Object,Object) - Determines whether the specied Object instances are considered
equal.
4 http://msdn.microsoft.com/en-us/library/system.object.aspx
5 http://msdn.microsoft.com/en-us/library/system.object.aspx
6 http://msdn.microsoft.com/en-us/library/system.object_members.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
54
CHAPTER 6. XNA0108-INHERITANCE IN C
•
•
•
•
•
•
Finalize - Allows an Object to attempt to free resources and perform other cleanup operations before
the Object is reclaimed by garbage collection.
GetHashCode - Serves as a hash function for a particular type.
GetType - Gets the Type of the current instance.
MemberwiseClone - Creates a shallow copy of the current Object.
ReferenceEquals - Determines whether the specied Object instances are the same instance.
ToString - Returns a String that represents the current Object.
Because these eight methods are inherited by every other class, they are always available for you to use in
your code. (Possibly the most frequently used of these methods is the ToString method.)
6.3.2.1 The Button class
Moving down a single path in the inheritance hierarchy, we nd that the family tree for the
in the System.Windows.Forms namespace is as follows:
•
•
•
•
•
•
Button
class
System. Object
System.MarshalByRefObject
System.ComponentModel.Component
System.Windows.Forms.Control
System.Windows.Forms.ButtonBase
System.Windows.Forms. Button
If you were to examine the documentation for each of the classes in the Button class' family tree, you
would probably nd that each class is more specialized than its superclass. For example, the Object class is
very generic and the Button class is very specialized. Generally speaking, classes become more specialized
as you move down the hierarchy beginning with the Object class.
6.3.2.2 The Object class is the default superclass
When you dene a new class, it becomes an immediate subclass of the
cause your new class to extend some other class.
Object
class by default unless you
6.3.2.3 An orderly hierarchy
The C# inheritance mechanism allows you build an orderly hierarchy of classes to supplement the classes
that are already in the class library.
When several of your abstract data types have characteristics in common, you can design their commonalities into a single superclass and separate their unique characteristics into unique subclasses. This is one
of the purposes of inheritance.
6.3.3 The airship hierarchy
For example, suppose you are building a program dealing with airships. All airships have altitude and range
properties in common. Therefore, you could build a base Airship class containing data and methods
having to do with range and altitude.
From this superclass, you could derive a Balloon class and an Airplane class.
6.3.3.1 The Balloon class
The Balloon class might add properties and methods dealing with passenger capacity and what makes
it go up (helium, hydrogen, or hot air) . Objects of the Balloon class would then be able to deal with
altitude, range, passenger capacity, and what makes it go up.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
55
6.3.3.2 The Airplane class
The Airplane class might add properties and methods dealing with engine type (jet or propeller) and
cargo capacity. Objects of the Airplane class could then deal with altitude, range, engine type, and cargo
capacity.
6.3.3.3 Three types of objects
Having created this hierarchy of classes, you could instantiate objects of type Airship , Balloon ,
and Airplane with the objects of each type having properties and methods to deal with those special
characteristics of the ying machine indicated by the name of the class.
6.3.3.4 From the general to the specialized
You may have noticed that in this hierarchical class structure, inheritance causes the structure to grow in a
direction from most general to more specialized. This is typical.
6.3.4 Single and multiple inheritance
C++ and some other object-oriented programming languages allow for multiple inheritance. This means
that a new class can extend more than one superclass. This has advantages in some cases, but can lead to
diculties in other cases.
C# does not support multiple inheritance. Instead it supports a dierent mechanism called an interface
that provides most of the benets of multiple inheritance without most of the problems. I will explain the
C# interface in a future module.
6.3.5 The ISA relationship
You will sometimes hear people speak of the ISA relationship when discussing OOP (such as in he is a
hero) . The source of this terminology is more fundamental than you may at rst suspect.
Object-oriented designers often strive to use inheritance to model relationships where a subclass "is a
kind of" the superclass. For example, a car "is a kind of" vehicle. A programmer "is a kind of" employee
which in turn "is a kind of" person. An airplane "is a kind of" airship and so is a hot-air balloon.
This relationship is called the ISA relationship. It's that simple.
6.3.6 The HASA relationship
If you were to dene a class that more fully represents an airplane, you might choose to break certain parts
of the airplane out into separate objects and to incorporate them by reference into your Airplane class.
For example, you might incorporate a Cockpit object, a LandingGear object, a Propeller object,
etc. Then, (even though it wouldn't be good grammar), you could say than an object of your Airplane
class "has a" Cockpit object, "has a" LandingGear object, etc. This is often referred to as a HASA
relationship. For example, an airplane ISA airship and HASA cockpit.
6.4 Preview
6.4.1 The airship hierarchy
A little earlier I explained an airship hierarchy involving an Airship class, a Balloon class, and an
class. I will present and explain a sample program that implements that hierarchy. The output
from the program is shown in Figure 1 (p. 56) . I will refer back to Figure 1 (p. 56) in the paragraphs that
follow.
Airplane
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
56
CHAPTER 6. XNA0108-INHERITANCE IN C
Figure 1
. Output from the program named Airship01.
Balloon
range = 5 miles
altitude = 500 feet
passenger capacity = 5
lift media = Hot Air
Airplane
range = 5000 miles
altitude = 35000 feet
cargo capacity = 20000 pounds
engine type = jet
6.5 Discussion and sample code
6.5.1 Will explain in fragments
I will explain this program in fragments. A complete listing of the program is provided in Listing 4 (p. 61)
near the end of the module.
6.5.2 Four class denitions
This program consists of four class denitions:
•
•
•
•
Airship
Balloon
Airplane
Driver
The
Airship
class denes properties that are common to machines that y:
• Range
• Altitude
The Balloon class extends the
are lighter than air:
Airship
class and denes properties that are peculiar to airships that
• Passenger capacity
• Lift Media (hot air, helium, or hydrogen)
The
Airplane
class extends the
Airship
class and denes properties that are peculiar to airplanes:
• Cargo capacity
• Engine type
The
set
Driver class instantiates objects of the Balloon
and get methods.
class and the
Airplane
class and exercises their
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
57
6.5.2.1 The Airship class
Listing 1 (p. 57) shows the Airship
Listing 1
class in its entirety.
. The Airship class.
//Define common properties in the base class.
class Airship {
private int rangeData = 0;
private int altitudeData = 0;
public int range {
get {
return rangeData;
}//end get
set {
rangeData = value;
}//end set
}//end range property
public int altitude {
get {
return altitudeData;
}//end get
set {
altitudeData = value;
}//end set
}//end altitude property
}//end class Airship
There is nothing in Listing 1 (p. 57) that you haven't seen in earlier modules. This class provides
get methods for two properties named range and altitude .
6.5.2.2 The Balloon class
Listing 2 (p. 57) shows the Balloon
Listing 2
class in its entirety.
. The Balloon class.
//Define unique properties in the subclass.
class Balloon : Airship {
private int passengerCapacityData;
private String liftMediaData;
public int passengerCapacity {
get {
return passengerCapacityData;
}//end get
set {
passengerCapacityData = value;
}//end set
}//end passengerCapacity property
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
set
and
58
CHAPTER 6. XNA0108-INHERITANCE IN C
public String liftMedia {
get {
return liftMediaData;
}//end get
set {
liftMediaData = value;
}//end set
}//end liftMedia property
}//end Balloon class
6.5.2.2.1 The new material
The only thing in Listing 2 (p. 57) that is new to this module is the colon that appears between the words
Balloon and Airship on the second line. This is the C# way of specifying that the class named Balloon
extends or inherits from the class named Airship . In this case, the Balloon class is the subclass or
derived class and the Airship class is the superclass or base class, depending on which avor of jargon you
prefer.
6.5.2.2.2 The eect of extending a class
The eect of having the Balloon class extend the Airship
class is dierent from anything that you have
seen in previous modules. When one class extends another class, the new class inherits all of the properties,
events, and methods of the superclass and all of its superclasses.
6.5.2.3 Airship extends Object
In this case, the Airship class extends the Object
from the Balloon class contains:
class by default. Therefore, an object instantiated
• The two properties dened in the Balloon class in Listing 2 (p. 57) .
• The two properties dened in the Airship class in Listing 1 (p. 57) .
• The eight methods dened in the Object class discussed earlier (p. 53) .
6.5.2.4 The beginning of the Driver class
At this point, I am going to show and explain the rst half of the
relate it to the program output shown in Figure 1 (p. 56) .
Listing 3
Driver
class (see Listing 3 (p. 58) ) and
. The beginning of the Driver class.
using System;namespace Airship01 {
//Define a class to exercise the Balloon class and the
// Airplane class.
class Driver {
static void Main(string[] args) {
Balloon balloon = new Balloon();
balloon.range = 5;
balloon.altitude = 500;
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
59
balloon.passengerCapacity = 5;
balloon.liftMedia = "Hot Air";
Console.WriteLine("Balloon");
Console.WriteLine(
"range = " + balloon.range + " miles");
Console.WriteLine(
"altitude = " + balloon.altitude + " feet");
Console.WriteLine("passenger capacity = "
+ balloon.passengerCapacity);
Console.WriteLine("lift media = "
+ balloon.liftMedia);
For convenience and because of their small sizes, I elected to dene all four classes in the same le as the
le that contains the Driver class with the Main method. That is not a requirement, however, and on
large projects you may want to put each class denition in its own le.
6.5.2.4.1 Code common to all four classes
The single line of code at the very beginning of Listing 3 (p. 58) applies to all four classes. You have seen
this before so it should not be new to you.
6.5.2.4.2 Instantiate a new Balloon object
The Main method begins by instantiating a new object of the Balloon class and saving its reference
in a reference variable of type Balloon named balloon . This reference will be used later to access the
new object.
6.5.2.4.3 Set properties in the Balloon object
Then the Main method calls the four set methods belonging to the new Balloon
to set values into the four properties belonging to the Balloon object.
object, using them
Remember that the set methods that hide two of the properties are dened in the Airship
class and are inherited into the Balloon class. The other two set methods are dened in the
Balloon class.
6.5.2.4.4 Get and display property values
After that, the Main method calls the four get
methods belonging to the new Balloon object, using
them to get and display values from the four properties belonging to the new Balloon object.
The get methods that hide two of the properties are dened in the Airship class and are
inherited into the Balloon class. The other two get methods are dened in the Balloon class.
6.5.2.4.5 The program output
The code in Listing 3 (p. 58) produces the rst ve lines of output text shown in Figure 1 (p. 56) .
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
60
CHAPTER 6. XNA0108-INHERITANCE IN C
6.5.2.5 The remaining code
You can view the remaining code in the Driver class and the code in the Airplane class in Listing 4
(p. 61) . If you understand my explanation of Listing 1 (p. 57) , Listing 2 (p. 57) , and Listing 3 (p. 58)
above, you should have no diculty understanding the behavior of the code in the Airplane class and the
remaining code in the Driver class.
6.5.2.6 No sharing of properties
There is an important point to remember, however. Even though the Balloon and Airplane classes
each inherit the range and altitude properties from the Airship class, objects instantiated from the
Balloon and Airplane classes do not physically share these two properties. Instead, each object has its
own copy of the range property and its own copy of the altitude property. The only thing shared by
the two objects is part of the blueprint from which each object is constructed.
Basically there is no sharing of anything among objects until the static keyword shows up and at that
point, some sharing does take place. The use of the static keyword is a topic for a future module.
6.6 Run the program
I encourage you to copy the code from Listing 4 (p. 61) . Use that code to create a C# console project.
Compile and run the project. Experiment with the code, making changes, and observing the results of your
changes. Make certain that you can explain why your changes behave as they do.
6.7 Run my program
Click here
7
to download a zip le containing my version of the program. Extract the folder named
Airship01 from the zip le and save it somewhere on your disk. Start Visual C# 2010 Express and
select Open Project... from the File menu. Navigate to the project folder and select the le with the
extension of .sln . This should cause the project to open and be ready to run or debug as described in the
earlier module titled Getting Started
8
.
6.8 Summary
In this module, you learned how one class can extend another class and inherit all of the properties, events,
and methods dened in that class and all of its superclasses. You learned that even though a class may be
extended into another class, it remains viable and can be instantiated in its own right. You learned that
inheritance is hierarchical with the overall hierarchy being rooted in a class named Object . You learned
that C# does not support multiple inheritance. You learned about the ISA and HASA relationships.
6.9 Miscellaneous
This section contains a variety of miscellaneous information.
Housekeeping material
• Module name: Xna0108-Inheritance in C#
• File: Xna0108.htm
• Published: 02/27/14
7 http://cnx.org/content/m49498/latest/Airship01.zip
8 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
61
Disclaimers: Financial : Although the Connexions site makes it possible for you to download
a PDF le for this module at no charge, and also makes it possible for you to purchase a pre-printed
version of the PDF le, you should be aware that some of the HTML elements in this module may
not translate well into PDF.
I also want you to know that, I receive no nancial compensation from the Connexions website even
if you purchase the PDF version of the module.
In the past, unknown individuals have copied my modules from cnx.org, converted them to Kindle
books, and placed them for sale on Amazon.com showing me as the author. I neither receive
compensation for those sales nor do I know who does receive compensation. If you purchase such
a book, please be aware that it is a copy of a module that is freely available on cnx.org and that it
was made and published without my prior knowledge.
Aliation : I am a professor of Computer Information Technology at Austin Community College
in Austin, TX.
6.10 Complete program listing
A complete listing of the C# program discussed in this module is provided in Listing 4 (p. 61) .
Listing 4
. The program named Airship01.
/*Project Airship01
* Illustrates inheritance
* ******************************************************/
using System;
namespace Airship01 {
//Define a class to exercise the Balloon class and the
// Airplane class.
class Driver {
static void Main(string[] args) {
Balloon balloon = new Balloon();
balloon.range = 5;
balloon.altitude = 500;
balloon.passengerCapacity = 5;
balloon.liftMedia = "Hot Air";
Console.WriteLine("Balloon");
Console.WriteLine(
"range = " + balloon.range + " miles");
Console.WriteLine(
"altitude = " + balloon.altitude + " feet");
Console.WriteLine("passenger capacity = "
+ balloon.passengerCapacity);
Console.WriteLine("lift media = "
+ balloon.liftMedia);
Airplane airplane = new Airplane();
airplane.range = 5000;
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
62
CHAPTER 6. XNA0108-INHERITANCE IN C
airplane.altitude = 35000;
airplane.cargoCapacity = 20000;
airplane.engineType = "jet";
Console.WriteLine("");//blank line
Console.WriteLine("Airplane");
Console.WriteLine(
"range = " + airplane.range + " miles");
Console.WriteLine(
"altitude = " + airplane.altitude + " feet");
Console.WriteLine("cargo capacity = "
+ airplane.cargoCapacity + " pounds");
Console.WriteLine("engine type = "
+ airplane.engineType);
//Pause and wait for the user to press any key.
Console.ReadKey();
}//end Main
}//end class Driver
//====================================================//
//Define common properties in the base class.
class Airship {
private int rangeData = 0;
private int altitudeData = 0;
public int range {
get {
return rangeData;
}//end get
set {
rangeData = value;
}//end set
}//end range property
public int altitude {
get {
return altitudeData;
}//end get
set {
altitudeData = value;
}//end set
}//end altitude property
}//end class Airship
//====================================================//
//Define unique properties in the subclass.
class Balloon : Airship {
private int passengerCapacityData;
private String liftMediaData;
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
63
public int passengerCapacity {
get {
return passengerCapacityData;
}//end get
set {
passengerCapacityData = value;
}//end set
}//end passengerCapacity property
public String liftMedia {
get {
return liftMediaData;
}//end get
set {
liftMediaData = value;
}//end set
}//end liftMedia property
}//end Balloon class
//====================================================//
//Define unique properties in the subclass.
class Airplane : Airship {
private int cargoCapacityData;
private String engineTypeData;
public int cargoCapacity {
get {
return cargoCapacityData;
}//end get
set {
cargoCapacityData = value;
}//end set
}//end cargoCapacity property
public String engineType {
get {
return engineTypeData;
}//end get
set {
engineTypeData = value;
}//end set
}//end engineType property
}//end Airplane class
//====================================================//
}//end namespace Airship01
-end-
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
64
CHAPTER 6. XNA0108-INHERITANCE IN C
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
Chapter 7
Xna0110-Polymorphism Based on
Overloaded Methods
1
Revised: Thu May 05 15:30:44 CDT 2016
This page is part of a Book titled XNA Game Studio
2
.
7.1 Table of Contents
• Table of Contents (p. 65)
• Preface (p. 66)
· The three main characteristics of an object-oriented program (p. 66)
· Viewing tip (p. 66)
* Figures (p. 67)
* Listings (p. 67)
• General background information (p. 67)
What is polymorphism? (p. 67)
How does C# implement polymorphism? (p. 67)
The class named Object (p. 67)
Multiple methods with the same name (p. 67)
Compile-time and runtime polymorphism, what's the dierence? (p. 67)
Every class extends some other class (p. 68)
A class hierarchy (p. 68)
Methods in the Object class (p. 68)
* Some methods are overloaded (p. 68)
* Some methods are meant to be overridden (p. 68)
* Some methods are meant to be used as is (p. 68)
· Three distinct forms of polymorphism (p. 68)
· Method overloading (p. 69)
* Duplicate method names (p. 69)
* Compile-time polymorphism (p. 69)
* Selection based on the argument list (p. 69)
·
·
·
·
·
·
·
·
• Preview (p. 69)
1 This content is available online at <http://cnx.org/content/m49505/1.2/>.
2 http://cnx.org/contents/GY804-eY
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
65
CHAPTER 7. XNA0110-POLYMORPHISM BASED ON OVERLOADED
METHODS
66
· Keeping it short and simple (p. 69)
· A sample program (p. 69)
* Within the class and the hierarchy (p. 69)
* Class B extends class A, which extends Object (p. 70)
* Designed to illustrate method overloading (p. 70)
• Discussion and sample code (p. 70)
· Will discuss in fragments (p. 70)
· The class named A (p. 70)
* Redundant code (p. 70)
* The method named m() (p. 70)
· The class named B (p. 70)
* Overloaded methods (p. 71)
· The driver class (p. 71)
* Call all three overloaded methods (p. 71)
* One version is inherited (p. 72)
* Two versions dened in class B (p. 72)
· The output (p. 72)
* Parameters are not displayed (p. 72)
•
•
•
•
•
Run the program (p. 72)
Run my program (p. 72)
Summary (p. 73)
Miscellaneous (p. 73)
Complete program listing (p. 73)
7.2 Preface
This module is one in a collection of modules designed primarily for teaching GAME 1343 Game and
at Austin Community College in Austin, TX. These modules are intended
to supplement and not to replace the textbook.
An earlier module titled Getting Started 3 provided information on how to get started programming with
Microsoft's XNA Game Studio.
Simulation Programming I
7.2.1 The three main characteristics of an object-oriented program
Object-oriented programs exhibit three main characteristics:
• Encapsulation
• Inheritance
• Polymorphism
I have explained encapsulation and inheritance in previous modules. I will begin the explanation of polymorphism in this module. However, polymorphism is a complex topic and several modules will be required
to complete the explanation.
7.2.2 Viewing tip
I recommend that you open another copy of this module in a separate browser window and use the following
links to easily nd and view the Figures and Listings while you are reading about them.
3 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
67
7.2.2.1 Figures
• Figure 1 (p. 72) . Screen output from the project named Polymorph01.
7.2.2.2 Listings
•
•
•
•
Listing
Listing
Listing
Listing
1
2
3
4
(p.
(p.
(p.
(p.
70)
70)
71)
73)
.
.
.
.
Class A from the project named Polymorph01.
Class B from the project named Polymorph01.
Class Polymorph01 from the project named Polymorph01.
Project Polymorph01.
7.3 General background information
7.3.1 What is polymorphism?
The meaning of the word polymorphism is something like one name, many forms .
7.3.2 How does C# implement polymorphism?
Polymorphism manifests itself in C# in the form of multiple methods having the same name.
In some cases, multiple methods have the same name, but have dierent formal argument lists. These
are overloaded methods.
In other cases, multiple methods have the same name, same return type, and same formal argument list.
These are overridden methods.
This module concentrates on the use of method overloading to achieve compile-time polymorphism.
7.3.3 The class named Object
As you learned in an earlier module, every class in C# is a direct or indirect subclass of the class named
Object . Methods dened in the Object class are inherited into all other classes. Some of those inherited
methods may be overridden to make their behavior more appropriate for objects instantiated from the new
subclasses. However, this module is not about overriding methods. Instead it is about overloading
methods. I will cover method overriding in future modules.
7.3.4 Multiple methods with the same name
Overloaded methods have the same name and dierent formal argument lists. They may or may not
have the same return type.
Polymorphism manifests itself in C# in the form of multiple methods having the same name. As mentioned above, this module concentrates on method overloading , sometimes referred to as compile-time
polymorphism . Subsequent modules concentrate on method overriding , sometimes referred to as runtime
polymorphism .
7.3.5 Compile-time and runtime polymorphism, what's the dierence?
During the compilation and execution of polymorphic code, the compiler and the runtime system must decide
which of two or more methods having the same name in the same scope must be executed. With method
overloading, that decision is made when the program is compiled. With method overriding, that decision
is deferred and made at runtime. Hence we have the terms compile-time polymorphism and runtime
polymorphism .
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 7. XNA0110-POLYMORPHISM BASED ON OVERLOADED
METHODS
68
7.3.6 Every class extends some other class
Every class in C# (except for the class named Object ) extends some other class. If you don't explicitly
specify the class that your new class extends, it will automatically extend the class named Object .
7.3.7 A class hierarchy
Thus, all classes in C# exist in a class hierarchy where the class named Object forms the root of the
hierarchy.
Some classes extend Object directly, while other classes are subclasses of Object further down the
hierarchy (they extend classes that extend classes that extend Object ).
7.3.8 Methods in the Object class
You learned in an earlier module that the class named Object denes default versions of the following
methods and that every class in the hierarchy inherits them:
•
Equals - Overloaded. Determines whether two Object instances are equal.
· Equals(Object) - Determines whether the specied Object is equal to the current Object.
· Equals(Object,Object) - Determines whether the specied Object instances are considered
equal.
•
•
•
•
•
•
Finalize - Allows an Object to attempt to free resources and perform other cleanup operations before
the Object is reclaimed by garbage collection.
GetHashCode - Serves as a hash function for a particular type.
GetType - Gets the Type of the current instance.
MemberwiseClone - Creates a shallow copy of the current Object.
ReferenceEquals - Determines whether the specied Object instances are the same instance.
ToString - Returns a String that represents the current Object.
7.3.8.1 Some methods are overloaded
The two methods named Equals are dened as overloaded methods in the Object
the same name and dierent formal argument lists.)
class. (They have
7.3.8.2 Some methods are meant to be overridden
Some of these methods are intended to be overridden for various purposes. This includes
, and ToString .
GetHashCode
7.3.8.3 Some methods are meant to be used as is
However, some of the methods, such as GetType are intended to be used as is without overriding.
7.3.9 Three distinct forms of polymorphism
From a practical programming viewpoint, polymorphism manifests itself in three distinct forms in C#:
• Method overloading
• Method overriding through class inheritance
• Method overriding through interface inheritance
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
69
7.3.10 Method overloading
I will begin the discussion of polymorphism with method overloading, which is the simplest of the three.
I will cover method overloading in this module and will cover polymorphism based on overridden methods
using class inheritance and interface inheritance in future modules.
7.3.10.1 Duplicate method names
C# allows you to have two or more method denitions in the same scope with the same name, provided that
they have dierent formal argument lists. This is method overloading .
7.3.10.2 Compile-time polymorphism
Some authors refer to method overloading as a form of compile-time polymorphism , as distinguished from
run-time polymorphism . This distinction comes from the fact that, for each method call, the compiler
determines which method (from a group of overloaded methods) will be executed. This decision is made
when the program is compiled.
In contrast, with method overriding, the determination of which overridden method to execute isn't made
until runtime.
7.3.10.3 Selection based on the argument list
In practice, with overloaded methods, the compiler simply examines the types, number, and order of the
parameters being passed in a method call and selects the overloaded method having a matching formal
argument list.
7.4 Preview
7.4.1 Keeping it short and simple
In these modules on polymorphism, I will explain sample programs that are as short and as simple as I
know how to make them, while still illustrating the important points regarding polymorphism. My objective
is to make the polymorphic concepts as clear as possible without having those concepts clouded by other
programming issues.
Because of their simplicity, these programs aren't likely to resemble problems that you will encounter in
the real world. I will simply ask you to trust me when I tell you that polymorphism has enormous application
in real world programming.
7.4.2 A sample program
I will explain a sample program named Polymorph01 to illustrate method overloading. A complete listing
of the program is provided in Listing 4 (p. 73) near the end of the module.
7.4.2.1 Within the class and the hierarchy
Method overloading can occur both within a class denition, and vertically within the class inheritance
hierarchy.
In other words, an overloaded method can be inherited into a class that denes other overloaded versions
of the method.
The program named Polymorph01 illustrates both aspects of method overloading.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 7. XNA0110-POLYMORPHISM BASED ON OVERLOADED
METHODS
70
7.4.2.2 Class B extends class A, which extends Object
Upon examination of the program, you will see that the class named A extends the class named Object
. You will also see that the class named B extends the class named A .
The class named Polymorph01 is a driver class whose Main method exercises the methods dened
in the classes named A and B .
7.4.2.3 Designed to illustrate method overloading
Once again, this program is not intended to correspond to any particular real-world scenario. Rather, it is
a very simple program designed specically to illustrate method overloading.
7.5 Discussion and sample code
7.5.1 Will discuss in fragments
As usual, I will discuss this program in fragments. A complete listing is provided in Listing 4 (p. 73) near
the end of the module.
7.5.2 The class named A
The code in Listing 1 (p. 70) denes the class named
Listing 1
A
, which explicitly extends
Object
.
. Class A from the project named Polymorph01.
using System;
class A : Object {
public void m() {
Console.WriteLine("m()");
}//end method m()
}//end class A
7.5.2.1 Redundant code
Recall that explicitly extending Object is not required (but it also doesn't hurt anything).
The class named A would extend the class named Object by default unless the class named
explicitly extends some other class.
A
7.5.2.2 The method named m()
The code in Listing 1 (p. 70) denes a method named m() . Note that this version of the method has an
empty argument list (it doesn't receive any parameters when it is executed). The behavior of the method is
simply to display a message indicating that it is executing.
7.5.3 The class named B
Listing 2 (p. 70) contains the denition for the class named B . This class extends the class named
and inherits the method named m dened in the class named A .
Listing 2
. Class B from the project named Polymorph01.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
A
71
class B : A {
public void m(int x) {
Console.WriteLine("m(int x)");
}//end method m(int x)
//---------------------------------//
public void m(String y) {
Console.WriteLine("m(String y)");
}//end method m(String y)
}//end class B
7.5.3.1 Overloaded methods
In addition to the inherited method named
versions of the method named m :
m
, the class named
B
denes two additional overloaded
• m(int x)
• m(String y)
Note that each of these versions of the method receives a single parameter and the type of the parameter is
dierent in each case.
As with the version of the method having the same name dened in the class named A , the behavior
of each of these two methods is simply to display a message indicating that it is executing.
7.5.4 The driver class
Listing 3 (p. 71) contains the denition of the driver class named
Listing 3
Polymorph01
.
. Class Polymorph01 from the project named Polymorph01.
public class Polymorph01 {
public static void Main() {
B var = new B();
var.m();
var.m(3);
var.m("String");
//Pause until the user presses a key.
Console.ReadKey();
}//end Main
}//end class Polymorph01
7.5.4.1 Call all three overloaded methods
The code in the Main method
• Instantiates a new object of the class named B
• Successively calls each of the three overloaded versions of the method named
that object.
m
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
on the reference to
CHAPTER 7. XNA0110-POLYMORPHISM BASED ON OVERLOADED
METHODS
72
7.5.4.2 One version is inherited
The overloaded version of the method named m , dened in the class named A , is inherited into the
class named B . Hence, it can be called on a reference to an object instantiated from the class named B .
7.5.4.3 Two versions dened in class B
The other two versions of the method named m are dened in the class named
be called on a reference to an object instantiated from the class named B .
B
. Thus, they also can
7.5.5 The output
As you would expect from the code that you examined for each of the three methods, the output produced
by sending messages to the object asking it to execute each of the three overloaded versions of the methods
named m is shown in Figure 1 (p. 72) .
Figure 1
. Screen output from the project named Polymorph01.
m()
m(int x)
m(String y)
7.5.5.1 Parameters are not displayed
The values of the parameters passed to the methods do not appear in the output. In this program, the
parameters are used solely to make it possible for the compiler to select the correct version of the overloaded
method to execute in each case.
In a real program, however, the parameters would normally be used by the code in the method for
some useful purpose.
This output conrms that each overloaded version of the method was properly selected for execution based
on the matching of method parameters to the formal argument list of each method.
7.6 Run the program
I encourage you to copy the code from Listing 4 (p. 73) . Use that code to create a C# console project.
Compile and run the project. Experiment with the code, making changes, and observing the results of your
changes. Make certain that you can explain why your changes behave as they do.
7.7 Run my program
Click here
4
to download a zip le containing my version of the program. Extract the folder named
Polymorph01 from the zip le and save it somewhere on your disk. Start Visual C# 2010 Express
and select Open Project... from the File menu. Navigate to the project folder and select the le with
the extension of .sln . This should cause the project to open and be ready to run or debug as described
in the earlier module titled Getting Started
5
.
4 http://cnx.org/content/m49505/latest/Polymorph01.zip
5 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
73
7.8 Summary
Overloaded methods have the same name and dierent formal argument lists. They may or may not have
the same return type.
The word polymorphism means something like one name, many forms . Polymorphism manifests itself
in C# in the form of multiple methods having the same name.
Polymorphism manifests itself in three distinct forms in C#:
• Method overloading
• Method overriding through class inheritance
• Method overriding through interface inheritance
This module concentrates on method overloading, sometimes referred to as compile-time polymorphism .
7.9 Miscellaneous
This section contains a variety of miscellaneous information.
Housekeeping material
• Module name: Xna0110-Polymorphism Based on Overloaded Methods
• File: Xna0110.htm
• Published: 02/27/14
Disclaimers: Financial : Although the Connexions site makes it possible for you to download
a PDF le for this module at no charge, and also makes it possible for you to purchase a pre-printed
version of the PDF le, you should be aware that some of the HTML elements in this module may
not translate well into PDF.
I also want you to know that, I receive no nancial compensation from the Connexions website even
if you purchase the PDF version of the module.
In the past, unknown individuals have copied my modules from cnx.org, converted them to Kindle
books, and placed them for sale on Amazon.com showing me as the author. I neither receive
compensation for those sales nor do I know who does receive compensation. If you purchase such
a book, please be aware that it is a copy of a module that is freely available on cnx.org and that it
was made and published without my prior knowledge.
Aliation : I am a professor of Computer Information Technology at Austin Community College
in Austin, TX.
7.10 Complete program listing
A complete listing of the C# program discussed in this module is provided in Listing 4 (p. 73) .
Listing 4
. Project Polymorph01.
/*Project Polymorph01
Copyright 2009, R.G.Baldwin
This program illustrates method
overloading, both within a class, and
up the inheritance hierarchy.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 7. XNA0110-POLYMORPHISM BASED ON OVERLOADED
METHODS
74
Program output is:
m()
m(int x)
m(String y)
**************************************/
using System;
class A : Object {
public void m() {
Console.WriteLine("m()");
}//end method m()
}//end class A
//===================================//
class B : A {
public void m(int x) {
Console.WriteLine("m(int x)");
}//end method m(int x)
//---------------------------------//
public void m(String y) {
Console.WriteLine("m(String y)");
}//end method m(String y)
}//end class B
//===================================//
public class Polymorph01 {
public static void Main() {
B var = new B();
var.m();
var.m(3);
var.m("String");
//Pause until the user presses
// a key.
Console.ReadKey();
}//end Main
}//end class Polymorph01
-end-
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
Chapter 8
Xna0112-Type Conversion, Casting, and
1
Assignment Compatibility
Revised: Fri May 06 14:33:08 CDT 2016
This page is part of a Book titled XNA Game Studio
2
.
8.1 Table of Contents
• Table of Contents (p. 75)
• Preface (p. 76)
· Viewing tip (p. 76)
* Listings (p. 77)
• General background information (p. 77)
· Type conversion (p. 77)
· Assignment compatibility (p. 77)
* Successful cast depends on class hierarchy (p. 77)
* The generic type Object (p. 77)
· Calling a method on an object (p. 77)
· Assignment compatibility and type conversion (p. 77)
* Type conversion and the cast operator (p. 78)
* Applying a cast operator (p. 78)
* Primitive values and type conversion (p. 78)
· Boolean types (p. 78)
· Numeric primitive types (p. 78)
* Assignment compatibility for references (p. 78)
· Type Object is completely generic (p. 79)
· Converting reference types with a cast (p. 79)
· Downcasting (p. 79)
• Preview (p. 79)
• Discussion and sample code (p. 79)
· The class named A (p. 79)
· The class named B (p. 80)
1 This content is available online at <http://cnx.org/content/m49500/1.2/>.
2 http://cnx.org/contents/GY804-eY
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
75
CHAPTER 8. XNA0112-TYPE CONVERSION, CASTING, AND
ASSIGNMENT COMPATIBILITY
76
·
·
·
·
•
•
•
•
•
* The method named m (p. 80)
* The class named C (p. 80)
The driver class (p. 80)
* An object of the class named B (p. 80)
* Automatic type conversion (p. 81)
Only part of the story (p. 81)
* An illegal operation (p. 81)
* A compiler error (p. 81)
An important rule (p. 81)
* This case violates the rule (p. 81)
* The solution is a downcast (p. 82)
* Still doesn't solve the problem (p. 82)
* What is the problem here? (p. 82)
* The real solution (p. 82)
A few odds and ends (p. 82)
* A legal operation (p. 83)
* Cannot be assigned to type C (p. 83)
* A runtime exception (p. 83)
* Another failed attempt (p. 83)
* The end of the program (p. 84)
Run the program (p. 84)
Run my program (p. 84)
Summary (p. 84)
Miscellaneous (p. 85)
Complete program listing (p. 85)
8.2 Preface
This module is one in a collection of modules designed primarily for teaching GAME 1343 Game and
Simulation Programming I at Austin Community College in Austin, TX. These modules are intended
to supplement and not to replace the textbook.
An earlier module titled Getting Started 3 provided information on how to get started programming with
Microsoft's XNA Game Studio.
Object-oriented programs exhibit three main characteristics:
• Encapsulation
• Inheritance
• Polymorphism
I have explained encapsulation, inheritance, and compile-time polymorphism in earlier modules. Before I
can explain runtime polymorphism, however, I need to step back and explain some concepts involving type
conversion, casting, and assignment compatibility.
8.2.1 Viewing tip
I recommend that you open another copy of this module in a separate browser window and use the following
links to easily nd and view the Listings while you are reading about them.
3 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
77
8.2.1.1 Listings
•
•
•
•
•
•
•
•
•
•
•
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
1 (p. 79) . Source code for class A.
2 (p. 80) . Source code for class B.
3 (p. 80) . Source code for class C.
4 (p. 80) . Beginning of the class named Polymorph02.
5 (p. 81) . Try to call method m on variable var.
6 (p. 82) . Try a downcast to type A.
7 (p. 82) . Try a downcast to type B.
8 (p. 82) . Assign var to v1.
9 (p. 83) . Cannot be assigned to C.
10 (p. 83) . Another failed attempt.
11 (p. 85) . Project Polymorph02.
8.3 General background information
8.3.1 Type conversion
This module explains type conversion for both primitive and reference types.
8.3.2 Assignment compatibility
A value of a particular type may be assignment-compatible with a variable of another type. If so, the value
can be assigned directly to the variable.
If not, it may be possible to perform a cast on the value to change its type and assign it to the variable
as the new type.
8.3.2.1 Successful cast depends on class hierarchy
With regard to reference types, whether or not a cast can be successfully performed depends on the relationship of the classes involved in the class hierarchy.
8.3.2.2 The generic type Object
A reference to any object can be assigned to a reference variable of the type
class is a superclass of every other class.
Object
, because the
Object
In other words, an object instantiated from any class is assignment-compatible with the type
.
Object
8.3.3 Calling a method on an object
Whether or not a method can be called on a reference to an object depends on the current type of the
reference and the location in the class hierarchy where the method is dened.
In order to use a reference of a class type to call a method, the method must be dened at or above that
class in the class hierarchy.
8.3.4 Assignment compatibility and type conversion
As background, for understanding runtime polymorphism, you need to understand assignment compatibility
and type conversion .
As I mentioned earlier, a value of a given type is assignment-compatible with another type if a value of
the rst type can be successfully assigned to a variable of the second type.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 8. XNA0112-TYPE CONVERSION, CASTING, AND
ASSIGNMENT COMPATIBILITY
78
8.3.4.1 Type conversion and the cast operator
In some cases, type conversion happens automatically. In other cases, type conversion must be forced through
the use of a cast operator.
A cast operator is a unary operator, which has a single right operand. The physical representation of the
cast operator is the name of a type enclosed by a pair of matching parentheses, as in:
(int)
8.3.4.2 Applying a cast operator
Applying a cast operator to the name of a variable doesn't actually change the type of the variable. However,
it does cause the contents of the variable to be treated as a dierent type for the evaluation of the expression
in which the cast operator is contained.
8.3.4.3 Primitive values and type conversion
Assignment compatibility issues come into play for both primitive types and reference types.
8.3.4.3.1 Boolean types
To begin with, values of type bool can only be assigned to variables of type bool (you cannot change
the type of a bool ). Thus, a value of type bool is not assignment-compatible with a variable of any
other type.
8.3.4.3.2 Numeric primitive types
In general, numeric primitive values can be assigned to (are assignment-compatible with) a variable of a type
whose numeric range is as wide as or wider than the range of the type of the value. In that case, the type
of the value is automatically converted to the type of the variable.
For example, types sbyte and short can be assigned to a variable of type
int has a wider range than either type sbyte or type short .
int
, because type
On the other hand, a primitive numeric value of a given type cannot be assigned to (is not assignmentcompatible with) a variable of a type with a narrower range than the type of the value.
However, it is possible to use a cast operator to force a type conversion for numeric primitive values.
Such a conversion will often result in the loss of data, and that loss is the responsibility of the
programmer who performs the cast.
8.3.4.4 Assignment compatibility for references
Assignment compatibility for references doesn't involve range issues, as is the case with primitives. Rather,
the reference to an object instantiated from a given class can be assigned to (is assignment-compatible with):
1. Any reference variable whose type is the same as the class from which the object was instantiated.
2. Any reference variable whose type is a superclass of the class from which the object was instantiated.
3. Any reference variable whose type is an interface that is implemented by the class from which the
object was instantiated.
4. Any reference variable whose type is an interface that is implemented by a superclass of the class from
which the object was instantiated.
5. A couple of other cases involving interfaces that extend other interfaces.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
79
In this module, we are interested only in cases 1 and 2 above. We will be interested in the other cases in
future modules involving interfaces.
Such an assignment does not require the use of a cast operator.
8.3.4.4.1 Type Object is completely generic
As mentioned earlier, a reference to any object can be assigned to a reference variable of the type
, because the Object class is a superclass of every other class.
Object
8.3.4.4.2 Converting reference types with a cast
Assignments of references, other than those listed above, require the use of a cast operator to purposely
change the type of the reference.
However, it is not possible to perform a successful cast to convert the type of a reference to another type
in all cases.
Generally, a cast can only be performed among reference types that fall on the same ancestral line of
the class hierarchy, or on an ancestral line of an interface hierarchy. For example, a reference cannot be
successfully cast to the type of a sibling or a cousin in the class hierarchy.
8.3.4.4.3 Downcasting
When we cast a reference along the class hierarchy in a direction away from the root class Object toward
the leaves , we often refer to it as a downcast .
While it is also possible to cast in the direction from the leaves to the root , this conversion happens
automatically, and the use of a cast operator is not required.
8.4 Preview
A sample program is provided that illustrates much of the detail involved in type conversion, method invocation, and casting with respect to reference types.
8.5 Discussion and sample code
The program named Polymorph02 , shown in Listing 11 (p. 85) near the end of the module illustrates
the use of the cast operator with references.
When you examine that program, you will see that two classes named A and C each extend the class
named Object . Hence, we might say that they are siblings in the class hierarchy.
Another class named B extends the class named A . Thus, we might say that A is a child of
Object , and B is a child of A .
8.5.1 The class named A
The denition of the class named
named Object by default.
Listing 1
A
is shown in Listing 1 (p. 79) . This class implicitly extends the class
. Source code for class A.
using System;
class A {
//this class is empty
}//end class A
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 8. XNA0112-TYPE CONVERSION, CASTING, AND
ASSIGNMENT COMPATIBILITY
80
The class named A is empty. It was included in this example for the sole purpose of adding a layer of
inheritance to the class hierarchy.
8.5.2 The class named B
Listing 2 (p. 80) shows the denition of the class named B . This class extends the class named
indicated by the colon that joins the B and the A on the rst line.
Listing 2
A
as
. Source code for class B.
class B : A {
public void m() {
Console.WriteLine("m in class B");
}//end method m()
}//end class B
8.5.2.1 The method named m
The class named B denes a method named m
message each time it is called.
. The behavior of the method is simply to display a
8.5.2.2 The class named C
Listing 3 (p. 80) shows the denition of the class named
Listing 3
C
, which also extends
Object
by default.
. Source code for class C.
class C {
//this class is empty
}//end class C
The class named C is also empty. It was included in this example as a sibling class for the class named
A . Stated dierently, it was included as a class that is not in the ancestral line of the class named B .
8.5.3 The driver class
Listing 4 (p. 80) shows the beginning of the driver class named
Object by default.
Listing 4
Polymorph02
, which also extends
. Beginning of the class named Polymorph02.
public class Polymorph02 {
public static void Main() {
Object var = new B();
8.5.3.1 An object of the class named B
This code instantiates an object of the class B
type Object .
and assigns the object's reference to a reference variable of
Let me repeat what I just said for emphasis. The reference to the object of type B was not
assigned to a reference variable of type B . Instead, it was assigned to a reference variable of type
Object .
This assignment is allowable because Object is a superclass of B . In other words, the reference to the
object of the class B is assignment-compatible with a reference variable of the type Object .
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
81
8.5.3.2 Automatic type conversion
In this case, the reference of type B is
reference variable of type Object .
automatically converted to type
Object
and assigned to the
Note that the use of a cast operator was not required in this assignment.
8.5.4 Only part of the story
However, assignment compatibility is only part of the story. The simple fact that a reference is assignmentcompatible with a reference variable of a given type says nothing about what can be done with the reference
after it is assigned to the reference variable.
8.5.4.1 An illegal operation
For example, in this case, the reference variable that was automatically converted to type Object cannot
be used directly to call the method named m on the object of type B. This is indicated in Listing 5 (p.
81) .
Listing 5
. Try to call method m on variable var.
//Following will not compile
//var.m();
8.5.4.2 A compiler error
An attempt to call the method named m on the reference variable of type
resulted in the following compiler error:
Object
in Listing 5 (p. 81)
error CS0117:
'object' does not contain a definition for 'm'
It was necessary to convert the statement to a comment in order to cause the program to compile successfully.
8.5.5 An important rule
In order to use a reference of a class type to call a method, the method must be dened at or above that
class in the class hierarchy. Stated dierently, the method must either be dened in, or inherited into that
class.
8.5.5.1 This case violates the rule
In this case, the method named m is dened in the class named B
class named Object .
The method named
m
, which is two levels down from the
is neither dened in nor inherited into the class named
Object
.
When the reference to the object of the class B was assigned to the reference variable of type Object ,
the type of the reference was automatically converted to type Object .
Therefore, because the reference is type Object , it cannot be used directly to call the method named
m .
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 8. XNA0112-TYPE CONVERSION, CASTING, AND
ASSIGNMENT COMPATIBILITY
82
8.5.5.2 The solution is a downcast
In this case, the solution to the problem is a downcast.
The code in Listing 6 (p. 82) shows an attempt to solve the problem by casting the reference down the
hierarchy to type A .
Listing 6
. Try a downcast to type A.
//Following will not compile
//((A)var).m();
8.5.5.3 Still doesn't solve the problem
However, this still doesn't solve the problem, and the result is another compiler error.
The method named
m
is neither dened in nor inherited into the class named
A
.
Again, it was necessary to convert the statement into a comment in order to cause the program to compile.
8.5.5.4 What is the problem here?
The problem with this approach is that the downcast simply didn't go far enough down the inheritance
hierarchy.
The class named A does not contain a denition of the method named m . Neither does it inherit
the method named m . The method named m is dened in class B, which is a subclass of A .
Therefore, a reference of type A is no more useful than a reference of type Object insofar as calling
the method named m is concerned.
8.5.5.5 The real solution
The solution to the problem is shown in Listing 7 (p. 82) .
Listing 7
. Try a downcast to type B.
//Following will compile and run
((B)var).m();
The code in Listing 7 (p. 82) casts (temporarily converts) the reference value contained in the Object
variable named var to type B .
The method named m is dened in the class named B . Therefore, a reference of type B can be
used to call the method.
The code in Listing 7 (p. 82) compiles and executes successfully. This causes the method named m to
execute, producing the following output on the computer screen:
m in class B
8.5.6 A few odds and ends
Before leaving this topic, let's look at a few more issues.
The code in Listing 8 (p. 82) declares and populates a new variable of type
Listing 8
B.
. Assign var to v1.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
83
//Following will compile and run
B v1 = (B)var;
The code in Listing 8 (p. 82) uses a cast to:
• Convert the contents of the Object variable to type B
• Assign the converted reference to the new reference variable of type
B
.
8.5.6.1 A legal operation
This is a legal operation. In this class hierarchy, the reference to the object of the class
to a reference variable of the types B, A , or Object .
B
can be assigned
While this operation is legal, it is usually not a good idea to have two dierent reference variables
that contain references to the same object. In this case, the variables named var and v1 both
contain a reference to the same object.
8.5.6.2 Cannot be assigned to type C
However, the reference to the object of the class B cannot be assigned to a reference variable of any other
type, including type C . An attempt to do so is shown in Listing 9 (p. 83) .
Listing 9
. Cannot be assigned to C.
//Following will not execute.
// Causes a runtime exception.
//C v2 = (C)var;
The code in Listing 9 (p. 83) attempts to cast the reference to type
of type C .
C
and assign it to a reference variable
8.5.6.3 A runtime exception
Although the program will compile, it won't execute. An attempt to execute the statement in Listing 9 (p.
83) results in an exception at runtime.
As a result, it was necessary to convert the statement into a comment in order to execute the program.
8.5.6.4 Another failed attempt
Similarly, an attempt to cast the reference to type
shown in Listing 10 (p. 83) , won't compile.
Listing 10
B
and assign it to a reference variable of type
C
, as
. Another failed attempt.
//Following will not compile
//C v3 = (B)var;
//Pause until user presses any key.
Console.ReadKey();
}//end Main
}//end class Polymorph02
The problem here is that the class C is not a superclass of the class named B . Therefore, a reference of
type B is not assignment-compatible with a reference variable of type C .
Again, it was necessary to convert the statement to a comment in order to compile the program.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 8. XNA0112-TYPE CONVERSION, CASTING, AND
ASSIGNMENT COMPATIBILITY
84
8.5.6.5 The end of the program
Listing 10 (p. 83) signals the end of the
Main
method, the end of the class, and the end of the program.
8.6 Run the program
I encourage you to copy the code from Listing 11 (p. 85) . Use that code to create a C# console project.
Compile and run the project. Experiment with the code, making changes, and observing the results of your
changes. Make certain that you can explain why your changes behave as they do.
8.7 Run my program
Click here
4
to download a zip le containing my version of the program. Extract the folder named
Polymoeph02 from the zip le and save it somewhere on your disk. Start Visual C# 2010 Express
and select Open Project... from the File menu. Navigate to the project folder and select the le with
the extension of .sln . This should cause the project to open and be ready to run or debug as described
in the earlier module titled Getting Started
5
.
8.8 Summary
This module discusses type conversion for primitive and reference types.
A value of a particular type may be assignment-compatible with variables of other types.
If the type of a value is not assignment-compatible with a variable of a given type, it may be possible to
perform a cast on the value to change its type and assign it to the variable as the new type. For primitive
types, this will often result in the loss of information.
In general, numeric values of primitive types can be assigned to any variable whose type represents a
numeric range that is as wide as or wider than the range of the value's type. (Values of type bool can
only be assigned to variables of type bool .)
With respect to reference types, the reference to an object instantiated from a given class can be assigned
to any of the following without the use of a cast:
• Any reference variable whose type is the same as the class from which the object was instantiated.
• Any reference variable whose type is a superclass of the class from which the object was instantiated.
• Any reference variable whose type is an interface that is implemented by the class from which the
object was instantiated.
• Any reference variable whose type is an interface that is implemented by a superclass of the class from
which the object was instantiated.
• A couple of other cases involving interfaces that extend other interfaces.
Assignments of references, other than those listed above, require the use of a cast to change the type of the
reference.
It is not always possible to perform a successful cast to convert the type of a reference. Whether or not a
cast can be successfully performed depends on the relationship of the classes involved in the class hierarchy.
A reference to any object can be assigned to a reference variable of the type Object , because the
Object class is a superclass of every other class.
When we cast a reference along the class hierarchy in a direction away from the root class Object
toward the leaves, we often refer to it as a downcast .
4 http://cnx.org/content/m49500/latest/Polymoeph02.zip
5 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
85
Whether or not a method can be called on a reference to an object depends on the current type of the
reference and the location in the class hierarchy where the method is dened. In order to use a reference of
a class type to call a method, the method must be dened in or inherited into that class.
8.9 Miscellaneous
This section contains a variety of miscellaneous information.
Housekeeping material
• Module name: Xna0112-Type Conversion, Casting, and Assignment Compatibility
• File: Xna0112.htm
• Published: 02/27/14
Disclaimers: Financial : Although the Connexions site makes it possible for you to download
a PDF le for this module at no charge, and also makes it possible for you to purchase a pre-printed
version of the PDF le, you should be aware that some of the HTML elements in this module may
not translate well into PDF.
I also want you to know that, I receive no nancial compensation from the Connexions website even
if you purchase the PDF version of the module.
In the past, unknown individuals have copied my modules from cnx.org, converted them to Kindle
books, and placed them for sale on Amazon.com showing me as the author. I neither receive
compensation for those sales nor do I know who does receive compensation. If you purchase such
a book, please be aware that it is a copy of a module that is freely available on cnx.org and that it
was made and published without my prior knowledge.
Aliation : I am a professor of Computer Information Technology at Austin Community College
in Austin, TX.
8.10 Complete program listing
A complete listing of the C# program discussed in this module is provided in Listing 11 (p. 85) .
Listing 11
. Project Polymorph02.
/*Project Polymorph02
Copyright 2002, R.G.Baldwin
This program illustrates downcasting
Program output is:
m in class B
*********************************************************/
using System;
class A {
//this class is empty
}//end class A
//======================================================//
class B : A {
public void m() {
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 8. XNA0112-TYPE CONVERSION, CASTING, AND
ASSIGNMENT COMPATIBILITY
86
Console.WriteLine("m in class B");
}//end method m()
}//end class B
//======================================================//
class C {
//this class is empty
}//end class C
//======================================================//
public class Polymorph02 {
public static void Main() {
Object var = new B();
//Following will not compile
//var.m();
//Following will not compile
//((A)var).m();
//Following will compile and run
((B)var).m();
//Following will compile and run
B v1 = (B)var;
//Following will not execute.
// Causes a runtime exception.
//C v2 = (C)var;
//Following will not compile
//C v3 = (B)var;
//Pause until user presses any key.
Console.ReadKey();
}//end Main
}//end class Polymorph02
//======================================================/
-end-
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
Chapter 9
Xna0114-Runtime Polymorphism
through Class Inheritance
Revised: Fri May 06 15:50:20 CDT 2016
This page is part of a Book titled XNA Game Studio
2
1
.
9.1 Table of Contents
• Table of Contents (p. 87)
• Preface (p. 88)
· Viewing tip (p. 89)
* Listings (p. 89)
• General background information (p. 89)
· What is polymorphism? (p. 89)
· How does C# implement polymorphism? (p. 89)
· The essence of runtime polymorphic behavior (p. 89)
* The decision process (p. 89)
* Late binding (p. 89)
· Operational description of runtime polymorphism (p. 90)
· Runtime polymorphism is very powerful (p. 90)
· An important attribute of runtime polymorphism (p. 90)
· Why is it called runtime polymorphism? (p. 90)
* Why defer the decision? (p. 90)
* Could be either type (p. 90)
• Preview (p. 91)
• Discussion and sample code (p. 91)
· The class named A (p. 91)
* A virtual method (p. 91)
* Behavior of the virtual method (p. 91)
· The class named B (p. 92)
* The override declaration (p. 92)
* A compiler warning (p. 92)
1 This content is available online at <http://cnx.org/content/m49508/1.2/>.
2 http://cnx.org/contents/GY804-eY
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
87
CHAPTER 9. XNA0114-RUNTIME POLYMORPHISM THROUGH CLASS
INHERITANCE
88
·
·
·
·
•
•
•
•
•
* Overriding versus hiding (p. 92)
* Behavior of the overridden method (p. 92)
The driver class (p. 93)
* A new object of the class B (p. 93)
* Downcast and call the method (p. 93)
· Which version was executed? (p. 93)
· Why was this version executed? (p. 93)
· Not runtime polymorphic behavior (p. 93)
* This is runtime polymorphic behavior (p. 94)
· The method output (p. 94)
· Very important (p. 94)
* Another invocation of the method (p. 94)
· Compiler error (p. 94)
Some important rules (p. 95)
* Necessary, but not sucient (p. 95)
* Must dene or inherit the method (p. 95)
One additional scenario (p. 95)
* A new object of type A (p. 95)
* Downcast and call the method (p. 93)
* The output (p. 96)
* Not polymorphic behavior (p. 96)
Once again, what is runtime polymorphism? (p. 96)
Run the program (p. 96)
Run my program (p. 96)
Summary (p. 96)
Miscellaneous (p. 97)
Complete program listing (p. 97)
9.2 Preface
This module is one in a collection of modules designed primarily for teaching GAME 1343 Game and
at Austin Community College in Austin, TX. These modules are intended
to supplement and not to replace the textbook.
An earlier module titled Getting Started 3 provided information on how to get started programming with
Microsoft's XNA Game Studio.
Object-oriented programs exhibit three main characteristics:
Simulation Programming I
• Encapsulation
• Inheritance
• Polymorphism
I have explained encapsulation, inheritance, and compile-time polymorphism in earlier modules. I will
continue my explanation of polymorphism in this module with an explanation of runtime polymorphism
using method overriding and class inheritance. I will defer an explanation of polymorphism using interface
inheritance until a future module.
3 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
89
9.2.1 Viewing tip
I recommend that you open another copy of this module in a separate browser window and use the following
links to easily nd and view the Listings while you are reading about them.
9.2.1.1 Listings
•
•
•
•
•
•
•
Listing
Listing
Listing
Listing
Listing
Listing
Listing
1
2
3
4
5
6
7
(p.
(p.
(p.
(p.
(p.
(p.
(p.
91)
92)
93)
94)
94)
95)
97)
.
.
.
.
.
.
.
Class A.
Class B.
Beginning of the driver class.
This is runtime polymorphic behavior.
A failed attempt.
Not polymorphic behavior.
Project Polymorph03.
9.3 General background information
9.3.1 What is polymorphism?
As you learned in an earlier module, the meaning of the word polymorphism is something like one name,
many forms .
9.3.2 How does C# implement polymorphism?
Also as you learned in an earlier module, polymorphism manifests itself in C# in the form of multiple
methods having the same name.
In some cases, multiple methods have the same name, but dierent formal argument lists. This is
polymorphism implemented using overloaded methods. I explained this form of polymorphism in an earlier
module.
In other cases, multiple methods have the same name, same return type, and same formal argument list.
This is method overriding , which is the main topic of this module.
9.3.3 The essence of runtime polymorphic behavior
Methods are called on references to objects. Typically, those references are stored in reference variables, or
in the elements of a collection such as an array, stack, or queue.
9.3.3.1 The decision process
With runtime polymorphism based on method overriding , the decision regarding which version of a method
will be executed is based on the actual type of the object whose reference is stored in the reference
variable, and not on the type of the reference variable on which the method is called.
Stated dierently, the type of the reference determines which methods can be called . The type of
the object determines which method (from that set of allowable methods) will be called .
9.3.3.2 Late binding
The decision regarding which version of the method to call cannot be made at compile time. That decision
must be deferred and made at runtime. This is sometimes referred to as late binding .
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 9. XNA0114-RUNTIME POLYMORPHISM THROUGH CLASS
INHERITANCE
90
9.3.4 Operational description of runtime polymorphism
Here is an operational description of runtime polymorphism as implemented in C# through inheritance and
method overriding:
• Assume that a class named SuperClass denes a method named method .
• Assume that a class named SubClass extends SuperClass and overrides the method named
method .
• Assume that a reference to an object of the class named SubClass is assigned to a reference variable
named ref of type SuperClass .
• Assume that the method named method is then called on the reference variable using the following
syntax: ref.method() .
• Result: The version of the method named method that will actually be executed is the overridden
version in the class named SubClass . The version that is dened in the class named SuperClass
will not be not executed.
This is runtime polymorphism.
9.3.5 Runtime polymorphism is very powerful
As you gain more experience with C#, you will learn that much of the power of OOP using C# is centered
on runtime polymorphism using class inheritance, interfaces, and method overriding. (The use of interfaces
for polymorphism will be discussed in a subsequent module.)
9.3.6 An important attribute of runtime polymorphism
This is worth repeating:
The decision regarding which version of the method to execute is based on the actual type of
object whose reference is stored in the reference variable, and not on the type of the reference
variable on which the method is called.
9.3.7 Why is it called runtime polymorphism?
The reason that this type of polymorphism is often referred to as runtime polymorphism is because the
decision regarding which version of the method to execute cannot be made until runtime. The decision
cannot be made at compile time (as is the case with overloaded methods).
9.3.7.1 Why defer the decision?
The decision cannot be made at compile time because the compiler has no way of knowing (when the
program is compiled) the actual type of the object whose reference will be stored in the reference variable.
For example, the type of the object might be the result of a choice made at runtime by a human user
among several dierent possible choices.
9.3.7.2 Could be either type
For the situation described earlier, that object could just as easily be of type SuperClass as of type
SubClass . In either case, it would be valid to assign the object's reference to the same superclass reference
variable.
If the object were of the SuperClass type, then a call to the method named method on the reference
would cause the version of the method dened in SuperClass , and not the version dened in SubClass
, to be executed.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
91
One more time the version that is executed is determined by the type of the object and not by
the type of the reference variable containing the reference to the object.
9.4 Preview
From a practical programming viewpoint, polymorphism manifests itself in three distinct forms in C#:
• Method overloading
• Method overriding through class inheritance
• Method overriding through the C# interface inheritance
I covered method overloading as one form of polymorphism (compile-time polymorphism) in an earlier
module.
I will begin the discussion of runtime polymorphism through method overriding and class inheritance
in this module. I will cover interfaces in a future module.
9.5 Discussion and sample code
Let's examine a sample program that illustrates runtime polymorphism using class inheritance and overridden
methods. The name of the program is Polymorph03 . A complete listing of the program is provided in
Listing 7 (p. 97) near the end of the module.
9.5.1 The class named A
I will discuss this program in fragments. Listing 1 (p. 91) shows the denition of a class named
extends the class named Object by default.
Listing 1
A
, which
. Class A.
using System;
class A {
public virtual void m() {
Console.WriteLine("m in class A");
}//end method m()
}//end class A
The class named
A
denes a simple method named
m
.
9.5.1.1 A virtual method
Note that the method named
this method in a subclass.
m
is declared to be
virtual
. This means that it is allowable to override
If you come from a Java programming background, you will note that this is the reverse of the
situation in Java. In Java, all methods are virtual by default unless they are declared to be nal.
9.5.1.2 Behavior of the virtual method
The behavior of the virtual method, as dened in the class named A , is to display a message indicating
that it has been called, and that it is dened in the class named A .
This message will allow us to determine which version of the method is executed in each case discussed
later.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 9. XNA0114-RUNTIME POLYMORPHISM THROUGH CLASS
INHERITANCE
92
9.5.2 The class named B
Listing 2 (p. 92) shows the denition of a class named
Listing 2
B
that extends the class named
A.
. Class B.
class B : A {
public override void m() {
Console.WriteLine("m in class B");
}//end method m()
}//end
The class named
A .
B
overrides (redenes) the method named
9.5.2.1 The override declaration
Note the use of the override declaration in Listing 2 (p.
is necessary to declare that fact.
m
, which it inherits from the class named
92) . In C#, if one method overrides another, it
Once again, if you come from a Java background, you will note that this is just the reverse of the
situation in Java. In Java, a method whose name, return type, and formal argument list matches
an inherited method will automatically override the inherited method.
9.5.2.2 A compiler warning
If you fail to make the override declaration in Listing 2 (p. 92) , you will get a compiler warning that reads
something like the following:
warning CS0114: 'B.m()' hides inherited member 'A.m()'.
To make the current member override that implementation,
add the override keyword. Otherwise add the new keyword.
9.5.2.3 Overriding versus hiding
I'm not going to get into a discussion of the dierence between overriding and hiding in this module. Perhaps
I will nd the time to provide such a discussion in a future module.
9.5.2.4 Behavior of the overridden method
Like the inherited version of the method, the overridden version displays a message indicating that it has
been called. However, the message is dierent from the message displayed by the inherited version discussed
above. The overridden version tells us that it is dened in the class named B .
According to the current jargon, the behavior of the overridden version of the method is appropriate
for an object instantiated from the class named B .
Again, this message will allow us to determine which version of the method is executed in each case discussed
later.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
93
9.5.3 The driver class
Listing 3 (p. 93) shows the beginning of the driver class named
Listing 3
Polymorph03
.
. Beginning of the driver class.
public class Polymorph03 {
public static void Main() {
Object var = new B();
//Following will compile and run
((B)var).m();
9.5.3.1 A new object of the class B
The code in the Main method begins by instantiating a new object of the class named B
the object's reference to a reference variable of type Object .
, and assigning
This is legal because an object's reference can be assigned to any reference variable whose type is
a superclass of the class from which the object was instantiated. The class named Object is the
superclass of all classes.
9.5.3.2 Downcast and call the method
If you have read the module titled Xna0112-Type Conversion, Casting, and Assignment Compatibility 4 , it
will come as no surprise to you that the second statement in the Main method, which casts the reference
down to type B and calls the method named m on it, will compile and execute successfully.
9.5.3.2.1 Which version was executed?
The execution of the method produces the following output on the computer screen:
m in class B
By examining the output, you can conrm that the version of the method that was overridden in the class
named B is the version that was executed.
9.5.3.2.2 Why was this version executed?
This should also come as no surprise to you. The cast converts the type of the reference from type Object
to type B .
You can always call a public method belonging to an object using a reference to the object whose type
is the same as the class from which the object was instantiated.
9.5.3.2.3 Not runtime polymorphic behavior
Just for the record, the above invocation of the method does not constitute runtime polymorphism (in my
opinion) . I included that invocation of the method to serve as a backdrop for what follows.
If this is runtime polymorphism, it is not a very signicant example, because there was no requirement for the runtime system to decide between dierent methods having the same name. The
runtime system simply executed a method belonging to an object using a reference of the same
type as the object.
4 http://cnx.org/contents/GY804-eY:pJQk7xmY
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 9. XNA0114-RUNTIME POLYMORPHISM THROUGH CLASS
INHERITANCE
94
9.5.3.3 This is runtime polymorphic behavior
However, the call to the method in Listing 4 (p. 94) does constitute runtime polymorphism.
Listing 4
. This is runtime polymorphic behavior.
//Following will also compile
// and run due to polymorphic
// behavior.
((A)var).m();
The statement in Listing 4 (p. 94) casts the reference down to type
on that reference.
A
and calls the method named
m
9.5.3.3.1 The method output
Here is the punch line. Not only does the statement in Listing 4 (p. 94) compile and run successfully, it
produces the following output, (which is exactly the same output as before) :
m in class B
The same method was executed in both cases
9.5.3.3.2 Very important
It is very important to note that this output, (produced by casting the reference variable to type A instead
of type B ) , is exactly the same as that produced by the earlier call to the method when the reference
was cast to type B . This means that the same version of the method was executed in both cases.
This conrms that even though the type of the reference was converted to type A , (rather than type
Object or type B ) , the overridden version of the method dened in class B was actually executed.
This is runtime polymorphic behavior in a nutshell.
The version of the method that was executed was based on the actual type of the object,
not on the type of the reference, A . This is an extremely powerful and useful concept.
B
, and
9.5.3.4 Another invocation of the method
Now take a look at the statement in Listing 5 (p. 94) . Will this statement compile and execute successfully?
If so, which version of the method will be executed?
Listing 5
. A failed attempt.
//Following will not compile
//var.m();
(That was easy. The answer to the question is given in Listing 5 (p. 94) ) .
9.5.3.4.1 Compiler error
The code in Listing 5 (p. 94) attempts, unsuccessfully, to call the method named m using the reference
variable named var , which is of type Object . The result is a compiler error, which reads something
like the following:
error CS0117: 'object' does not contain a definition for 'm'
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
95
9.5.4 Some important rules
The Object class does not dene a method named m . Therefore, the overridden method named m in
the class named B is not an overridden version of a method that is dened in the class named Object .
9.5.4.1 Necessary, but not sucient
Runtime polymorphism based on class inheritance requires that the type of the reference variable be a
superclass of the class from which the object (on which the method will be called) is instantiated.
At least this requirement is true if a signicant decision among methods is to be made.
However, while necessary, that is not sucient to ensure runtime polymorphic behavior.
9.5.4.2 Must dene or inherit the method
The type of the reference variable must also be the name of a class that either denes or inherits a version
of the method that will ultimately be called on the object.
Since the class named Object does not dene (or inherit) the method named m , a reference of
type Object does not qualify as a participant in runtime polymorphic behavior in this case. The attempt
to use it as a participant results in the compiler error given above.
9.5.5 One additional scenario
Before leaving this topic, let's look at one additional scenario to help you distinguish what is, and what is
not, runtime polymorphism. Consider the code shown in Listing 6 (p. 95) .
Listing 6
. Not polymorphic behavior.
//Instantiate obj of class A
var = new A();
//Call the method on it
((A)var).m();
// Pause until the user presses any key.
Console.ReadKey();
}//end Main
}//end class Polymorph03
9.5.5.1 A new object of type A
The code in Listing 6 (p. 95) instantiates a new object of the class named
reference in the original reference variable named var of type Object .
A
, and stores the object's
As a side note, this overwrites the previous contents of the reference variable with a new reference
and causes the object whose reference was previously stored there to become eligible for garbage
collection.
9.5.5.2 Downcast and call the method
Then the code in Listing 6 (p. 95) casts the reference down to type A , (the type of the object to which
the reference refers) , and calls the method named m on the downcast reference.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 9. XNA0114-RUNTIME POLYMORPHISM THROUGH CLASS
INHERITANCE
96
9.5.5.3 The output
As you would probably predict, this produces the following output on the computer screen:
m in class A
In this case, the version of the method dened in the class named
executed.
A
, (not the version dened in B) was
9.5.5.4 Not polymorphic behavior
Once again, in my view, this is not runtime polymorphic behavior (at least it isn't a very useful form of
polymorphic behavior) . This code simply converts the type of the reference from type Object to the
type of the class from which the object was instantiated, and calls one of its methods. Nothing special takes
place regarding a selection among dierent versions of the method.
9.5.6 Once again, what is runtime polymorphism?
As I have discussed in this module, runtime polymorphic behavior based on inheritance occurs when
• The type of the reference is a superclass of the class from which the object was instantiated.
• The version of the method that is executed is the version that is either dened in, or inherited into,
the class from which the object was instantiated.
And that is probably more than you ever wanted to hear about runtime polymorphism based on inheritance.
A future module will discuss runtime polymorphism based on the C# interface. From a practical viewpoint, you will nd the rules to be similar but somewhat dierent in the case of the C# interface.
9.6 Run the program
I encourage you to copy the code from Listing 7 (p. 97) . Use that code to create a C# console project.
Compile and run the project. Experiment with the code, making changes, and observing the results of your
changes. Make certain that you can explain why your changes behave as they do.
9.7 Run my program
Click here
5
to download a zip le containing my version of the program. Extract the folder named
Polymorph03 from the zip le and save it somewhere on your disk. Start Visual C# 2010 Express
and select Open Project... from the File menu. Navigate to the project folder and select the le with
the extension of .sln . This should cause the project to open and be ready to run or debug as described
in the earlier module titled Getting Started
6
.
9.8 Summary
Polymorphism manifests itself in C# in the form of multiple methods having the same name.
From a practical programming viewpoint, polymorphism manifests itself in three distinct forms in C#:
• Method overloading
• Method overriding through class inheritance
• Method overriding through interface inheritance
5 http://cnx.org/content/m49508/latest/Polymorph03.zip
6 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
97
This module discusses method overriding through class inheritance.
With runtime polymorphism based on method overriding, the decision regarding which version of a
method will be executed is based on the actual type of object whose reference is stored in a reference
variable, and not on the type of the reference variable on which the method is called.
The decision regarding which version of the method to call cannot be made at compile time. That decision
must be deferred and made at runtime. This is sometimes referred to as late binding.
9.9 Miscellaneous
This section contains a variety of miscellaneous information.
Housekeeping material
• Module name: Xna0114-Runtime Polymorphism through Class Inheritance
• File: Xna0114.htm
• Published: 02/27/14
Disclaimers: Financial : Although the Connexions site makes it possible for you to download
a PDF le for this module at no charge, and also makes it possible for you to purchase a pre-printed
version of the PDF le, you should be aware that some of the HTML elements in this module may
not translate well into PDF.
I also want you to know that, I receive no nancial compensation from the Connexions website even
if you purchase the PDF version of the module.
In the past, unknown individuals have copied my modules from cnx.org, converted them to Kindle
books, and placed them for sale on Amazon.com showing me as the author. I neither receive
compensation for those sales nor do I know who does receive compensation. If you purchase such
a book, please be aware that it is a copy of a module that is freely available on cnx.org and that it
was made and published without my prior knowledge.
Aliation : I am a professor of Computer Information Technology at Austin Community College
in Austin, TX.
9.10 Complete program listing
A complete listing of the program discussed in this module is provided in Listing 7 (p. 97) .
Listing 7
. Project Polymorph03.
/*Project Polymorph03
Copyright 2009, R.G.Baldwin
This program illustrates downcasting
and polymorphic behavior
Program output is:
m in class B
m in class B
m in class A
*********************************************************/
using System;
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 9. XNA0114-RUNTIME POLYMORPHISM THROUGH CLASS
INHERITANCE
98
class A {
public virtual void m() {
Console.WriteLine("m in class A");
}//end method m()
}//end class A
//======================================================//
class B : A {
public override void m() {
Console.WriteLine("m in class B");
}//end method m()
}//end class B
//======================================================//
public class Polymorph03 {
public static void Main() {
Object var = new B();
//Following will compile and run
((B)var).m();
//Following will also compile
// and run due to polymorphic
// behavior.
((A)var).m();
//Following will not compile
//var.m();
//Instantiate obj of class A
var = new A();
//Call the method on it
((A)var).m();
// Pause until the user presses any key.
Console.ReadKey();
}//end Main
}//end class Polymorph03
-end-
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
Chapter 10
Xna0116-Runtime Polymorphism and
the Object Class
1
Revised: Sat May 07 11:13:14 CDT 2016
This page is part of a Book titled XNA Game Studio
2
.
10.1 Table of Contents
• Table of Contents (p. 99)
• Preface (p. 100)
· Viewing tip (p. 100)
* Figures (p. 101)
* Listings (p. 101)
• General background information (p. 101)
·
·
·
·
·
·
·
·
The generic type Object (p. 101)
References of type Object (p. 101)
Methods in the Object class (p. 101)
The dierence between string and String (p. 102)
Every class inherits these methods (p. 102)
To be overridden... (p. 102)
Calling methods of the Object class (p. 102)
And the behavior will be... (p. 102)
• Preview (p. 102)
• Discussion and sample code (p. 102)
· The class named A (p. 102)
* Does not override the ToString method (p. 103)
· The class named B (p. 103)
* Overrides the ToString method (p. 103)
* Purpose of the ToString method (p. 103)
* Can be overridden (p. 103)
* Behavior of the default and overridden versions (p. 104)
* Will be useful later (p. 104)
1 This content is available online at <http://cnx.org/content/m49506/1.2/>.
2 http://cnx.org/contents/GY804-eY
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
99
CHAPTER 10. XNA0116-RUNTIME POLYMORPHISM AND THE OBJECT
CLASS
100
· The class named C (p. 104)
* Behavior of overridden version (p. 104)
· The driver class (p. 104)
* A new object of the class A (p. 105)
· Call ToString method on the reference (p. 105)
· Display the returned String (p. 105)
· Default ToString behavior (p. 105)
· Class A does not override ToString (p. 105)
* A new object of the class B (p. 105)
· Call ToString and display the result (p. 105)
· Do you recognize this? (p. 106)
· Overridden version of ToString was executed (p. 106)
· Once again, what is the rule? (p. 106)
* An object of the class C (p. 106)
· What will the output look like? (p. 106)
· Overridden version of ToString was called (p. 107)
· No downcasting was required (p. 107)
· A generic array object (p. 107)
· Collections (p. 107)
•
•
•
•
•
Run the program (p. 107)
Run my program (p. 107)
Summary (p. 107)
Miscellaneous (p. 108)
Complete program listing (p. 108)
10.2 Preface
This module is one in a collection of modules designed primarily for teaching GAME 1343 Game and
Simulation Programming I at Austin Community College in Austin, TX. These modules are intended
to supplement and not to replace the textbook.
An earlier module titled Getting Started 3 provided information on how to get started programming with
Microsoft's XNA Game Studio.
Object-oriented programs exhibit three main characteristics:
• Encapsulation
• Inheritance
• Polymorphism
I have explained encapsulation, inheritance, compile-time polymorphism, and runtime polymorphism using
method overriding and class inheritance in earlier modules. This module will explain the importance of
the Object class in polymorphic behavior. I will defer an explanation of polymorphism using interface
inheritance until a future module.
10.2.1 Viewing tip
I recommend that you open another copy of this module in a separate browser window and use the following
links to easily nd and view the Figures and Listings while you are reading about them.
3 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
101
10.2.1.1 Figures
• Figure 1 (p. 105) . Text output for ToString method and class A.
• Figure 2 (p. 106) . Text output for overridden ToString method and class B.
• Figure 3 (p. 106) . Text output for overridden ToString method and class C.
10.2.1.2 Listings
•
•
•
•
•
•
•
Listing
Listing
Listing
Listing
Listing
Listing
Listing
1
2
3
4
5
6
7
(p.
(p.
(p.
(p.
(p.
(p.
(p.
102)
103)
104)
104)
105)
106)
108)
.
.
.
.
.
.
.
Denition of class A.
Denition of class B.
Denition of class C.
Beginning of the driver class.
A new object of the class B.
An object of the class C.
Project Polymorph04.
10.3 General background information
10.3.1 The generic type Object
In this module, I will explain the use of the Object class as a completely generic type for storing references
to objects of subclass types, and will explain how that results in a very useful form of runtime polymorphism.
I will briey discuss the default versions of some of the methods dened in the Object class, and will
explain that in many cases, those default versions are meant to be overridden.
10.3.2 References of type Object
The Object type is a completely generic type that can be used to store a reference to any object that can
be instantiated in C#.
10.3.3 Methods in the Object class
The
•
Object class denes the following eight methods 4 , which are inherited by every other class:
Equals - Overloaded. Determines whether two Object instances are equal.
· Equals(Object) - Determines whether the specied Object is equal to the current Object
.
·
Equals(Object,Object)
equal.
•
- Determines whether the specied
Object
instances are considered
Finalize
- Allows an Object to attempt to free resources and perform other cleanup operations
Object is reclaimed by garbage collection.
GetHashCode - Serves as a hash function for a particular type.
GetType - Gets the Type of the current instance.
MemberwiseClone - Creates a shallow copy of the current Object .
ReferenceEquals - Determines whether the specied Object instances are the same instance.
ToString - Returns a String that represents the current Object .
before the
•
•
•
•
•
Because these eight methods are inherited by every other class, they are always available for you to use in
your code. (Possibly the most frequently used of these methods is the ToString method.)
Two of the methods in this list are dened in overloaded versions (same name, dierent formal argument
lists) .
4 http://msdn.microsoft.com/en-us/library/system.object_members.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 10. XNA0116-RUNTIME POLYMORPHISM AND THE OBJECT
CLASS
102
10.3.4 The dierence between string and String
This author 5 says "string is an alias for System.String. So technically, there is no dierence. It's like int vs.
System.Int32."
10.3.5 Every class inherits these methods
Because every class is either a direct or indirect subclass of
classes that you dene) , inherit these methods.
Object
, every class in C#, (including new
10.3.6 To be overridden...
Some of these methods are intended to be overridden for various purposes. This includes Equals ,
Finalize , GetHashCode , and ToString , which are all declared virtual .
However, some of them, such as GetType , are not declared virtual and therefore are intended to
be used directly without overriding.
10.3.7 Calling methods of the Object class
You can store a reference to any object in a reference variable of type Object .
Because every new class inherits these methods, you can call any of these methods on any reference to
any object stored in a reference variable of type Object or in a reference variable of any other type.
10.3.8 And the behavior will be...
If the class from which the object was instantiated inherits or denes an overridden version of one of the
methods in the above list, calling that method on the reference will cause the overridden version to be
executed.
Otherwise, calling that method on the reference will cause the default version dened in the Object
class to be executed.
10.4 Preview
The behavior described above is illustrated in the sample program named Polymorph04 , which you can
view in its entirety in Listing 7 (p. 108) near the end of this module.
For purposes of illustration, this program deals specically with the method named ToString from the
above list, but could deal with the other virtual methods in the list as well.
10.5 Discussion and sample code
10.5.1 The class named A
Listing 1 (p. 102) denes a class named A, which explicitly extends the class named Object .
(Recall that classes extend Object by default. It is not necessary to explicitly show that a class
extends Object . I showed that here simply to remind you that all classes in C# are rooted in the class
named Object .)
Listing 1
. Denition of class A.
5 http://stackoverow.com/questions/7074/in-c-what-is-the-dierence-between-string-and-string
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
103
using System;
class A : Object {
//This class is empty
}//end class A
10.5.1.1 Does not override the ToString method
The most important thing to note about the class named A is that it does not override any of the methods
that it inherits from the class named Object .
Therefore, it inherits the default version of the method named ToString from the class named Object
.
(We will see an example of the behavior of the default version of that method shortly.)
10.5.2 The class named B
Listing 2 (p. 103) denes the class named
Listing 2
B
. This class extends the class named
A
.
. Denition of class B.
class B : A {
public override String ToString() {
return "ToString in class B";
}//end overridden ToString()
}//end class B
10.5.2.1 Overrides the ToString method
Of particular interest, (for purposes of this module) , is the fact that the class named B overrides the
inherited ToString method.
It inherits the default version of the ToString method, because its superclass named A , which
extends Object , does not override the ToString method.
10.5.2.2 Purpose of the ToString method
The purpose of the ToString method is to return
a reference to an object of the class
represents an object instantiated from a class that overrides the method.
Here is part of what Microsoft has to say about the ToString method:
String
that
"This method returns a human-readable string that is culture-sensitive. For example, for an
instance of the Double class whose value is zero, the implementation of Double.ToString
might return "0.00" or "0,00" depending on the current UI culture."
10.5.2.3 Can be overridden
The ToString method can be overridden in a subclass to return values that are meaningful for that type.
Again, according to Microsoft:
"For example, the base data types, such as Int32, implement
string form of the value that the object represents."
ToString
so that it returns the
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 10. XNA0116-RUNTIME POLYMORPHISM AND THE OBJECT
CLASS
104
10.5.2.4 Behavior of the default and overridden versions
The default implementation of the ToString method, as dened in the Object
class, returns the fully
qualied name of the type of the object.
I didn't override the ToString method in the class named A , but I did override it in the class named
B , which is a subclass of A .
The behavior of my overridden version of the method in the class named B returns a reference to a
String object, containing text that indicates that the overridden version of the method in the class named
B has been executed.
10.5.2.5 Will be useful later
The reference to the String object returned by the overridden version of the method will prove useful later
when we need to determine which version of the method is actually executed.
10.5.3 The class named C
Listing 3 (p. 104) shows the denition of a class named C, which extends the class named B , and
overrides the method named ToString again.
An inherited virtual method can be overridden by every class that inherits it, resulting in potentially
many dierent overridden versions of a given method in a class hierarchy.
Listing 3
. Denition of class C.
class C : B {
public override String ToString() {
return "ToString in class C";
}//end overridden ToString()
}//end class B
10.5.3.1 Behavior of overridden version
The behavior of this overridden version of the method is similar to, but dierent from the overridden version
in the class B.
In this case, the method returns a reference to a String object that can be used to conrm that this
overridden version of the method has been executed.
10.5.4 The driver class
Listing 4 (p. 104) shows the beginning of the driver class named
Listing 4
Polymorph04
.
. Beginning of the driver class.
public class Polymorph04 {
public static void Main() {
Object varA = new A();
String v1 = varA.ToString();
Console.WriteLine(v1);
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
105
10.5.4.1 A new object of the class A
The Main method of the driver class begins by instantiating a new object of the class A,
object's reference in a reference variable of type Object , named varA .
and saving the
It is very important to note that even though the object was instantiated from the class named
reference was saved as type Object , not type A.
A
, the
10.5.4.1.1 Call ToString method on the reference
Then the code in Listing 4 (p. 104) calls the ToString method on the reference variable named varA ,
saving the returned reference to the String object in a reference variable of type String named v1 .
10.5.4.1.2 Display the returned String
Finally, that reference is passed to the WriteLine method, causing the String returned by
ToString method to be displayed on the computer screen. This causes the text shown in Figure 1 (p.
to be displayed.
Figure 1
the
105)
. Text output for ToString method and class A.
A
10.5.4.1.3 Default ToString behavior
What you are seeing here is the String produced by the default version of the ToString method, as
dened by the class named Object . The default implementation of the ToString method returns the
fully qualied name of the type of the object. In this case, the object was instantiated from the class named
A (so the type of the object is A ) .
10.5.4.1.4 Class A does not override ToString
Recall that our new class named A does not override the ToString method. Therefore, when the
ToString method is called on a reference to an object of the class A, the default version of the method
is executed, producing the output shown in Figure 1 (p. 105) .
10.5.4.2 A new object of the class B
Now consider the code shown in Listing 5 (p. 105) , which instantiates a new object of the class named
, and stores the object's reference in a reference variable of type Object .
Listing 5
B
. A new object of the class B.
Object varB = new B();
String v2 = varB.ToString();
Console.WriteLine(v2);
10.5.4.2.1 Call ToString and display the result
The code in Listing 5 (p. 105) calls the ToString method on the reference of type Object , saving the
returned reference in the reference variable named v2 . (Recall that the ToString method is overridden
in the class named B .)
As before, the reference is passed to the WriteLine method, which causes the text shown in Figure 2
(p. 106) to be displayed on the computer screen.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 10. XNA0116-RUNTIME POLYMORPHISM AND THE OBJECT
CLASS
106
Figure 2
. Text output for overridden ToString method and class B.
ToString in class B
10.5.4.2.2 Do you recognize this?
You should recognize this as the text that was encapsulated in the String object returned by the overridden
version of the ToString method dened in the class named B . (See Listing 2 (p. 103) .)
10.5.4.2.3 Overridden version of ToString was executed
This veries that even though the reference to the object of the class B was stored in a reference variable
of type Object , the overridden version of the ToString method in the class named B was executed
(instead of the default version dened in the class named Object) . This is runtime polymorphic
behavior , as described in a previous module.
10.5.4.2.4 Once again, what is the rule?
The selection of a method for execution is based on the actual type of object whose reference is stored in
a reference variable, and not on the type of the reference variable on which the method is called.
10.5.4.3 An object of the class C
Finally, the code in Listing 6 (p. 106)
•
•
•
•
Instantiates a new object of the class C
Stores the object's reference in a reference variable of type Object
Calls the ToString method on the reference and saves the returned string
Displays the returned string on the computer screen
Listing 6
. An object of the class C.
Object varC = new C();
String v3 = varC.ToString();
Console.WriteLine(v3);
//Pause until user presses any key.
Console.ReadKey();
}//end Main
}//end class Polymorph04
10.5.4.3.1 What will the output look like?
By now, you should know what to expect in the way of text appearing on the computer screen. The code in
Listing 6 (p. 106) causes the text shown in Figure 3 (p. 106) to be displayed.
Figure 3
. Text output for overridden ToString method and class C.
ToString in class C
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
107
10.5.4.3.2 Overridden version of ToString was called
This conrms what you should already have known by now. In particular, even though the reference to the
object of the class C was stored in a reference variable of type Object , the overridden version of the
ToString method dened in the class named C was executed. Again, this is runtime polymorphic
behavior based on class inheritance and method overriding.
10.5.5 No downcasting was required
It is also very important to note that no downcasting was required in order to call the ToString method
in any of the cases shown above.
Because a default version of the ToString method is dened in the Object class, that method can
be called without a requirement for downcasting on a reference to any object stored in a variable of type
Object . This holds true for any of the methods dened in the class named Object .
10.5.6 A generic array object
Therefore, if we create an array object designed to store references of type Object , we can store
(potentially mixed) references to any type of object in that array. We can later call any of the methods
dened in the Object class on any of the references stored in the array.
The result will be the execution of the overridden version of the method as dened in the class from
which the object was instantiated, or the version inherited into that class if the method is not overridden in
that class. Current jargon would say that the behavior of the method is appropriate for the type of object
on which it is called.
10.5.7 Collections
The C# library provides a number of generic data structure classes, such as Stack , and Queue , which
store and retrieve references to objects as type Object . These classes can be used to create objects that
can store and retrieve objects of any class.
10.6 Run the program
I encourage you to copy the code from Listing 7 (p. 108) . Use that code to create a C# console project.
Compile and run the project. Experiment with the code, making changes, and observing the results of your
changes. Make certain that you can explain why your changes behave as they do.
10.7 Run my program
Click here
6
to download a zip le containing my version of the program. Extract the folder named
Polymorph04 from the zip le and save it somewhere on your disk. Start Visual C# 2010 Express
and select Open Project... from the File menu. Navigate to the project folder and select the le with
the extension of .sln . This should cause the project to open and be ready to run or debug as described
in the earlier module titled Getting Started
7
.
10.8 Summary
Polymorphism manifests itself in C# in the form of multiple methods having the same name.
From a practical programming viewpoint, polymorphism manifests itself in three distinct forms in C#:
6 http://cnx.org/content/m49506/latest/Polymorph04.zip
7 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 10. XNA0116-RUNTIME POLYMORPHISM AND THE OBJECT
CLASS
108
• Method overloading
• Method overriding through class inheritance
• Method overriding through interface inheritance
In this module, I have continued my discussion of the implementation of polymorphism using method overriding through class inheritance, and have concentrated on a special case in that category.
More specically, I have discussed the use of the Object class as a completely generic type for storing
references to objects of subclass types, and have explained how that results in a very useful form of runtime
polymorphism.
I briey mentioned the default version of the methods dened in the Object class, and explained that
in many cases, those default versions are meant to be overridden.
I provided a sample program that illustrates the overriding of the ToString method, which is one of
the methods dened in the Object class.
10.9 Miscellaneous
This section contains a variety of miscellaneous information.
Housekeeping material
• Module name: Xna0116-Runtime Polymorphism and the Object Class
• File: Xna0116.htm
• Published: 02/27/14
Disclaimers: Financial : Although the Connexions site makes it possible for you to download
a PDF le for this module at no charge, and also makes it possible for you to purchase a pre-printed
version of the PDF le, you should be aware that some of the HTML elements in this module may
not translate well into PDF.
I also want you to know that, I receive no nancial compensation from the Connexions website even
if you purchase the PDF version of the module.
In the past, unknown individuals have copied my modules from cnx.org, converted them to Kindle
books, and placed them for sale on Amazon.com showing me as the author. I neither receive
compensation for those sales nor do I know who does receive compensation. If you purchase such
a book, please be aware that it is a copy of a module that is freely available on cnx.org and that it
was made and published without my prior knowledge.
Aliation : I am a professor of Computer Information Technology at Austin Community College
in Austin, TX.
10.10 Complete program listing
A complete listing of the program discussed in this module is provided in Listing 7 (p. 108) .
Listing 7
. Project Polymorph04.
/*Project Polymorph04
Copyright 2009, R.G.Baldwin
This program illustrates polymorphic behavior
and the Object class
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
109
Program output is:
A
ToString in class B
ToString in class C
*********************************************************/
using System;
class A : Object {
//This class is empty
}//end class A
//======================================================//
class B : A {
public override String ToString() {
return "ToString in class B";
}//end overridden ToString()
}//end class B
//======================================================//
class C : B {
public override String ToString() {
return "ToString in class C";
}//end overridden ToString()
}//end class B
//======================================================//
public class Polymorph04 {
public static void Main() {
Object varA = new A();
String v1 = varA.ToString();
Console.WriteLine(v1);
Object varB = new B();
String v2 = varB.ToString();
Console.WriteLine(v2);
Object varC = new C();
String v3 = varC.ToString();
Console.WriteLine(v3);
//Pause until user presses any key.
Console.ReadKey();
}//end Main
}//end class Polymorph04
-end-
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
110
CHAPTER 10. XNA0116-RUNTIME POLYMORPHISM AND THE OBJECT
CLASS
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
Chapter 11
Xna0118-The XNA Framework and the
1
Game Class
Revised: Sat May 07 18:29:12 CDT 2016
This page is part of a Book titled XNA Game Studio
2
.
11.1 Table of Contents
• Table of Contents (p. 111)
• Preface (p. 113)
· Moving from C# to XNA (p. 114)
* My objective (p. 114)
* Fasten your seatbelt (p. 114)
· Viewing tip (p. 114)
* Figures (p. 114)
* Listings (p. 115)
• General background information (p. 115)
· A software framework (p. 115)
* What is a software framework? (p. 115)
· New project choices in Visual C# (p. 115)
· A Console Application (p. 116)
* Start debugging (p. 116)
* The Main method (p. 116)
* The starting point for a Console Application (p. 116)
· A Windows Game Application (p. 116)
* Source code les (p. 117)
* Build and debug (p. 117)
* The le named Program.cs (p. 117)
· Instantiate a Game1 object and call the Run method (p. 118)
· What do we know about the class named Game1? (p. 118)
· The using directive (p. 118)
· Memory management (p. 118)
1 This content is available online at <http://cnx.org/content/m49509/1.2/>.
2 http://cnx.org/contents/GY804-eY
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
111
112
CHAPTER 11. XNA0118-THE XNA FRAMEWORK AND THE GAME CLASS
· Don't modify the le named Program.cs (p. 119)
* The Game1 class (p. 119)
* How to create a game (p. 121)
· Game1 extends Game (p. 121)
· Main method calls the Run method (p. 121)
· The Run method (p. 121)
· Override ve methods (p. 121)
· Initialization (p. 121)
· The game loop (p. 121)
· Override Update for game logic (p. 122)
· Override the Draw method (p. 122)
• Preview (p. 122)
· Program output (p. 122)
· What if you don't honor the transparent background? (p. 123)
· Not a very exciting program (p. 123)
• Discussion and sample code (p. 124)
· Creating a new Windows Game project (p. 124)
* Step 1: Create a new Windows Game project named XNA0118Proj (p. 124)
· Select a Windows Game project (p. 124)
* Step 2: Add your image le to the Content folder (p. 126)
· Add your image to the Content folder (p. 128)
· The Asset Name (p. 128)
* Steps 3, 4, and 5: Write code (p. 128)
· Modify two overridden methods (p. 128)
· Will discuss in fragments (p. 128)
· Beginning of the class named Game1 (p. 128)
* The namespace (p. 128)
· General information (p. 129)
*
*
*
*
The superclass named Game (p. 129)
Overridden methods (p. 129)
The game loop (p. 129)
The big picture (p. 129)
· The constructor for the Game1 class (p. 130)
*
*
*
*
*
What is a constructor? (p. 130)
A new GraphicsDeviceManager object (p. 130)
Passing the this keyword as a parameter (p. 131)
The ContentManager (p. 131)
When the constructor in Listing 5 terminates... (p. 131)
· The Run method of the Game class (p. 131)
* The BeginRun method (p. 132)
· The game loop (p. 132)
· The overridden LoadContent method (p. 132)
* Two new instance variables (p. 132)
* The Texture2D class (p. 132)
* The SurfaceFormat enumeration (p. 133)
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
113
* The Texture Format of my image (p. 133)
· A new SpriteBatch object (p. 133)
* The SpriteBatch class (p. 133)
· This can be confusing (p. 134)
· The inherited property (p. 134)
* The new code (p. 134)
* Generic methods (p. 134)
· Required syntax for the Load method (p. 134)
· What do the angle brackets mean? (p. 134)
· Calling the Load method of the current ContentManager (p. 135)
· Populate the variable named myTexture (p. 135)
* The Vector2 structure (p. 135)
· The overridden Game.Draw method (p. 135)
* Game loop timing (p. 135)
· More general information (p. 136)
*
*
*
*
What
What
What
What
is a sprite? (p. 136)
is a bitmap? (p. 136)
is GDI+? (p. 136)
about our image? (p. 136)
· Beginning of the Game.Draw method (p. 136)
* GameTime information (p. 137)
* The call to the GraphicsDevice.Clear method (p. 137)
* The Color class (p. 137)
· Type ARGB (p. 137)
· Constructors, methods, and constants (p. 137)
* Code to draw the sprite (p. 138)
· Honor the alpha values (p. 138)
· Ignore the alpha values (p. 138)
· Drawing the sprite(s) (p. 138)
* Overloaded Draw methods (p. 138)
* The SpriteBatch.End method (p. 139)
· Call Game.Draw on the superclass (p. 139)
* Execute both versions of the overridden method (p. 139)
* A required statement (p. 139)
· The end of the program (p. 140)
•
•
•
•
•
Run the program (p. 140)
Run my program (p. 140)
Summary (p. 140)
Miscellaneous (p. 140)
Complete program listing (p. 141)
11.2 Preface
This module is one in a collection of modules designed primarily for teaching GAME 1343 Game and
Simulation Programming I at Austin Community College in Austin, TX. These modules are intended
to supplement and not to replace the textbook.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
114
CHAPTER 11. XNA0118-THE XNA FRAMEWORK AND THE GAME CLASS
The modules were originally published for use with XNA 3.1 and have been upgraded for XNA 4.0. This
upgrade had very little impact on earlier modules in this collection. However, beginning with this module,
we begin to see major dierences between version 3.1 and version 4.0 of XNA. In May of 2016, the modules
are being updated for use with version 4.0 Refresh . This will have very little impact on the modules.
Here is what Microsoft has to say about the newer product:
Microsoft XNA Game Studio 4.0 Refresh updates XNA Game Studio 4.0 to x bugs and add
support for developing games that target Windows Phone OS 7.1 and developing games in Visual
Basic.
This course doesn't address Visual Basic or Windows Phone, but bug xes are always welcome.
An earlier module titled Getting Started 3 provided information on how to get started programming with
Microsoft's XNA Game Studio.
11.2.1 Moving from C# to XNA
All of the modules prior to this one have been preparing you for this module. This is the module where we
will begin applying what you have learned about C# and OOP to the XNA framework.
11.2.1.1 My objective
My objective is that you fully understand what you are doing when you write C# programs using the XNA
framework. I don't want you to simply be lling in the blanks and hacking something together in hopes
that you can nd a combination of statements that seems to work. If we can accomplish that, you will
be prepared to go much further into the sophisticated use of the XNA framework on your own after you
complete the course.
11.2.1.2 Fasten your seatbelt
This module may be a rough ride from a technical viewpoint, so fasten your seatbelt, arm yourself with a
pot of coee, and let's go.
11.2.2 Viewing tip
I recommend that you open another copy of this module in a separate browser window and use the following
links to easily nd and view the Figures and Listings while you are reading about them.
11.2.2.1 Figures
•
•
•
•
Figure 1 (p. 117) . Initial game window.
Figure 2 (p. 122) . Raw image with an (almost) transparent background.
Figure 3 (p. 122) . Cropped upper-left corner of the game window
Figure 4 (p. 123) . Cropped upper-left corner of the game window without honoring alpha transparency.
•
•
•
•
Figure
Figure
Figure
Figure
5
6
7
8
(p.
(p.
(p.
(p.
124)
125)
126)
134)
.
.
.
.
Select New Project on the Visual C# File menu.
Select a Windows Game project.
Solutions explorer and properties window exposed.
The Load method of the ContentManager class.
3 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
115
11.2.2.2 Listings
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
•
•
•
•
•
•
•
•
•
•
1 (p. 116) . Initial contents of the le named Program.cs for a Console Application.
2 (p. 118) . The le named Program.cs for a Windows Game project.
3 (p. 119) . Initial contents of the le named Game1.cs.
4 (p. 128) . Beginning of the class named Game1.
5 (p. 130) . Constructor for the Game1 class.
6 (p. 132) . The overridden LoadContent method.
7 (p. 136) . Beginning of the Game.Draw method.
8 (p. 138) . Draw the sprite.
9 (p. 139) . Call Game.Draw on the superclass.
10 (p. 141) . The Game1 class for the project named XNA0118Proj.
11.3 General background information
11.3.1 A software framework
XNA is a very sophisticated C# application. It isn't simply a program in the sense of the programs that you
have seen so far in this course or a word processor or a spread sheet. Instead, it is a software framework
designed specically to make it easier for you to create computer games using the C# programming language.
11.3.1.1 What is a software framework?
Here is part of what Wikipedia
4
has to say about a software framework:
A software framework, in computer programming, is an abstraction in which common code providing generic functionality can be selectively overridden or specialized by user code providing specic
functionality. Frameworks are a special case of software libraries in that they are reusable abstractions of code wrapped in a well-dened API, yet they contain some key distinguishing features that
separate them from normal libraries.
Software frameworks have these distinguishing features that separate them from libraries or normal user
applications:
inversion of control - In a framework, unlike in libraries or normal user applications, the overall
program's ow of control is not dictated by the caller, but by the framework.
2. default behavior - A framework has a default behavior. This default behavior must actually be
some useful behavior and not a series of no-ops.
3. extensibility - A framework can be extended by the user usually by selective overriding or specialized
by user code providing specic functionality
4. non-modiable framework code - The framework code, in general, is not allowed to be modied.
Users can extend the framework, but not modify its code.
1.
In short, a framework is a computer program that helps you to write computer programs . The description
given above is a good match for the XNA framework.
11.3.2 New project choices in Visual C#
Up to this point in this collection of modules, all of the programs that I have explained have been Visual
C# Console Applications .
Assuming that you have Visual C# 2010 and XNA Game Studio 4.0 Refresh installed on your computer,
if you start Visual C# and select New Project from the File menu, you have a choice of about a
4 http://en.wikipedia.org/wiki/Software_framework
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
116
CHAPTER 11. XNA0118-THE XNA FRAMEWORK AND THE GAME CLASS
dozen dierent kinds of projects that you can create within Visual C#. One of those choices is to create a
Console Application .
11.3.3 A Console Application
When you create a Console Application, a project tree is created on your disk containing numerous folders
and les. One of those les is a C# source code le named Program.cs . This le, which is the skeleton
of a new class named Program , is opened in the editor pane in Visual C#.
The skeleton code in the le looks something like Listing 1 (p. 116) when it rst opens.
Listing 1
using
using
using
using
. Initial contents of the le named Program.cs for a Console Application.
System;
System.Collections.Generic;
System.Linq;
System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
}
}
}
11.3.3.1 Start debugging
If you select Start Debugging
on the Debug menu, the program will run and terminate almost
immediately. At that point, the project tree will have expanded to contain more les and folders even
though the program hasn't yet done anything useful.
11.3.3.2 The Main method
As you have learned in earlier modules, every C# program must have a Main method. Program execution
begins and ends in the Main method. When control is in the Main method and it no longer has any
code to execute, the program terminates. The Main method in Listing 1 (p. 116) is empty, which explains
why the program runs and terminates almost immediately.
11.3.3.3 The starting point for a Console Application
This is the starting point for creating a Console Application in Visual C#. To cause your program to exhibit
some useful behavior, you may add code inside the Main method, add new methods to the Program
class, dene new classes, instantiate objects, call methods on those objects etc.
If you have studied the earlier modules in this collection, this will not be new information for you.
11.3.4 A Windows Game Application
Another choice that you have when creating a new project is a Windows Game (4.0) application.
Once again, a project tree is created on your disk but it contains more folders and les than the tree that
is created for a console application. (Some of the les are initially empty.)
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
117
11.3.4.1 Source code les
As before, there is a C# source code le named Program.cs along with another C# source code le
named Game1.cs . Unlike before, however, the le named Program.cs is not opened in the editor pane
of Visual C#. Instead, the le named Game1.cs is opened in the editor pane.
11.3.4.2 Build and debug
If you debug this program at this point, it does not run and terminate immediately like a console application.
Instead it runs and displays a game window like the one shown in Figure 1 (p. 117) (except that it is quite
a bit larger).
Figure 1
. Initial game window.
The game window will remain on your computer screen until you terminate the program by clicking the X
in the upper-right corner or terminate it in some other way.
11.3.4.3 The le named Program.cs
The le named Program.cs doesn't automatically open in the editor for one simple reason.
The creators
of XNA didn't intend for us to modify it. However, it will be instructive for us to take a look at it anyway.
The source code contained in the le named Program.cs is shown in Listing 2 (p. 118) .
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
118
CHAPTER 11. XNA0118-THE XNA FRAMEWORK AND THE GAME CLASS
Listing 2
. The le named Program.cs for a Windows Game project.
using System;
namespace WindowsGame2
{
#if WINDOWS || XBOX
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main(string[] args)
{
using (Game1 game = new Game1())
{
game.Run();
}
}
}
#endif
}
11.3.4.3.1 Instantiate a Game1 object and call the Run method
The code in Listing 2 (p. 118) :
• Instantiates a new object of a class named Game1 .
• Saves the object's reference in a reference variable named game .
• Calls the Run method on the reference to the Game1 object
This code is inside the Main method in the le named Program.cs . The Main method runs when
the program is started. The call to the Run method starts the game portion of the program running.
11.3.4.3.2 What do we know about the class named Game1?
We don't know anything about the class named Game1 yet, but we will learn about it shortly.
get to that, however, I need to explain some unusual syntax in Listing 2 (p. 118) .
Before we
11.3.4.3.3 The using directive
You learned earlier that one of the benets of the using directive is to eliminate the requirement to always
type the namespace (such as System ) when referring to a class that belongs to that namespace. That is
the purpose of the using directive at the top of Listing 2 (p. 118) .
However, there is another benet that derives from the using directive that may be more important.
11.3.4.3.4 Memory management
One of the big issues in game programming (or any kind of programming that makes use of graphics les,
sound les, or other large resource les) is to make certain that the memory occupied by those resources is
freed up as soon as the resource is no longer needed.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
119
Without going into a lot of detail, the use of the using keyword inside the Main method in Listing
2 (p. 118) will assure that the Dispose method is called to free up all of the memory occupied by the
Game1 object when control reaches the closing curly brace following the using keyword.
11.3.4.3.5 Don't modify the le named Program.cs
As an XNA game programmer, you shouldn't normally have any reason to modify the contents of the le
named Program.cs . We need to leave it alone and modify the le named Game1.cs instead.
11.3.4.4 The Game1 class
I told you earlier that when you create a new Windows Game project, the le named
opened in the editor pane. Listing 3 (p. 119) shows the contents of that le.
Listing 3
using
using
using
using
using
using
using
using
using
using
. Initial contents of the le named Game1.cs.
System;
System.Collections.Generic;
System.Linq;
Microsoft.Xna.Framework;
Microsoft.Xna.Framework.Audio;
Microsoft.Xna.Framework.Content;
Microsoft.Xna.Framework.GamerServices;
Microsoft.Xna.Framework.Graphics;
Microsoft.Xna.Framework.Input;
Microsoft.Xna.Framework.Media;
namespace WindowsGame2
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
///
///
///
///
///
///
///
///
<summary>
Allows the game to perform any initialization
it needs to before starting to run.
This is where it can query for any required
services and load any non-graphic
related content. Calling base.Initialize
will enumerate through any components
and initialize them as well.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
Game1.cs
is
120
CHAPTER 11. XNA0118-THE XNA FRAMEWORK AND THE GAME CLASS
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
}
base.Initialize();
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
}
// TODO: use this.Content to load your game content here
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if(GamePad.GetState(PlayerIndex.One).Buttons.Back==ButtonState.Pressed)
this.Exit();
// TODO: Add your update logic here
}
base.Update(gameTime);
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
121
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
}
}
}
base.Draw(gameTime);
11.3.4.5 How to create a game
I will begin with a simplied explanation of how to create games using XNA.
11.3.4.5.1 Game1 extends Game
To begin with, note that the Game1 class extends the Game class. We do not modify the Game class.
Instead, we modify the behavior of the Game class by extending it and overriding some of its methods.
11.3.4.5.2 Main method calls the Run method
As you saw earlier in Listing 2 (p. 118) , the Main method in the le named Program.cs instantiates
an object of the Game1 class (shown in Listing 3 (p. 119) ) and calls the Run method on that object.
That starts the program running.
11.3.4.5.3 The Run method
The class named Game1 does not dene and does not override a method named Run . However, it does
inherit a method named Run from the Game class.
Therefore, when the Run method is called on the object of the Game1 class, the version of the
Run method that is dened in the superclass named Game is executed.
11.3.4.5.4 Override ve methods
The skeleton code for the Game1 class in Listing 3 (p.
from the Game class:
1.
2.
3.
4.
5.
119) overrides the following ve methods inherited
Initialize
LoadContent
UnloadContent
Update
Draw
11.3.4.5.5 Initialization
The rst three methods contain code that is needed to get everything initialized at the start of the game
play and to shut down the program at the end of the game play.
11.3.4.5.6 The game loop
Once the game is initialized, the Run method, or some method called by the Run method ping-pongs
back and forth between calls to the overridden Update method and the overridden Draw method.
(Note, however that the two methods don't necessarily take turns executing.)
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
122
CHAPTER 11. XNA0118-THE XNA FRAMEWORK AND THE GAME CLASS
11.3.4.5.6.1 Override Update for game logic
You override the Update method to create the program logic associated with game play.
To accomplish
this, you will likely need to dene other methods, dene other classes, instantiate objects of other classes,
call methods on those objects, test the keyboard, test the mouse, etc. In other words, at this point you
need to know how to program in C# .
11.3.4.5.6.2 Override the Draw method
You override the Draw method to cause the various graphics objects in your game to be rendered in the
game window shown in Figure 1 (p. 117) .
This module includes an explanation of a very simple program that displays a green arrow sprite near
the upper-left corner of the game window (see Figure 3 (p. 122) ).
11.4 Preview
I will create a simple Windows game application that imports the image shown in Figure 2 (p. 122) . Note
that this is a rectangular image with an (almost) transparent background. (The values of the alpha bytes
outside the blue elliptical shape are about 5.)
Figure 2
. Raw image with an (almost) transparent background.
If you would like to replicate my program using this image, you should be able to right-click on the image in
Figure 2 (p. 122) , download it, and save it on your computer. You can save it under any name you choose
but the le name extension should be png .
11.4.1 Program output
The program displays the image near the upper-left corner of the game window and honors the transparency
of the background as shown in Figure 3 (p. 122) .
Figure 3
. Cropped upper-left corner of the game window.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
123
11.4.2 What if you don't honor the transparent background?
Figure 4 (p. 123) shows the result of causing the alpha transparency value to be ignored and allowing the
pixels that are almost transparent in Figure 3 (p. 122) to be opaque.
Figure 4
. Cropped upper-left corner of the game window without honoring alpha transparency.
Honoring alpha transparency is the default in XNA 4.0. Figure 4 (p. 123) was created by setting the
Premultiply Alpha property (see Figure 7 (p. 126) ) of the image named gorightarrow.png to a value
of False and then re-running the program.
11.4.3 Not a very exciting program
This program isn't very exciting because there is no motion and no sound. The program simply draws the
same image in the same position during every iteration of the game loop. Despite that, this program will
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
124
CHAPTER 11. XNA0118-THE XNA FRAMEWORK AND THE GAME CLASS
give us the opportunity to drill down into the technical aspects of several areas of the XNA framework.
11.5 Discussion and sample code
11.5.1 Creating a new Windows Game project
Before getting into the details of the code, I'm going to walk you through the steps involved in creating this
Windows Game project using XNA.
11.5.1.1 Step 1: Create a new Windows Game project named XNA0118Proj
Pull down the Visual C# File menu and select New Project as shown in Figure 5 (p.
Figure 5 . Select New Project on the Visual C# File menu.
124) .
11.5.1.1.1 Select a Windows Game project
Select XNA Game Studio 4.0 in the left pane of the New Project dialog. Select the Windows
Game (4.0) icon and enter the name of your project in the Name eld. Enter the storage location in
the Location eld and click the OK button that is o screen further to the right in Figure 6 (p. 125) .
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
125
Figure 6
. Select a Windows Game project.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
126
CHAPTER 11. XNA0118-THE XNA FRAMEWORK AND THE GAME CLASS
11.5.1.2 Step 2: Add your image le to the Content folder
If necessary, pull down the View menu and select Other Windows . Then select Solution Explorer
and/or Properties Window so that they are exposed on the right side of the IDE as shown in Figure 7
(p. 126) . (Note that the initial view of the Properties window is dierent from that shown in Figure 7
(p. 126) .)
Figure 7
. Solutions explorer and properties window exposed.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
127
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
128
CHAPTER 11. XNA0118-THE XNA FRAMEWORK AND THE GAME CLASS
11.5.1.2.1 Add your image to the Content folder
Assuming that your project is named XNA0118proj , right click on the XNA0118projContent
(Content) folder in the Solution Explorer . Select Add/Existing Item in the dialog that follows.
Browse to the image le that you are going to use and click the Add button. A copy of the image le
should appear in the Content folder.
11.5.1.2.2 The Asset Name
Click the image le name in the Content folder and the information in the Properties Window should
change to describe that le. Note the value of the Asset Name property in the Properties Window
. You will need it later. (In this example, the value of the Asset Name in Figure 7 (p. 126) is
gorightarrow .)
11.5.1.3 Steps 3, 4, and 5: Write code
The next three steps involve writing code to upgrade the skeleton version of the class denition of the
Game1 class. I will explain that new code later. For now, the three steps for this example program are:
• Declare two instance variables named myTexture and spritePosition .
• Add a statement to the LoadContent method to load the image.
• Add statements to the Draw method to cause the image to be drawn in the game window.
11.5.1.3.1 Modify two overridden methods
As you saw in Listing 3 (p. 119) , there are ve overridden methods in the class denition for the Game1
class that you can modify to customize the class for your game. This program modies only two of those
methods:
•
•
LoadContent
Draw
I will discuss those two methods along with some other material in this module. I will defer a detailed
discussion of the other three methods until a future module when I write a program that modies them.
11.5.1.3.2 Will discuss in fragments
A complete listing of the modied denition of the Game1 class is provided in Listing 10 (p. 141) near
the end of the module. I will explain selected code fragments in the following paragraphs.
11.5.2 Beginning of the class named Game1
The beginning of the class denition for the class named
Listing 4
Game1
is shown in Listing 4 (p. 128) .
. Beginning of the class named Game1.
namespace XNA0118Proj{
public class Game1 : Microsoft.Xna.Framework.Game{
11.5.2.1 The namespace
This class denition belongs to the XNA0118Proj namespace. This is the name of the folder containing
all of the other folders and les in the project tree as shown in the Solution Explorer in Figure 7 (p. 126) .
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
129
11.5.3 General information
11.5.3.1 The superclass named Game
The new class named Game1 extends the existing class named Game . You will nd the documentation
for the class named Game here 5 . The description of this class in the documentation is fairly modest. It
says simply
"Provides basic graphics device initialization, game logic, and rendering code."
11.5.3.2 Overridden methods
As I explained earlier, in order to write a program that runs under the XNA framework, you need to override
some or all of ve methods that are inherited into the Game1 class from the Game class. Let's see
some of what the documentation for the Game class has to say about these methods.
•
•
•
•
•
Initialize - Called after the Game and GraphicsDevice are created, but before LoadContent
.Override this method to query for any required services, and load any non-graphics resources. Use
LoadContent to load graphics resources.
LoadContent - Called when graphics resources need to be loaded. Override this method to load
any game-specic graphics resources. This method is called by Initialize . Also, it is called any time
the game content needs to be reloaded, such as when the DeviceReset event occurs.
UnloadContent - Called when graphics resources need to be unloaded. Override this method to
unload any game-specic graphics resources.
Update - Called when the game has determined that game logic needs to be processed. This might
include the management of the game state, the processing of user input, or the updating of simulation
data. Override this method with game-specic logic.
Draw - Called when the game determines it is time to draw a frame. Override this method with
game-specic rendering code.
11.5.3.3 The game loop
According to the documentation for the Game class,
" Update and Draw are called at dierent rates depending on whether IsFixedTimeStep is
true or false.
If IsFixedTimeStep is false, Update and Draw will be called in a continuous loop.
If IsFixedTimeStep is true, Update will be called at the interval specied in TargetElapsedTime
, while Draw will only be called if an Update is not due.
If Draw is not called, IsRunningSlowly will be set to true.
For more information on xed-step and variable-step game loops, see Application Model Overview 6 ."
11.5.3.4 The big picture
Sifting through all of this detail in an attempt to get a big picture view, we see that we should:
• Override Initialize for any special initialization and for loading any non-graphic resources. For
example, sound les are non-graphic resources.
• Override LoadContent to load all graphic resources.
• Override UnloadContent if any graphic resources need to be unloaded.
• Override Update to implement all of the game logic.
5 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.game.aspx
6 http://msdn.microsoft.com/en-us/library/bb203873.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
130
CHAPTER 11. XNA0118-THE XNA FRAMEWORK AND THE GAME CLASS
Draw to draw an individual frame based on values created and stored by the overridden
method, such as the current position of a sprite.
• Be aware that there are two dierent approaches to controlling the timing of the game loop, depending
on whether the IsFixedTimeStep 7 property of the Game object is true or false. The default value is
true, meaning that the game will attempt to call the Update method on a xed time interval even
if that means that the Draw method doesn't get called during some iterations of the game loop.
• Override
Update
11.5.4 The constructor for the Game1 class
Listing 5 (p. 130) shows the declaration of two instance variables followed by the constructor for the Game1
class. One of the instance variables is used in the constructor and the other is used later in the program.
Listing 5
. Constructor for the Game1 class.
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
public Game1() {
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}// end constructor
11.5.4.1 What is a constructor?
A constructor is a special method-like structure that is executed once and only once during the instantiation
of an object.
The rst statement in the Main method in Listing 2 (p. 118) uses the new operator to cause the
constructor to be executed. When the constructor completes its task, it returns a reference to the object
just constructed. That reference is stored in the local reference variable of type Game1 named game in
Listing 2 (p. 118) .
11.5.4.2 A new GraphicsDeviceManager object
The rst statement in the constructor in Listing 5 (p. 130) instantiates a new object of the class GraphicsDeviceManager 8 and stores that object's reference in the instance variable named graphics .
The documentation for GraphicsDeviceManager 9 isn't very descriptive. Here is some of what Aaron
Reed (the author of the Learning XNA books from O'Reilly) has to say on the topic.
"This (GraphicsDeviceManager) is a very important object because it provides you, as a developer,
with a way to access the graphics device on your ... The GraphicsDeviceManager object has a
property called GraphicsDevice that represents the actual graphics device on your machine."
He goes on to explain how the GraphicsDevice object acts as a conduit between your XNA program and
the physical graphics device on your machine.
7 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.game.isxedtimestep.aspx
8 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphicsdevicemanager.aspx
9 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphicsdevicemanager.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
131
11.5.4.3 Passing the this keyword as a parameter
Note the parameter that is passed to the GraphicsDeviceManager constructor in Listing 5 (p. 130)
. The documentation tells us that the parameter must be of type Game and is the Game that the
GraphicsDeviceManager should be associated with.
I don't recall having discussed the keyword this earlier in this collection of modules. According to
Jesse Liberty ( Programming C#
the keyword
this
10
from O'Reilly)
is a variable that references the current instance of a class or struct.
Any time that the code in an instance method needs a reference to the object to which the method belongs,
this is available as a reference to that object.
In this case, the code in Listing 5 (p. 130) says to instantiate a new GraphicsDeviceManager object
and to associate it with this Game object.
11.5.4.4 The ContentManager
A Game object has a property named Content
11
.
that references an object of the class ContentManager
According to the documentation, the ContentManager
"is the run-time component which loads managed objects from the binary les produced by the
design time content pipeline. It also manages the lifespan of the loaded objects..."
The second statement in the constructor in Listing 5 (p. 130) noties the ContentManager that the
folder named " Content" is the root of a directory tree in which content for the game will be stored. In
this program, we only have one item of content and it is the image le that was added to the Content
folder earlier.
11.5.4.5 When the constructor in Listing 5 terminates...
When the constructor terminates, the new Game1 object occupies memory and a reference to the object
is stored in the variable named game in Listing 2 (p. 118) . The code in the Main method in Listing 2
(p. 118) immediately calls the Run method on the Game1 object's reference.
The Game1 class neither denes nor overrides a method named Run . However, it does inherit a
method named Run 12 from the Game class. Therefore, the method named Run that is dened in the
Game class is executed.
11.5.5 The Run method of the Game class
Here is what the documentation has to say about this method.
"Call this method to initialize the game, begin running the game loop, and start processing events
for the game.
This method calls the game Initialize and
processing events for the game."
BeginRun
methods before it begins the game loop and starts
10 http://oreilly.com/catalog/9780596001179
11 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.content.contentmanager.aspx
12 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.game.run.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
132
CHAPTER 11. XNA0118-THE XNA FRAMEWORK AND THE GAME CLASS
11.5.5.1 The BeginRun method
We already know about the Initialize
BeginRun method.
method. Here is what the documentation
13
has to say about the
"Called after all components are initialized but before the rst update in the game loop."
11.5.6 The game loop
At this point, after the Initialize and the LoadContent methods have been called, either the Run
method or the BeginRun method, or perhaps some other method that is called by one of those methods
goes into a loop calling the Update method and the Draw method.
The timing and the order in which the two methods are called is determined by the value of IsFixedTimeStep as explained earlier 14 .
But, we're getting ahead of ourselves. We need to slow down and discuss the overridden LoadContent
method.
11.5.7 The overridden LoadContent method
The overridden LoadContent method is shown in its entirety in Listing 6 (p. 132) along with the
declaration of two instance variables.
Listing 6
. The overridden LoadContent method.
//Declare two variables
Texture2D myTexture;
Vector2 spritePosition = new Vector2(10.0f,15.0f);
protected override void LoadContent() {
// Create a new SpriteBatch, which can be used
// to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
//Load the image
myTexture =
Content.Load<Texture2D>("gorightarrow");
}//end LoadContent
11.5.7.1 Two new instance variables
Listing 6 (p. 132) begins by declaring two new instance variables of types Texture2D and Vector2
named myTexture and spritePosition respectively.
The variable named myTexture will be used in the LoadContent method of Listing 6 (p. 132)
to store a reference to a Texture2D object created from the image le with the Asset Name of
gorightarrow (see Figure 7 (p. 126) ). It will also be used later in the overridden Draw method where
it is the sprite being drawn.
The variable named spritePosition will be used later in the overridden Draw method to specify the
location to draw the sprite. I will have more to say about this variable later.
11.5.7.2 The Texture2D class
Here is some of what the documentation
15
has to say about the
Texture2D
class.
13 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.game.beginrun.aspx
14 http://cnx.org/content/m49509/latest/XNA0118revised.htm#Update
15 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.texture2d.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
133
"Represents a 2D grid of texels .
A texel represents the smallest unit of a texture that can be read from or written to by the GPU
(Graphics Processing Unit). A texel is composed of 1 to 4 components. Specically, a texel may
be any one of the available texture formats represented in the SurfaceFormat enumeration.
A Texture2D resource contains a 2D grid of texels. Each texel is addressable by a u, v vector.
Since it is a texture resource, it may contain mipmap levels."
You can view a diagram of a texture resource containing a single 3x5 texture with three mipmap levels here
16 . You can read what Wikipedia has to say about mipmaps here 17 . The image used in this program
doesn't have any mipmaps.
11.5.7.3 The SurfaceFormat enumeration
The SurfaceFormat enumeration denes numeric values representing about 50 dierent types of surface
formats such as Rgba32 , which is dened as
"(Unsigned format) 32-bit RGBA pixel format with alpha, using 8 bits per channel."
11.5.7.4 The Texture Format of my image
For example, the Properties Window in Figure 7 (p. 126) shows the image that I used for my program to
have a Texture Format property value of Color . The denition of the SurfaceFormat enumeration
for Color is
"(Unsigned format) 32-bit ARGB pixel format with alpha, using 8 bits per channel."
Note that this is similar to
bytes is dierent.
Rgba32
except that the position of the alpha byte relative to the other three
11.5.8 A new SpriteBatch object
The code in the overridden LoadContent method of Listing 6 (p. 132) begins by instantiating a new
SpriteBatch object and saving its reference in the reference variable named spriteBatch . (That
variable is declared at the top of Listing 3 (p. 119) .)
Note that the statement that instantiates the SpriteBatch object in Listing 6 (p. 132) is already
in the skeleton of the Game1 class when it rst appears in the edit window of the Visual C#
IDE. (See Listing 3 (p. 119) .)
11.5.8.1 The SpriteBatch class
According to the documentation
18
, an object of the
SpriteBatch
class
"Enables a group of sprites to be drawn using the same settings."
The constructor for an object of the SpriteBatch class requires an incoming parameter that is a reference
to the graphicsDevice of the current platform as type GraphicsDevice .
16 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.texture2d.aspx
17 http://en.wikipedia.org/wiki/Mipmap
18 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.spritebatch.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
134
CHAPTER 11. XNA0118-THE XNA FRAMEWORK AND THE GAME CLASS
11.5.8.1.1 This can be confusing
GraphicsDevice is the name of an XNA class. It is also the name of a property of the Game class that
is inherited into the Game1 class. The parameter that is passed to the constructor for the SpriteBatch
object in Listing 6 (p. 132) is the inherited property.
11.5.8.1.2 The inherited property
The inherited property contains a reference to an object of the class GraphicsDevice , which is apparently
populated in conjunction with the instantiation of the GraphicsDeviceManager object in the constructor
of Listing 5 (p. 130) . However it gets populated, it is a reference to the graphicsDevice on the current
platform. This causes the new SpriteBatch object to be aware of the graphicsDevice on the current
platform. It will be used in the Draw method later to draw the sprite.
11.5.8.2 The new code
The last statement in Listing 6 (p. 132) is the new code that I wrote into the overridden LoadContent
method. The Game1 class inherits a property of the Game class named Content . This property
contains a reference to the current ContentManager 19 object.
Therefore, the new code in Listing 6 (p. 132) calls the Load method on the current ContentManager
object.
11.5.8.3 Generic methods
Some methods in C# are known as generic methods 20 , and the Load 21 method of the
class is one of them. The documentation describes the Load method as follows:
ContentManager
"Loads an asset that has been processed by the Content Pipeline."
11.5.8.3.1 Required syntax for the Load method
Figure 8 (p. 134) shows the syntax required for calling the
documentation.
Figure 8
Load
method. This syntax was taken from the
. The Load method of the ContentManager class.
public virtual T Load<T> (string assetName)
11.5.8.3.2 What do the angle brackets mean?
To call this method, you must replace the T between the angle brackets in Figure 8 (p. 134) with the type
of asset to be loaded. According to the documentation 22 ,
" Model , Eect , SpriteFont , Texture , Texture2D , Texture3D and TextureCube
are all supported by default by the standard Content Pipeline processor, but additional types may
be loaded by extending the processor."
19 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.content.contentmanager.aspx
20 http://msdn.microsoft.com/en-us/library/twcad0zb(VS.80).aspx
21 http://msdn.microsoft.com/en-us/library/bb197848.aspx
22 http://msdn.microsoft.com/en-us/library/bb197848.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
135
11.5.8.3.3 Calling the Load method of the current ContentManager
Listing 6 (p. 132) calls the Load method, specifying an asset type of Texture2D , for the purpose of
loading the content identied in Figure 7 (p. 126) with an Asset Name property value of gorightarrow
.
You will recall that this is the value given to the Asset Name property of the image le named
when it was added to the Content folder earlier in this module.
gorightarrow.png
11.5.8.3.4 Populate the variable named myTexture
The value returned from the Load method is assigned to the variable named myTexture in Listing 6
(p. 132) . It will be used later in the Draw method to draw the sprite in the game window as shown in
Figure 3 (p. 122) and Figure 4 (p. 123) .
That completes the denition of the overridden
LoadContent
method.
11.5.8.4 The Vector2 structure
Returning to the variable declarations in Listing 6 (p. 132) , Vector2 23 is a structure (similar to a class
with no inheritance capability) containing two components of type oat named X and Y .
In this program, the structure referred to by spritePosition in Listing 6 (p. 132) is used to encapsulate
the coordinates of the upper-left corner of the sprite (10.0f,15.0f) when the sprite is drawn in the game window
as shown in Figure 3 (p. 122) and more obviously in Figure 4 (p. 123) .
This variable will also be used later in the overridden Draw method.
11.5.9 The overridden Game.Draw method
That brings us to the Draw method inherited from the Game class, shown near the bottom of Listing
3 (p. 119) . According to the documentation 24 , this method is
"Called when the game determines it is time to draw a frame. Override this method with gamespecic rendering code.
Note that signicant changes were made to the required contents of the
as compared to XNA 3.1.
Game.Draw
method in XNA 4.0
11.5.9.1 Game loop timing
As you learned earlier, Update and Draw are called at dierent rates depending on whether IsFixedTimeStep is true or false.
If IsFixedTimeStep is false, Update and Draw will be called sequentially as often as possible.
If IsFixedTimeStep is true, Update will be called at the interval specied in TargetElapsedTime
, while Draw will continue to be called as often as possible. For more information on xed-step and
variable-step game loops, see Application Model Overview 25 ."
Because this program doesn't override the Update method, it doesn't matter how often the Draw
method is called. Each time it is drawn, the sprite is drawn in the same position as shown in Figure 3 (p.
122) and Figure 4 (p. 123) .
23 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.vector2.aspx
24 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.game.draw.aspx
25 http://msdn.microsoft.com/en-us/library/bb203873.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
136
CHAPTER 11. XNA0118-THE XNA FRAMEWORK AND THE GAME CLASS
11.5.10 More general information
11.5.10.1 What is a sprite?
According to the 2D Graphics Overview
26
,
"Sprites are 2D bitmaps drawn directly on the screen, as opposed to being drawn in 3D space.
Sprites are commonly used to display information such as health bars, number of lives, or text such
as scores. Some games, especially older games, are composed entirely of sprites."
11.5.10.2 What is a bitmap?
According to the documentation for the Bitmap
27
class,
"A bitmap consists of the pixel data for a graphics image and its attributes. There are many
standard formats for saving a bitmap to a le. GDI+ supports the following le formats: BMP,
GIF, EXIG, JPG, PNG and TIFF."
11.5.10.3 What is GDI+?
According to the documentation
28
,
"Microsoft Windows GDI+ is a class-based API for C/C++ programmers. It enables applications
to use graphics and formatted text on both the video display and the printer. Applications based
on the Microsoft Win32 API do not access graphics hardware directly. Instead, GDI+ interacts
with device drivers on behalf of applications."
11.5.10.4 What about our image?
Working backwards through the above information, we started with an image le named gorightarrow.png
. We manually added the le to the Content folder producing a game asset with an Asset Name of
gorightarrow (see Figure 7 (p. 126) ).
Then we called the Load method in Listing 6 (p. 132) to load the contents of the le into an object
of type Texture2D and saved that object's reference in the instance variable named myTexture . At
that point in the process, we had converted the contents of our image le into a format that can be thought
of as a sprite. The variable named myTexture contains a reference to our sprite.
11.5.11 Beginning of the Game.Draw method
The Game.Draw method begins in Listing 7 (p. 136) . I am referring to the method here as Game.Draw
to distinguish it from the method named SpriteBatch.Draw , which we will encounter shortly.
Listing 7
. Beginning of the Game.Draw method.
protected override void Draw(GameTime gameTime) {
GraphicsDevice.Clear(Color.CornflowerBlue);
26 http://msdn.microsoft.com/en-us/library/bb203919.aspx
27 http://msdn.microsoft.com/en-us/library/system.drawing.bitmap.aspx
28 http://msdn.microsoft.com/en-us/library/ms533798(VS.85).aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
137
11.5.11.1 GameTime information
Each time the Game.Draw method
is executed, the incoming parameter contains time information
encapsulated in an object of type GameTime 29 . According to the documentation, the GameTime object
provides a
"Snapshot of the game timing state expressed in values that can be used by variable-step (real
time) or xed-step (game time) games."
We won't be using this information in this module, so I won't pursue it further here. However, we will need
the information in future modules when we write code to cause a sprite to be moved and/or animated.
11.5.11.2 The call to the GraphicsDevice.Clear method
The call to the GraphicsDevice.Clear method in Listing 7 (p. 136) is contained in the skeleton code for
the Game1 class as shown in Listing 3 (p. 119) .
The GraphicsDevice class provides overloaded versions of the Clear 30 method. According to the
documentation, the version shown in Listing 7 (p. 136)
"Clears the viewport to a specied color."
This version of the
Clear
method requires a single incoming parameter of type
Color
.
11.5.11.3 The Color class
The documentation describes an object of the Color
31
class as follows:
"A Color object stores a 32-bit value that represents a color. The color value contains four,
8-bit components: alpha, red, green, and blue. The rst 8 bits (the most signicant) contain the
alpha component, the next 8 bits contain the red component, the next 8 bits contain the green
component, and the next 8 bits (the least signicant) contain the blue component. The 32-bit
value is stored in a variable of type ARGB."
11.5.11.3.1 Type ARGB
We learned about the ARGB texture format earlier 32 . Although ARGB is referred to as a type in the
above quotation, it is not a class. Rather, it is a type established using a C-style typedef 33 .
11.5.11.3.2 Constructors, methods, and constants
The Color class provides several overloaded constructors and numerous methods that allow you to perform
various operations on a Color object.
One of the constructors allows you to create a Color object that represents the color of your choice by
specifying the individual values of the alpha, red, green, and blue color components.
In addition, the class provides many constants that represent dierent colors, one of which is named
CornowerBlue . This is the background color of the game window shown in Figure 1 (p. 117) .
You can create Color objects representing those colors simply by calling out the name of the class and
the name of the color as shown by the code in Listing 7 (p. 136) .
29 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.gametime.aspx
30 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.graphicsdevice.clear.aspx
31 http://msdn.microsoft.com/en-us/library/ms534427(VS.85).aspx
32 http://cnx.org/content/m49509/latest/XNA0118revised.htm#Texture
33 http://msdn.microsoft.com/en-us/library/aa911306.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
138
CHAPTER 11. XNA0118-THE XNA FRAMEWORK AND THE GAME CLASS
11.5.11.4 Code to draw the sprite
Three statements are required to draw one sprite and twelve statements are required to draw ten sprites
with the same settings. The sequence consists of a Begin statement, one or more SpriteBatch.Draw
statements, and one End statement.
Listing 8 (p. 138) shows the code that is used to draw our sprite once each time the Game.Draw
method is called. Note that the SpriteBatch.Draw method is called inside the Game.Draw method.
Listing 8
. Draw the sprite.
spriteBatch.Begin();
spriteBatch.Draw(
myTexture,spritePosition,Color.White);
spriteBatch.End();
The image that we used to create the sprite is shown in raw form in Figure 2 (p. 122) . This is a rectangular
image with the pixels outside the blue area having an alpha value of about 5.
11.5.11.4.1 Honor the alpha values
As mentioned earlier, the default case is to honor the alpha values in XNA 4.0. This produces the output
image shown in Figure 3 (p. 122) .
Setting the Premultiply Alpha property value to False in Figure 7 (p. 126) will cause the alpha value
to be ignored. This will produce the output image shown in Figure 4 (p. 123) .
11.5.11.4.2 Ignore the alpha values
As explained earlier, honoring alpha transparency is the default case in XNA 4.0. Figure 4 (p. 123) was
created by setting the Premultiply Alpha property (see Figure 7 (p. 126) ) of the image named
gorightarrow.png to a value of False and then re-running the program. This causes even the pixels with
the very low alpha values to be opaque as shown in Figure 4 (p. 123) .
11.5.11.4.3 Drawing the sprite(s)
You can draw as many sprites as you need following the call to the Begin method in Listing 8 (p. 138) .
Each sprite drawn will be drawn according to the parameters passed to the Begin method.
If you need to draw some sprites with dierent parameters, call the SpriteBatch.End method and
start the sequence over with a new call to the SpriteBatch.Begin method and new parameters.
In this case we only have one sprite to draw. Listing 8 (p. 138) calls the SpriteBatch.Draw method
to draw that sprite and then calls the SpriteBatch.End method to end the drawing sequence.
11.5.11.5 Overloaded Draw methods
There are several overloaded versions of the
, the version used in Listing 8 (p. 138)
34
SpriteBatch.Draw
method. According to the documentation
"Adds a sprite to the batch of sprites to be rendered, specifying the texture, screen position, and
color tint. Before any calls to Draw , you must call Begin . Once all calls to Draw are
complete, call End ."
The code in Listing 8 (p. 138) passes three parameters to the Draw method:
•
myTexture
- The sprite texture. (See Listing 6 (p. 132) .)
34 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.spritebatch.draw.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
139
•
spritePosition
(p. 132) .)
•
Color.White
- The location, in screen coordinates, where the sprite will be drawn. (See Listing 6
- The color channel modulation to use. (Use Color.White for full color with no tinting.)
11.5.11.6 The SpriteBatch.End method
According to the documentation
35
, this method
"Flushes the sprite batch and restores the device state to how it was before Begin was called. Call
End after all calls to Draw are complete."
11.5.12 Call Game.Draw on the superclass
When you instantiate an object from a class that extends another class and overrides a method from the
superclass, the new object contains both the original version and the overridden version of the method.
11.5.12.1 Execute both versions of the overridden method
Often it is desirable or necessary to cause both versions to be executed. The code in Listing 9 (p. 139) shows
the syntax used to cause an overridden method to call the original version of the method using the keyword
base . The keyword base is a reference to that portion of the object that represents the properties,
events, and methods of the superclass.
Listing 9
. Call Game.Draw on the superclass.
base.Draw(gameTime);
}//end Draw method
}//End class
}//End namespace
11.5.12.2 A required statement
The statement shown in Listing 9 (p. 139) is already contained in the skeleton code produced by Visual C#
(see Listing 3 (p. 119) ).
The documentation 36 for the Game.Draw method contains the following:
"In classes that derive from
Game
, it is necessary to make these calls:
Call base.Draw in Draw to enumerate through any graphics components that have been
added to Components . This method will automatically call the Initialize method for every
component that has been added to the collection."
We won't worry about the reason why we must do this at this point. We will simply follow the instructions
and make the call.
35 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.spritebatch.end.aspx
36 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.game.draw.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
140
CHAPTER 11. XNA0118-THE XNA FRAMEWORK AND THE GAME CLASS
11.5.13 The end of the program
That completes the explanation for this program. Because of the simplicity of the program, we had no need
to override the following methods (see Listing 3 (p. 119) ):
• Initialize
• UnloadContent
• Update
We will develop more complicated programs in future modules and will have a need to override one of more
of these methods. I will explain them at that time.
11.6 Run the program
I encourage you to copy the code from Listing 10 (p. 141) . Use that code to create an XNA project.
Compile and run the project. Experiment with the code, making changes, and observing the results of your
changes. Make certain that you can explain why your changes behave as they do.
11.7 Run my program
Click here
37
to download a zip le containing my version of the program. Extract the folder named
XNA0118Proj from the zip le and save it somewhere on your disk. Start Visual C# 2010 Express
and select Open Project... from the File menu. Navigate to the project folder and select the le with
the extension of .sln . This should cause the project to open and be ready to run or debug as described
in the earlier module titled Getting Started
38
.
11.8 Summary
In this module, I used a very simple XNA program to teach you many of the details regarding the incorporation of the XNA framework into the object-oriented C# programming language. I also taught you
about constructors, the this keyword, the base keyword, and some of the dierences between a Console
Application and a Windows Game application.
11.9 Miscellaneous
This section contains a variety of miscellaneous information.
Housekeeping material
• Module name: Xna0118-The XNA Framework and the Game Class
• File: Xna0118.htm
• Published: 02/28/14
Disclaimers: Financial : Although the Connexions site makes it possible for you to download
a PDF le for this module at no charge, and also makes it possible for you to purchase a pre-printed
version of the PDF le, you should be aware that some of the HTML elements in this module may
not translate well into PDF.
37 http://cnx.org/content/m49509/latest/XNA0118Proj.zip
38 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
141
I also want you to know that, I receive no nancial compensation from the Connexions website even
if you purchase the PDF version of the module.
In the past, unknown individuals have copied my modules from cnx.org, converted them to Kindle
books, and placed them for sale on Amazon.com showing me as the author. I neither receive
compensation for those sales nor do I know who does receive compensation. If you purchase such
a book, please be aware that it is a copy of a module that is freely available on cnx.org and that it
was made and published without my prior knowledge.
Aliation : I am a professor of Computer Information Technology at Austin Community College
in Austin, TX.
11.10 Complete program listing
A complete listing of the XNA program discussed in this module is provided in Listing 10 (p. 141) .
Listing 10
using
using
using
using
using
using
using
using
using
. The Game1 class for the project named XNA0118Proj.
System.Collections.Generic;
System.Linq;
Microsoft.Xna.Framework;
Microsoft.Xna.Framework.Audio;
Microsoft.Xna.Framework.Content;
Microsoft.Xna.Framework.GamerServices;
Microsoft.Xna.Framework.Graphics;
Microsoft.Xna.Framework.Input;
Microsoft.Xna.Framework.Media;
namespace XNA0118Proj{
public class Game1 : Microsoft.Xna.Framework.Game{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
// TODO: Add your initialization logic here
}
base.Initialize();
//Declare two variables
Texture2D myTexture;
Vector2 spritePosition = new Vector2(10.0f, 15.0f);
protected override void LoadContent()
{
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
142
CHAPTER 11. XNA0118-THE XNA FRAMEWORK AND THE GAME CLASS
// Create a new SpriteBatch, which can be used
// to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
//Load the image
myTexture =
Content.Load<Texture2D>("gorightarrow");
}//end LoadContent
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if(GamePad.GetState(PlayerIndex.One).Buttons.Back==ButtonState.Pressed)
this.Exit();
// TODO: Add your update logic here
}
base.Update(gameTime);
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.Draw(myTexture, spritePosition, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}//end Draw method
}//End class
}//End namespace
-end-
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
Chapter 12
Xna0120-Moving Your Sprite and using
1
the Debug Class
Revised: Sun May 08 09:19:08 CDT 2016
This page is part of a Book titled XNA Game Studio
2
.
12.1 Table of Contents
• Table of Contents (p. 143)
• Preface (p. 144)
· Viewing tip (p. 144)
* Figures (p. 144)
* Listings (p. 144)
• General background information (p. 145)
· Skeleton code (p. 145)
· Override some or all of ve methods (p. 146)
• Preview (p. 146)
· Moving and bouncing sprite (p. 146)
· The Debug class (p. 147)
• Discussion and sample code (p. 148)
· Making the sprite move (p. 148)
* The game loop (p. 148)
* Distance to move (p. 148)
* How often is the Update method called? (p. 148)
· If the IsFixedTimeStep property is false... (p. 148)
· If the IsFixedTimeStep property is true... (p. 149)
· If the Draw method is not called... (p. 149)
* The overridden Update method (p. 149)
· Xbox 360 code (p. 149)
1 This content is available online at <http://cnx.org/content/m49511/1.2/>.
2 http://cnx.org/contents/GY804-eY
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
143
CHAPTER 12. XNA0120-MOVING YOUR SPRITE AND USING THE
DEBUG CLASS
144
·
·
·
·
·
·
·
·
Test for sprite out of bounds horizontally (p. 149)
Test for a collision of the sprite with an edge (p. 150)
Test for sprite out of bounds vertically (p. 150)
Change the current position of the sprite (p. 150)
Move to the right and down (p. 150)
Reverse the direction of motion (p. 150)
No visible movement at this time (p. 151)
The remainder of the overridden Update method (p. 151)
· Using the Debug class (p. 151)
*
*
*
*
*
•
•
•
•
•
Namespace considerations (p. 151)
Overridden LoadContent method (p. 151)
Overloaded WriteLine methods (p. 152)
Execute once only (p. 152)
The output (p. 152)
Run the program (p. 152)
Run my program (p. 152)
Summary (p. 153)
Miscellaneous (p. 153)
Complete program listing (p. 153)
12.2 Preface
This module is one in a collection of modules designed primarily for teaching GAME 1343 Game and
at Austin Community College in Austin, TX. These modules are intended
to supplement and not to replace the textbook.
An earlier module titled Getting Started 3 provided information on how to get started programming with
Microsoft's XNA Game Studio.
Simulation Programming I
12.2.1 Viewing tip
I recommend that you open another copy of this module in a separate browser window and use the following
links to easily nd and view the Figures and Listings while you are reading about them.
12.2.1.1 Figures
• Figure 1 (p. 146) . Moving and bouncing sprite.
• Figure 2 (p. 147) . Debug output window.
12.2.1.2 Listings
•
•
•
•
•
•
•
•
•
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
1
2
3
4
5
6
7
8
9
(p.
(p.
(p.
(p.
(p.
(p.
(p.
(p.
(p.
145)
148)
149)
149)
150)
150)
151)
151)
153)
.
.
.
.
.
.
.
.
.
Skeleton code for a new Windows Game project.
Declare variables that specify the incremental distance to move.
Beginning of the overridden Update method.
Test for sprite out of bounds horizontally.
Test for sprite out of bounds vertically.
Change the current position of the sprite.
The remainder of the overridden Update method.
Overridden LoadContent method.
The class named Game1 for the project named XNA0120Proj.
3 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
12.3 General background information
145
The earlier module titled Xna0118-The XNA Framework and the Game Class 4 used a very simple XNA
program to teach many of the details regarding the incorporation of the XNA framework into the objectoriented C# programming language.
12.3.1 Skeleton code
When you create a new Windows Game project using Visual C#, a source code le named Game1.cs
is automatically created and opened in the editor window. The le contains skeleton code for a windows
game based on XNA. Listing 1 (p. 145) shows the skeleton code contained in the le named Game1 (with
most of the comments deleted for brevity).
Listing 1
. Skeleton code for a new Windows Game project.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace WindowsGame2
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
// TODO: Add your initialization logic here
}
base.Initialize();
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
4 http://cnx.org/contents/GY804-eY:iAGEPWAd
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 12. XNA0120-MOVING YOUR SPRITE AND USING THE
DEBUG CLASS
146
}
// TODO: use this.Content to load your game content here
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if(GamePad.GetState(PlayerIndex.One).Buttons.Back==ButtonState.Pressed)
this.Exit();
// TODO: Add your update logic here
}
base.Update(gameTime);
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
}
}
}
base.Draw(gameTime);
12.3.2 Override some or all of ve methods
To create a new game program, you override some or all of the ve methods shown in Listing 1 (p. 145) .
The program that I explained in the earlier module overrode the LoadContent and Draw methods
in such a way as to create a sprite and display it in the upper-left corner of the game window. However,
that program did not override the Update method. The sprite did not move and was not animated.
12.4 Preview
12.4.1 Moving and bouncing sprite
The Update method is used to implement game logic. The Draw method is used to render the current
state of the game on the computer screen. In this module, I will override the Update method to cause
the sprite to move around the game window and to bounce o the edges of the game window.
Figure 1 (p. 146) shows a reduced screen shot of the sprite moving in the game window.
Figure 1
. Moving and bouncing sprite.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
147
12.4.2 The Debug class
On a completely unrelated note, I will introduce you to the Debug class and show you how to use the
WriteLine method of the Debug class to display information while the program is running.
Figure 2 (p. 147) shows a screen shot of the Debug output window in the lower left corner of the
Visual C# IDE. I will explain the values that you see in Figure 2 (p. 147) later in this module.
Figure 2
. Debug output window.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 12. XNA0120-MOVING YOUR SPRITE AND USING THE
DEBUG CLASS
148
12.5 Discussion and sample code
I will explain the code in fragments, and I will only explain those fragments that are dierent from the
code that I explained in the earlier module titled Xna0118-The XNA Framework and the Game Class 5 . A
complete listing of the code for the class named Game1 is provided in Listing 9 (p. 153) near the end of
the module.
12.5.1 Making the sprite move
Sprite motion in an XNA game is accomplished by changing the current position coordinates of the sprite
in the Update method and drawing the sprite in its new position in the Draw method.
12.5.1.1 The game loop
You learned about the XNA game loop in the earlier module .
In order to make the sprite move, we need to override the Update method to cause the sprite's position
coordinates to change during each iteration of the game loop. The code to accomplish this begins in Listing
2 (p. 148) .
Listing 2
. Declare variables that specify the incremental distance to move.
//Specify the distance in pixels that the sprite
// will move during each iteration.
int stepsX = 5;
int stepsY = 3;
12.5.1.2 Distance to move
Listing 2 (p. 148) declares and populates two instance variables that specify the incremental distance that
the sprite will move each time the Update method is called. The horizontal and vertical incremental
distances are 5 pixels and 3 pixels respectively.
Note that these two variables are physically declared between the UnloadContent and Update
methods. Because they are declared inside a class but outside of a method or constructor, they are
instance variables.
12.5.1.3 How often is the Update method called?
In this program, the computer will do its best to cause the Update method to be called once every 16.67
milliseconds or 60 times per second.
The Update and Draw methods are called at dierent rates depending on whether the Game
property named IsFixedTimeStep is true or false.
12.5.1.3.1 If the IsFixedTimeStep property is false...
If the IsFixedTimeStep property is false, the Update
and Draw methods will be called in a
continuous loop. The repetition rate of the loop will depend on how long it takes the code in the loop to
execute. That is not the case in this program because the value of the IsFixedTimeStep property is true,
which is the default value.
5 http://cnx.org/contents/GY804-eY:iAGEPWAd
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
149
12.5.1.3.2 If the IsFixedTimeStep property is true...
If the IsFixedTimeStep property is true, the Update method will be called at the interval specied in
the property named TargetElapsedTime , while the Draw method will only be called if an Update
is not due.
In this program, the value of the
default value.
TargetElapsedTime
property is 0.0166667 seconds, which is the
12.5.1.3.3 If the Draw method is not called...
If the Draw method is not called (meaning that the computer can't keep up with the demands of the
Update method), the property named IsRunningSlowly will be set to true. This property can be
tested by the program during development to expose potential timing problems in the game loop.
12.5.1.4 The overridden Update method
The overridden Update method begins in Listing 3 (p. 149) .
Listing 3
. Beginning of the overridden Update method.
protected override void Update(GameTime gameTime) {
// Allows the game to exit
if(GamePad.GetState(PlayerIndex.One).Buttons.Back
== ButtonState.Pressed)
this.Exit();
If you compare Listing 3 (p. 149) with Listing 1 (p. 145) , you will see that the code in Listing 3 (p. 149)
is contained in the skeleton code for the Game1 class that is generated by Visual C#.
12.5.1.4.1 Xbox 360 code
The code in Listing 3 (p. 149) , which references a method of the GamePad 6 class "Allows retrieval of user
interaction with an Xbox 360 Controller." Since we are working on a PC game, we aren't really interested in
the code in the body of the Update method in Listing 3 (p. 149) . The new code in our program begins
in Listing 4 (p. 149) .
12.5.1.4.2 Test for sprite out of bounds horizontally
The code in Listing 4 (p. 149) tests to determine if the sprite has encountered the right or left sides of the
game window. If so, the sign is changed on stepsX to cause the sprite to reverse its horizontal direction
of motion.
Listing 4
. Test for sprite out of bounds horizontally.
if(((spritePosition.X + myTexture.Width) >
Window.ClientBounds.Width) ||
(spritePosition.X < 0)){
stepsX *= -1;//Out of bounds, reverse direction
}//end if
The value of Window.ClientBounds.Width is the width of the game window in pixels.
The value of spritePosition.X species the current horizontal position of the upper-left corner of the
sprite relative to the upper-left corner of the game window.
6 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.input.gamepad.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 12. XNA0120-MOVING YOUR SPRITE AND USING THE
DEBUG CLASS
150
Even though the sprite appears to have an elliptical shape in Figure 1 (p. 146) , it actually has a
rectangular shape with all of the pixels outside the blue elliptical shape being almost transparent.
You learned about this in the earlier module titled Xna0118-The XNA Framework and the Game
Class
The value of myTexture.Width is the width of the sprite. Therefore, the sum of spritePosition.X
and myTexture.Width species the current position of the upper-right corner of the sprite.
12.5.1.4.3 Test for a collision of the sprite with an edge
Listing 4 (p. 149) tests to determine if the upper-right corner of the sprite has encountered the right edge of
the game window or if the upper-left corner of the sprite has encountered the left edge of the game window.
If so, the sign of stepsX is changed to cause the direction of motion of the sprite to be reversed. You will
see how the change in sign causes a reversal in the direction of motion shortly.
12.5.1.4.4 Test for sprite out of bounds vertically
The code in Listing 5 (p. 150) tests to determine if the sprite has encountered the bottom or top edges of
the game window. If so, the sign is changed on stepsY to cause the sprite to reverse its vertical direction
of motion.
Listing 5
. Test for sprite out of bounds vertically.
if(((spritePosition.Y + myTexture.Height) >
Window.ClientBounds.Height) ||
(spritePosition.Y < 0)) {
stepsY *= -1;//Out of bounds, reverse direction
}//end if
The logic in Listing 5 (p. 150) is essentially the same as the logic in Listing 4 (p. 149) with the dierence
being that Listing 5 (p. 150) deals with vertical motion and the top and bottom edges of the game window
instead of horizontal motion and the left and right edges of the game window.
12.5.1.4.5 Change the current position of the sprite
Listing 6 (p. 150) changes the current position of the sprite by adding the incremental distances,
and stepY , to the current horizontal and vertical coordinates of the sprite.
Listing 6
stepX
. Change the current position of the sprite.
spritePosition.X += stepsX;//move horizontal
spritePosition.Y += stepsY;//move vertical
12.5.1.4.6 Move to the right and down
The incremental distances were initialized with positive values in Listing 2 (p. 148) . Adding positive
incremental distance values to the current coordinate values causes the position of the sprite to move to the
right and down.
12.5.1.4.7 Reverse the direction of motion
The logic in Listing 4 (p. 149) and Listing 5 (p. 150) reverses the sign on the incremental distance values
when the sprite encounters a left, right, top, or bottom edge of the game window.
Adding a negative incremental distance value to the current horizontal coordinate of the sprite causes it
to be moved to the left. Adding a negative incremental distance value to the current vertical coordinate of
the sprite causes it to be moved up the screen.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
151
12.5.1.4.8 No visible movement at this time
Simply changing the current horizontal and vertical coordinates of the sprite does not produce a visible
change in position. The visible change in position happens later in the Draw method when the game
window is cleared to a constant color and the sprite is redrawn in the game window in its new position.
The Draw method in this program was not modied relative to the version in the earlier module titled
Xna0118-The XNA Framework and the Game Class 7 . You can view the code in the Draw method in
Listing 9 near the end of the module.
12.5.1.4.9 The remainder of the overridden Update method
The remainder of the overridden Update method is shown in Listing 7 (p.
Listing 7
151) .
. The remainder of the overridden Update method.
//The following statement is always required.
base.Update(gameTime);
}//end Update
The statement in Listing 7 (p. 151) , which calls the Update method of the superclass, is always required
in the overridden version. This code is placed in the overridden Update method in the skeleton code that
is generated by Visual C# (see Listing 1 (p. 145) ) when you create a new Windows Game project.
12.5.2 Using the Debug class
One of the must useful debugging tools for relatively simple programs is the ability to get information
displayed while the program is running. First I will introduce you to the Microsoft Support website titled
How to trace and debug in Visual C# 8 . You will nd a great deal of useful information there.
Next 9 I will introduce you to the Debug 10 class, which "Provides a set of methods and properties that
help debug your code." There are some very useful tools there also.
Finally, I will show you some examples of using methods from the Debug class.
12.5.2.1 Namespace considerations
The Debug class is in the System.Diagnostics
namespace. Therefore, you will either need to include
that namespace in your "using" list or qualify every reference to the Debug class with the name of the
namespace. I chose to include the namespace in my "using" list for this example.
12.5.2.2 Overridden LoadContent method
Listing 8 (p. 151) shows my overridden LoadContent method. Only the code near the end of the method
is dierent from the version of the LoadContent method that I explained in the earlier module titled
Xna0118-The XNA Framework and the Game Class
Listing 8
11
.
. Overridden LoadContent method.
protected override void LoadContent() {
//Create a new SpriteBatch, which can be used to
// draw textures.
7 http://cnx.org/contents/GY804-eY:iAGEPWAd
8 http://support.microsoft.com/kb/815788#5
9 http://msdn.microsoft.com/en-us/library/system.diagnostics.debug(lightweight).aspx
10 http://msdn.microsoft.com/en-us/library/system.diagnostics.debug(lightweight).aspx
11 http://cnx.org/contents/GY804-eY:iAGEPWAd
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 12. XNA0120-MOVING YOUR SPRITE AND USING THE
DEBUG CLASS
152
spriteBatch = new SpriteBatch(GraphicsDevice);
//Load the image
myTexture = Content.Load<Texture2D>(
"gorightarrow");
//Debug code for illustration purposes.
Debug.WriteLine(myTexture.Width);
Debug.WriteLine(myTexture.Height);
Debug.WriteLine(Window.ClientBounds.Width);
Debug.WriteLine(Window.ClientBounds.Height);
Debug.WriteLine(IsFixedTimeStep);
Debug.WriteLine(TargetElapsedTime);
}//end LoadContent
12.5.2.3 Overloaded WriteLine methods
The Debug class provides overloaded versions of the WriteLine
method, which can be used to display
information in the lower-left corner of the Visual C# IDE. These are static methods so they can be called
simply by joining the name of the class to the name of the method as shown in Listing 8 (p. 151) .
12.5.2.4 Execute once only
I elected to put the code to illustrate this capability in the LoadContent method so that it would execute
once and only once. Had I put it in either the Update method or the Draw method, it would have tried
to execute during every iteration of the game loop which would not be satisfactory.
If you need to display information from inside the game loop, you will probably also need to use a counter
with some logic to cause the information to be displayed only every nth iteration.
12.5.2.5 The output
The output from each of the six WriteLine statements in Listing 8 (p. 151) (plus some other stu) is
shown in Figure 2 (p. 147) . As you can see, the sprite referred to by myTexture is 143 pixels wide
and 107 pixels high. The game window is 800 pixels wide and 480 pixels high. As I mentioned earlier, the
value of the IsFixedTimeStep property is True, and the value of the TargetElapsedTime property
is 0.0166667 seconds.
12.6 Run the program
I encourage you to copy the code from Listing 9 (p. 153) . Use that code to create an XNA project. Compile
and run the project. Experiment with the code, making changes, and observing the results of your changes.
Make certain that you can explain why your changes behave as they do.
12.7 Run my program
Click here
12
to download a zip le containing my version of the program. Extract the folder named
XNA0120Proj from the zip le and save it somewhere on your disk. Start Visual C# 2010 Express
and select Open Project... from the File menu. Navigate to the project folder and select the le with
the extension of .sln . This should cause the project to open and be ready to run or debug as described
in the earlier module titled Getting Started
13
.
12 http://cnx.org/content/m49511/latest/XNA0120Proj.zip
13 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
12.8 Summary
153
In this module, you learned how to make it appear that a sprite is moving by changing the current position
coordinates of the sprite in the Update method and drawing the sprite in the new position in the Draw
method. This takes place once during each iteration of the game loop.
You also learned how to use the WriteLine method of the Debug class to display information while
the program is running.
12.9 Miscellaneous
This section contains a variety of miscellaneous information.
Housekeeping material
• Module name: Xna0120-Moving Your Sprite and using the Debug Class
• File: Xna0120.htm
• Published: 02/28/14
Disclaimers: Financial : Although the Connexions site makes it possible for you to download
a PDF le for this module at no charge, and also makes it possible for you to purchase a pre-printed
version of the PDF le, you should be aware that some of the HTML elements in this module may
not translate well into PDF.
I also want you to know that, I receive no nancial compensation from the Connexions website even
if you purchase the PDF version of the module.
In the past, unknown individuals have copied my modules from cnx.org, converted them to Kindle
books, and placed them for sale on Amazon.com showing me as the author. I neither receive
compensation for those sales nor do I know who does receive compensation. If you purchase such
a book, please be aware that it is a copy of a module that is freely available on cnx.org and that it
was made and published without my prior knowledge.
Aliation : I am a professor of Computer Information Technology at Austin Community College
in Austin, TX.
12.10 Complete program listing
A complete listing of the XNA program discussed in this module is provided in Listing 9 (p. 153) .
Listing 9. The class named Game1 for the project named XNA0120Proj.
/*Project XNA0120Proj
* 12/27/09 R.G.Baldwin
*******************************************************/
using
using
using
using
using
using
System;
System.Collections.Generic;
System.Linq;
Microsoft.Xna.Framework;
Microsoft.Xna.Framework.Audio;
Microsoft.Xna.Framework.Content;
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 12. XNA0120-MOVING YOUR SPRITE AND USING THE
DEBUG CLASS
154
using
using
using
using
using
using
using
Microsoft.Xna.Framework.GamerServices;
Microsoft.Xna.Framework.Graphics;
Microsoft.Xna.Framework.Input;
Microsoft.Xna.Framework.Media;
Microsoft.Xna.Framework.Net;
Microsoft.Xna.Framework.Storage;
System.Diagnostics;//to access Debug
namespace XNA0120Proj {
public class Game1 : Microsoft.Xna.Framework.Game {
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
public Game1() {
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}// end constructor
protected override void Initialize() {
//No initialization needed
base.Initialize();
}//end Initialize
//Declare two variables
Texture2D myTexture;
Vector2 spritePosition = new Vector2(10.0f,15.0f);
protected override void LoadContent() {
//Create a new SpriteBatch, which can be used to
// draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
//Load the image
myTexture = Content.Load<Texture2D>(
"gorightarrow");
//Debug code for illustration purposes.
Debug.WriteLine(myTexture.Width);
Debug.WriteLine(myTexture.Height);
Debug.WriteLine(Window.ClientBounds.Width);
Debug.WriteLine(Window.ClientBounds.Height);
Debug.WriteLine(IsFixedTimeStep);
Debug.WriteLine(TargetElapsedTime);
}//end LoadContent
protected override void UnloadContent() {
//No unload code needed.
}//end UnloadContent
//Specify the distance in pixels that the sprite
// will move during each iteration.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
155
int stepsX = 5;
int stepsY = 3;
protected override void Update(GameTime gameTime) {
// Allows the game to exit
if(GamePad.GetState(PlayerIndex.One).Buttons.Back
== ButtonState.Pressed)
this.Exit();
//New code begins here.
//Test to determine if the sprite moves out of the
// game window on the right or the left.
if(((spritePosition.X + myTexture.Width) >
Window.ClientBounds.Width) ||
(spritePosition.X < 0)){
stepsX *= -1;//Out of bounds, reverse direction
}//end if
//Test to determine if the sprite moves out of the
// game window on the bottom or the top.
if(((spritePosition.Y + myTexture.Height) >
Window.ClientBounds.Height) ||
(spritePosition.Y < 0)) {
stepsY *= -1;//Out of bounds, reverse direction
}//end if
spritePosition.X += stepsX;//move horizontal
spritePosition.Y += stepsY;//move vertical
//The following statement is always required.
base.Update(gameTime);
}//end Update
protected override void Draw(GameTime gameTime) {
GraphicsDevice.Clear(Color.CornflowerBlue);
// Draw the sprite.
spriteBatch.Begin();
spriteBatch.Draw(
myTexture,spritePosition,Color.White);
spriteBatch.End();
//This statement is always required.
base.Draw(gameTime);
}//end Draw method
}//End class
}//End namespace
-end-
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
156
CHAPTER 12. XNA0120-MOVING YOUR SPRITE AND USING THE
DEBUG CLASS
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
Chapter 13
Xna0122-Frame Animation using a
Sprite Sheet
1
Revised: Sun May 08 14:07:44 CDT 2016
This page is part of a Book titled XNA Game Studio
2
.
13.1 Table of Contents
• Table of Contents (p. 157)
• Preface (p. 159)
· Viewing tip (p. 159)
* Figures (p. 159)
* Listings (p. 159)
• General background information (p. 159)
·
·
·
·
A sprite sheet (p. 160)
Hundreds of sprite images (p. 160)
Frame animation (p. 160)
Downloading the sprite sheet (p. 160)
• Preview (p. 161)
• Discussion and sample code (p. 162)
· The class named Game1 (p. 162)
· The modied constructor for the Game1 class (p. 163)
· The overridden LoadContent method (p. 163)
* Load the image (p. 164)
* Initialization of variables (p. 164)
· spriteCol and spriteRow (p. 164)
· frameWidth and frameHeight (p. 164)
· msPerFrame (p. 164)
· spriteEect (p. 165)
· winWidth (p. 165)
1 This content is available online at <http://cnx.org/content/m49518/1.2/>.
2 http://cnx.org/contents/GY804-eY
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
157
158
CHAPTER 13. XNA0122-FRAME ANIMATION USING A SPRITE SHEET
· The overridden Update method (p. 165)
* The update method is fairly complex (p. 165)
* The animation frame rate (p. 166)
· Many drawings will be repeated (p. 166)
· The GameTime parameter (p. 166)
· The ElapsedGameTime property (p. 166)
· The TimeSpan structure (p. 166)
· Accumulate and compare elapsed time (p. 166)
* Compute the location of the sprite to draw (p. 167)
* The overall animation cycle (p. 167)
· Two animation cycles from the bottom row of sprite images (p. 167)
· Pause and animate in the same location (p. 167)
* Some complex logic (p. 167)
· Adjust column and row counters (p. 167)
· Increment the column counter and compare (p. 167)
· Execute the pause sequence if it is time for it (p. 168)
· The conditional clause (p. 168)
· Set the row counter to 1 (p. 168)
· Increment the pauseSequenceCnt (p. 169)
· Adjust sprite position and frame rate (p. 169)
* More complex logic (p. 169)
· Scaling (p. 169)
· The slide variable (p. 169)
· The sign of the variable named slide (p. 170)
· Move the sprite image back and forth across the game window (p. 170)
· Test for a collision with an edge (p. 170)
· The spriteEect variable (p. 170)
· The overridden Draw method (p. 170)
* Begin the drawing process (p. 171)
* Call the SpriteBatch.Draw method (p. 171)
· The rst two parameters (p. 172)
· Three interesting parameters (p. 172)
· The remaining four parameters (p. 172)
· The rectangle (p. 172)
· The upper left corner of the rectangle (p. 173)
· The width and the height of the rectangle (p. 173)
· The horizontal and vertical scale factors (p. 173)
· A new object (p. 173)
· Four times larger (p. 173)
· Causing the sprite to face in the correct direction (p. 173)
* Call the SpriteBatch.End method (p. 173)
· The visual frame rate (p. 174)
* The fast frame rate (p. 174)
* The slow frame rate (p. 174)
* Avoid icker but animate more slowly (p. 174)
· The end of the program (p. 174)
• Run the program (p. 174)
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
159
•
•
•
•
Run my program (p. 174)
Summary (p. 175)
Miscellaneous (p. 175)
Complete program listing (p. 175)
13.2 Preface
This module is one in a collection of modules designed primarily for teaching GAME 1343 Game and
at Austin Community College in Austin, TX. These modules are intended
to supplement and not to replace the textbook.
An earlier module titled Getting Started 3 provided information on how to get started programming with
Microsoft's XNA Game Studio.
Simulation Programming I
13.2.1 Viewing tip
I recommend that you open another copy of this module in a separate browser window and use the following
links to easily nd and view the Figures and Listings while you are reading about them.
13.2.1.1 Figures
•
•
•
•
•
Figure
Figure
Figure
Figure
Figure
1
2
3
4
5
(p.
(p.
(p.
(p.
(p.
160)
161)
161)
161)
162)
.
.
.
.
.
Sprite sheet used to animate a dog.
A top row image.
A bottom row image.
A bottom row image ipped horizontally.
A top row image ipped horizontally.
13.2.1.2 Listings
•
•
•
•
•
•
•
•
•
•
•
•
•
•
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
1 (p. 162) . Beginning of the class named Game1.
2 (p. 163) . The constructor for the Game1 class.
3 (p. 163) . The overridden LoadContent method.
4 (p. 165) . Beginning of the Update method.
5 (p. 167) . Compute the location of the sprite to draw.
6 (p. 168) . Adjust column and row counters.
7 (p. 168) . Execute the pause sequence if it is time for it.
8 (p. 169) . Adjust sprite position and frame rate.
9 (p. 170) . Move the sprite image back and forth across the game window.
10 (p. 171) . Beginning of the overridden Draw method.
11 (p. 171) . Begin the drawing process.
12 (p. 171) . Call the SpriteBatch.Draw method.
13 (p. 174) . The end of the program.
14 (p. 175) . Class Game1 from the project named XNA0122Proj.
13.3 General background information
Frame animation typically involves displaying a series of images one at a time in quick succession where each
image is similar to but dierent from the one before it. For example, Figure 1 (p. 160) shows a series of
images of a small dog running, jumping, stopping to answer nature's call, and then scratching the ground as
dogs are prone to do.
3 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
160
CHAPTER 13. XNA0122-FRAME ANIMATION USING A SPRITE SHEET
Figure 1
. Sprite sheet used to animate a dog.
Credit for sprite artwork
I have no recollection of when and where I acquired this sprite sheet. If you are the artist that drew
these sprites, please contact me and identify the original source and I will gladly give you credit for
the artwork.
13.3.1 A sprite sheet
The format that you see in Figure 1 (p. 160) is a small scale version of a format that is commonly known
as a sprite sheet. If you Google "animation sprite sheet", you will nd hundreds and possibly thousands of
examples of animation sprite sheets on the web.
13.3.2 Hundreds of sprite images
Many of the sprite sheets that you will nd on the web will contain hundreds of individual images usually
arranged is several groups. One group may have several images that can be animated to create the illusion
of a character running. Another group may have several images that can be animated to create the illusion
of the character engaging in a martial arts battle. Other groups can be animated to create the illusion of
other activities.
There are two groups of sprite images in Figure 1 (p. 160) . The images in the top row can be animated
to show the dog running, jumping, playing and generally having fun.
The images in the bottom row can be animated to show the dog answering nature's call.
13.3.3 Frame animation
By displaying the individual images from a group sequentially with an appropriate time delay between images,
you can create the illusion that the character is engaging in some particular activity. When displayed in
this manner, each image is often referred to as a frame. The overall process is often referred to as frame
animation.
13.3.4 Downloading the sprite sheet
If you would like to replicate my program using the same sprite sheet, you should be able to right-click on
Figure 1 (p. 160) and save the image on your disk. Be sure to save it as an image le of type JPEG.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
161
13.4 Preview
I will explain a program in this module that causes the dog to run back and forth across a small game
window always facing in the correct direction as shown in Figure 2 (p. 161) . Figure 2 (p. 161) through
Figure 5 (p. 162) show four random screen shots taken while the program was running.
Figure 2
. A top row image.
Figure 3
. A bottom row image.
Figure 4
. A bottom row image ipped horizontally.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
162
CHAPTER 13. XNA0122-FRAME ANIMATION USING A SPRITE SHEET
Figure 5
. A top row image ipped horizontally.
You should be able to correlate the images of the dog shown in Figure 2 (p. 161) through Figure 5 (p. 162)
with the individual images shown in Figure 1 (p. 160) . Note, however, that the images in Figure 4 (p.
161) and Figure 5 (p. 162) were ipped horizontally so that the dog would be facing the correct way when
moving from left to right across the game window.
13.5 Discussion and sample code
I will explain the code in this program in fragments, and I will only discuss the code that I modied relative
to the skeleton code produced by Visual C# when I created the project. A complete listing of the le named
Game1.cs is shown in Listing 14 (p. 175) near the end of the module.
13.5.1 The class named Game1
The class named Game1 begins in Listing 1 (p. 162) .
Listing 1
. Beginning of the class named Game1.
namespace XNA0122Proj {
public class Game1 : Microsoft.Xna.Framework.Game {
//Declare and populate instance variables
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D myTexture;
Vector2 spritePosition = new Vector2(0.0f,0.0f);
int slide = 8;//Used to move sprite across screen.
int scale = 4;//Size scale factor.
int fast = 175;//Used for fast frame rate.
int slow = 525;//Used for slow frame rate.
int msPerFrame = 0;//Gets set for fast or slow.
int msElapsed;//Time since last new frame.
int spriteCol;//Sprite column counter.
int spriteColLim = 5;//Number of sprite columns.
int spriteRow;//Sprite row counter.
int spriteRowLim = 2;//Number of sprite rows.
int frameWidth;//Width of an individual image
int frameHeight;//Height of an individual image
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
163
int xStart;//Corner of frame rectangle
int yStart;//Corner of frame rectangle
SpriteEffects noEffect = SpriteEffects.None;
SpriteEffects flipEffect =
SpriteEffects.FlipHorizontally;
SpriteEffects spriteEffect;//noEffect or flipEffect
int winWidth;//Width of the game window.
int funSequenceCnt = 0;
int pauseSequenceCnt = 0;
Listing 1 (p. 162) declares a large number of instance variables that are used by code throughout the
program. I will explain the purpose of the instance variables when we encounter them in the program code
later.
13.5.2 The modied constructor for the Game1 class
The constructor for the
Listing 2
Game1
class is shown in Listing 2 (p. 163) .
. The constructor for the Game1 class.
public Game1() {//constructor
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
//Set the size of the game window.
graphics.PreferredBackBufferWidth = 450;
graphics.PreferredBackBufferHeight = 100;
}// end constructor
I added the last two statements to the standard constructor that is generated by Visual C# when you create
a new project.
Although it isn't very clear in the documentation 4 , the values for PreferredBackBuerWidth and
PreferredBackBuerHeight set the size of the game window provided that they are consistent with the
screen resolution. These two statements caused the game window to be small as shown in Figure 2 (p. 161)
instead of the default size.
13.5.3 The overridden LoadContent method
The overridden
Listing 3
LoadContent
method is shown in Listing 3 (p. 163) .
. The overridden LoadContent method.
protected override void LoadContent() {
//Create a new SpriteBatch object, which can be
// used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
//Load the image
myTexture = Content.Load<Texture2D>("dogcropped");
4 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphicsdevicemanager_members.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
164
CHAPTER 13. XNA0122-FRAME ANIMATION USING A SPRITE SHEET
//Initialize instance variables
spriteCol = 0;
spriteRow = 0;
frameWidth = myTexture.Width / spriteColLim;
frameHeight = myTexture.Height / spriteRowLim;
msPerFrame = fast;
spriteEffect = flipEffect;
winWidth = Window.ClientBounds.Width;
}//end LoadContent
13.5.3.1 Load the image
The code to load the image, which is a sprite sheet, is the same as code that I have explained in earlier
modules.
13.5.3.2 Initialization of variables
Some of the instance variables in Listing 1 (p. 162) were initialized when they were declared. Others couldn't
be initialized when they were declared for a variety of reasons.
Some could have been initialized in the constructor and others couldn't because the required information
wasn't yet available.
I elected to initialize variables in the LoadContent method. By the time the LoadContent method
executes, all of the information necessary to initialize the variables is available.
13.5.3.2.1 spriteCol and spriteRow
The variables named spriteCol and spriteRow
will be used as counters to keep track of and to specify
the column and row for a particular sprite image as shown in Figure 1 (p. 160) . The columns are numbered
from 0 through 4 (ve columns) and the rows are numbered from 0 through 1 (two rows).
13.5.3.2.2 frameWidth and frameHeight
These two variables specify the width and the height of an individual sprite image (see Figure 1 (p. 160) ).
The width and the height of the individual sprite images are computed by dividing the total width of the
image loaded in Listing 3 (p. 163) by the number of sprite images in a row and by dividing the total height
of the image loaded in Listing 3 (p. 163) by the number of sprite images in a column.
13.5.3.2.3 msPerFrame
Listing 1 (p. 162) declares two variables named fast and slow and initializes their values to 175
milliseconds and 525 milliseconds respectively. These two values are used to switch the animation frame rate
between a fast rate and a slow rate by assigning one or the other value to the variable named msPerFrame
. The fast value is assigned to msPerFrame in Listing 3 (p. 163) to specify a fast frame rate when the
animation begins.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
165
13.5.3.2.4 spriteEect
The SpriteEects enumeration lists the following eects that can be applied to a sprite when it is drawn:
• FlipHorizontally
• FlipVertically
• None
The images in the raw sprite sheet shown in Figure 1 (p. 160) are all facing to the left. The FlipHorizontally enumeration value will be used to cause the images to face to the right when the dog is moving from
left to right across the game window. The None enumeration value will be used to cause the images to
face to the left (the default) when the dog is moving from right to left across the game window.
This is accomplished with the variables named spriteEect , ipEect , and noEect . The
value of spriteEect is initialized to ipEect in Listing 3 (p. 163) because the dog starts o moving
from left to right.
13.5.3.2.5 winWidth
winWidth is set to the width of the game window. The value Window.ClientBounds.Width could have been used everywhere that winWidth is used but the length of
The variable named
the expression created some formatting problems when attempting to format the source code for this narrow
publication format.
13.5.4 The overridden Update method
You will recall that after initialization, the XNA game loop switches back and forth between calling the
Update method and the Draw method. The Update method is overridden to implement the game
logic and the Draw method is overridden to render the current state of the game on the computer screen.
There are some subtle timing issues that I explained in earlier modules and I won't get into them
here.
13.5.4.1 The update method is fairly complex
The Update method in the earlier modules has been fairly simple.
The Update method in this module, which begins in Listing 4 (p.
logic.
Listing 4
That is not the case in this module.
165) , contains some fairly complex
. Beginning of the Update method.
protected override void Update(GameTime gameTime) {
// Allows the game to exit
if(GamePad.GetState(PlayerIndex.One).Buttons.Back
== ButtonState.Pressed)
this.Exit();
//-----------------------------------------------//
//New code begins here.
msElapsed += gameTime.ElapsedGameTime.Milliseconds;
if(msElapsed > msPerFrame){
//Reset the elapsed time and draw the new frame.
msElapsed = 0;
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
166
CHAPTER 13. XNA0122-FRAME ANIMATION USING A SPRITE SHEET
13.5.4.2 The animation frame rate
The code at the beginning of Listing 4 (p. 165) is the standard code that is generated by Visual C# when
you create a new Windows Game project.
The new code in Listing 4 (p. 165) deals with the animation frame rate . The animation frame rate
needs to be much slower than the default repetition rate of the game loop, which is 60 iterations per second.
Otherwise the dog would run around so fast that it wouldn't look natural.
13.5.4.2.1 Many drawings will be repeated
Therefore, we won't change the drawing parameters during every iteration of the game loop. Instead, we
will cause the sprite to be drawn in the game window sixty times per second, but many of those drawings
will look exactly like the previous drawing.
We will accomplish this by changing the drawing parameters only once every msPerFrame milliseconds.
(Recall that msPerFrame can have either of two values: fast and slow .)
13.5.4.2.2 The GameTime parameter
Each time the Update method is called, an incoming parameter contains information in an object of type
GameTime that allows us to determine the number of milliseconds that have elapsed since the last time
the Update method was called.
The documentation for the GameTime
5
class has this to say:
"Snapshot of the game timing state expressed in values that can be used by variable-step (real
time) or xed-step (game time) games."
13.5.4.2.3 The ElapsedGameTime property
The GameTime object has several properties, one of which is named ElapsedGameTime 6 .
which is a structure of type TimeSpan provides:
This property,
"The amount of elapsed game time since the last update."
13.5.4.2.4 The TimeSpan structure
A TimeSpan 7 structure has a large number of properties including one named Milliseconds 8 . This property:
"Gets the milliseconds component of the time interval represented by the current TimeSpan structure."
Therefore, the rst line of new code in Listing 4 (p. 165) returns the elapsed time in milliseconds since the
last call to the Update method.
13.5.4.2.5 Accumulate and compare elapsed time
Listing 4 (p. 165) adds the value in milliseconds to an accumulator variable named msElapsed each time
the Update method is called.
Listing 4 (p. 165) also compares the accumulated value with the desired animation interval stored in
msElapsed . If the accumulated value exceeds the desired animation interval, the accumulated value is
set to zero and the body of the if statement that begins in Listing 4 (p. 165) is executed to modify the
drawing parameters.
5 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.gametime.aspx
6 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.gametime.elapsedgametime.aspx
7 http://msdn.microsoft.com/en-us/library/system.timespan.aspx
8 http://msdn.microsoft.com/en-us/library/system.timespan.milliseconds.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
167
13.5.4.3 Compute the location of the sprite to draw
The code in Listing 5 (p. 167) computes the location in pixel coordinates of the sprite image that needs to
be drawn the next time the Draw method is called.
Listing 5
. Compute the location of the sprite to draw.
xStart = spriteCol * frameWidth;
yStart = spriteRow * frameHeight;
That sprite image is identied by the intersection of the spriteCol column and the spriteRow row in
Figure 1 (p. 160) .
The column and row values are used in conjunction with the width and height of the sprite images to
compute the coordinates of the upper-left corner of the sprite image to be drawn. These values are stored in
xStart and yStart , which will be used in the Draw method to select the correct image from I mage
1 (p. 160) and to draw that image.
13.5.4.4 The overall animation cycle
The program plays ve animation cycles of the ve sprite images in the top row of Figure 1 (p. 160) . These
ve cycles are played with the fast animation frame rate discussed earlier. This is controlled by a counter
variable named funSequenceCnt . (This name was chosen because these images portray the dog running
and jumping and having fun.)
13.5.4.4.1 Two animation cycles from the bottom row of sprite images
Then the program plays two animation cycles of the ve sprite images in the bottom row of Figure 1 (p.
160) . These ve cycles are played with the slow animation frame rate discussed earlier.
13.5.4.4.2 Pause and animate in the same location
During this period, the dog doesn't move across the game window but rather the animation cycles are
played with the dog remaining in the same location. This is controlled by a counter variable named
pauseSequenceCnt . (This name was chosen because the dog pauses and animates in the same location.)
After that, the overall cycle repeats.
13.5.4.5 Some complex logic
This is where the logic becomes a little complex and it remains to be seen how well I can explain it. However,
my students are supposed to have the prerequisite knowledge that prepares them to dissect and understand
complex logic directly from source code.
13.5.4.5.1 Adjust column and row counters
The drawing parameters have already been established to identity the sprite image that will be drawn the
next time the Draw method is called. The code that follows is preparing for the sprite selection that will
take place after that one.
13.5.4.5.2 Increment the column counter and compare
Listing 6 (p. 168) increments the column counter and compares it with the number of columns in the sprite
sheet in Figure 1 (p. 160) . If they match, Listing 6 (p. 168) resets the column counter to 0 and increments
the funSequenceCnt to indicate that another one of the ve cycles through the ve images in the top
row of Figure 1 (p. 160) has been completed.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
168
CHAPTER 13. XNA0122-FRAME ANIMATION USING A SPRITE SHEET
Listing 6
. Adjust column and row counters.
if(++spriteCol == spriteColLim){
//Column limit has been hit, reset the
// column counter and increment the
// funSequenceCnt.
spriteCol = 0;
funSequenceCnt++;
13.5.4.5.3 Execute the pause sequence if it is time for it
The last statement in Listing 6 (p. 168) increments the funSequenceCnt
. The rst statement in Listing
7 (p. 168) tests to see if it has a value of 5. If so, all ve cycles of the fun sequence have been executed and
the code in the body of the if statement that begins at the top of Listing 7 (p. 168) will be executed. The
purpose of this code is to execute two cycles of the pause sequence.
Listing 7
. Execute the pause sequence if it is time for it.
if((funSequenceCnt == 5) || (spriteRow == 1)){
spriteRow = 1;//advance to second row
//Increment the pause sequence counter.
pauseSequenceCnt++;
//After two cycles in the pause mode, reset
// variables and start the overall cycle
// again.
if(pauseSequenceCnt == 3){
spriteRow = 0;
funSequenceCnt = 0;
pauseSequenceCnt = 0;
}//end if on pauseSequenceCnt
}//end if on funSequenceCnt
}//end if on spriteColLim in
13.5.4.5.4 The conditional clause
The conditional clause in the if statement at the top of Listing 7 (p.
168) also tests to see if the row counter
is pointing to row 1. If so, this means that the pause cycle has already begun and should be continued.
Therefore, the body of that if statement will be executed. In other words, the body of the if statement
will be executed if the funSequenceCnt is equal to 5 or the spriteRow is equal to 1.
13.5.4.5.5 Set the row counter to 1
The rst statement in the body of the if
statement sets the row counter to 1. This is necessary because
control may have just entered the body of the if statement for the rst time following completion of ve
cycles using the sprites in row 0 (the top row in Figure 1 (p. 160) ).
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
169
13.5.4.5.6 Increment the pauseSequenceCnt
Then Listing 7 (p. 168) increments the pauseSequenceCnt
and compares it with the literal value 3. If
there is a match, two cycles of animation using the sprite images in the bottom row of Figure 1 (p. 160) have
been completed and it's time to return to the ve cycles using the sprite images in the top row of Figure 1
(p. 160) .
To accomplish this, the row counter, the funSequenceCnt , and the pauseSequenceCnt are all set
to 0. This will cause ve cycles using the sprite images in the top row of Figure 1 (p. 160) to be executed
before control will once again enter the code in Listing 7 (p. 168) .
13.5.4.5.7 Adjust sprite position and frame rate
The code that we have examined so far mainly deals with selecting the sprite image to draw each time the
Draw method is called. We haven't dealt with the location where the sprite will be drawn in the game
window, the orientation of the sprite when it is drawn, and the frame animation rate of fast versus slow
.
Listing 8 (p. 169) adjusts these drawing parameters.
Listing 8
. Adjust sprite position and frame rate.
if((spriteRow == 0) ||
((spriteRow == 1) && (spriteCol == 0))) {
msPerFrame = fast;
spritePosition.X += frameWidth * scale / slide;
}
else if ((spriteRow == 1) ||
((spriteRow == 0) && (spriteCol == 0))){
//Stop and display images.
msPerFrame = slow;
}//end if-else
13.5.4.6 More complex logic
The logic in Listing 8 (p. 169) is fairly complex due mainly to the need to adjust the frame rate from fast to
slow or from slow to fast when transitioning between the two rows of sprites in Listing 1 (p. 162) . Rather
than to try to explain this logic, I am going to leave it as an exercise for the student to analyze the code and
to determine where the frame rate transitions occur.
13.5.4.6.1 Scaling
Although I haven't mentioned it before, the SpriteBatch.Draw method (not the Game.Draw method)
that will be used to draw the sprites in the game window has a scaling parameter that can be used to scale
the images before drawing them.
Listing 1 (p. 162) declares a variable named scale and sets its value to 4. This will be used as a scale
factor when the sprite images are drawn.
13.5.4.6.2 The slide variable
Listing 1 (p. 162) also declares a variable named slide and sets its value to 8. This variable is used to
control how far the sprite moves each time it is drawn.
That distance, along with the new sprite position, is computed in Listing 8 (p. 169) as the product of
the width of the sprite image and the scale factor divided by the value of slide . This is a distance that I
determined experimentally to cause the animation to look like I wanted it to look.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
170
CHAPTER 13. XNA0122-FRAME ANIMATION USING A SPRITE SHEET
13.5.4.6.3 The sign of the variable named slide
It is worth noting that the sign of the variable named slide determines whether the incremental distance
is positive or negative.
It is also worth noting that the change in spritePosition.X only occurs when sprites from the top row
in Figure 1 (p. 160) are being drawn. When sprites from the bottom row in Figure 1 (p. 160) are being
drawn, the sprite is animated in place.
13.5.4.6.4 Move the sprite image back and forth across the game window
The code in Listing 9 (p. 170) causes the sprite image to move back and forth across the game window
always facing in the correct direction.
Listing 9
. Move the sprite image back and forth across the game window.
if(spritePosition.X >
winWidth - frameWidth * scale) {
slide *= -1;
spriteEffect = noEffect;
}//end if
if(spritePosition.X < 0){
slide *= -1;
spriteEffect = flipEffect;
}//end if
}//end if
//-----------------------------------------------//
//New code ends here.
base.Update(gameTime);
}//end Update
13.5.4.6.5 Test for a collision with an edge
Listing 9 (p. 170) tests for a collision between the sprite and the right edge or the left edge of the game
window. If the sprite collides with the right edge, the sign on the variable named slide is changed to cause
future incremental distance movements to be negative (from right to left).
If the sprite collides with the left edge, the sign on the variable named slide is changed to cause future
incremental distance movements to be positive (from left to right).
13.5.4.6.6 The spriteEect variable
In addition, when the sprite collides with one edge or the other, the value of the spriteEect variable
is set such that the dog will be facing the correct direction as it moves toward the other edge of the game
window.
That concludes the explanation of the overridden Update method.
13.5.5 The overridden Draw method
The overridden Draw method selects the correct sprite image by extracting a rectangular area from the
sprite sheet and draws the rectangle containing the sprite image at a specied location in the game window.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
171
Note in Figure 1 (p. 160) that the sprite sheet has a white non-transparent background. The game
window is also caused to have a white background so that the white background of the rectangle containing
the sprite image can't be distinguished from the game window background.
The overridden Draw method begins in Listing 10 (p. 171) .
Listing 10
. Beginning of the overridden Draw method.
protected override void Draw(GameTime gameTime) {
GraphicsDevice.Clear(Color.White);//Background
The statement in Listing 10 (p. 171) erases everything in the game window by painting over it with a solid
white background.
13.5.5.1 Begin the drawing process
You learned in an earlier module that the drawing process consists of a minimum of three statements.
The rst statement is a call to the SpriteBatch.Begin method. This is followed by one or more calls
to the SpriteBatch.Draw method. This is followed by a call to the SpriteBatch.End method.
There are four overloaded versions of the SpriteBatch.Begin method. The parameters for the dierent
versions establish drawing parameters that apply to all of the subsequent calls to the SpriteBatch.Draw
method until there is a call to the SpriteBatch.End method.
This program uses the simplest version of the SpriteBatch.Begin method with no parameters as
shown in Listing 11 (p. 171) . This version simply:
"Prepares the graphics device for drawing sprites."
Listing 11
. Begin the drawing process.
spriteBatch.Begin();
13.5.5.2 Call the SpriteBatch.Draw method
This program makes a single call to the SpriteBatch.Draw method followed by a call to the SpriteBatch.End method each time the Game.Draw method is called.
There are several overloaded versions of the SpriteBatch.Draw method and the one shown in Listing
12 (p. 171) is one of the most complex. It was necessary to use this version to cause the sprite to be scaled
by the value of the variable named scale when it is drawn.
Listing 12
. Call the SpriteBatch.Draw method.
spriteBatch.Draw(myTexture,//sprite sheet
spritePosition,//position to draw
//Specify rectangular area of the
// sprite sheet.
new Rectangle(
xStart,//Upper left corner
yStart,// of rectangle.
frameWidth, //Width and height
frameHeight),// of rectangle
Color.White,//Don't tint sprite
0.0f,//Don't rotate sprite
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
172
CHAPTER 13. XNA0122-FRAME ANIMATION USING A SPRITE SHEET
//Origin of sprite. Can offset re
// position above.
new Vector2(0.0f,0.0f),
//X and Y scale size scale factor.
new Vector2(scale,scale),
spriteEffect,//Face correctly
0);//Layer number
spriteBatch.End();
The nine parameters required for this version of the method are identied in the documentation 9 as shown
below. (Note that square brackets were substituted for angle brackets in the following list to avoid problems
in creating the cnxml format required for the OpenStax website.)
•
•
•
•
•
•
•
•
•
Texture2D texture
Vector2 position
Nullable[Rectangle] sourceRectangle
Color color
oat rotation
Vector2 origin
Vector2 scale
SpriteEects eects
oat layerDepth
13.5.5.2.1 The rst two parameters
The rst two parameters that identify the sprite sheet and the position at which to draw the image are the
same as the version that I explained in an earlier module.
13.5.5.2.2 Three interesting parameters
Three of the parameters shown in the above list and in Listing 12 (p. 171) are parameters that are new to
this module and in which I have an interest.
13.5.5.2.3 The remaining four parameters
Four of the parameters shown in the above list and in Listing 12 (p. 171) are new to this module, but I
don't have any interest in them. The program simply passes "harmless" values to them.
13.5.5.2.4 The rectangle
The rst thing that is new to this module in Listing 12 (p. 171) is the passing of a new object of type
Rectangle as the third parameter.
With this version of the Draw method, only the contents of the sprite sheet that fall within that
rectangle are drawn. The size and position of the rectangle are specied with:
• The coordinates of the upper left corner of the rectangle
• The width and the height of the rectangle
9 http://msdn.microsoft.com/en-us/library/bb196420.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
173
13.5.5.2.4.1 The upper left corner of the rectangle
The upper left corner of the rectangle is specied in Listing 12 (p. 171) by the contents of the variables
named xStart and yStart . The values in these two variables were assigned in Listing 5 (p. 167) . They
specify the coordinates of the upper left corner of a small rectangle that contains one of the sprite images
shown in Figure 1 (p. 160) .
13.5.5.2.4.2 The width and the height of the rectangle
The width and the height are specied by the contents of the variables named frameWidth and frameHeight . The values in these two variables were assigned in Listing 3 (p. 163) as the width and height of
the small rectangle that contains one of the sprite images in Figure 1 (p. 160) .
13.5.5.2.5 The horizontal and vertical scale factors
The seventh parameter requires a Vector2D object containing
the scale factors to be applied to the
horizontal and vertical sizes of the sprite before it is drawn. (You learned about the Vector2D class in an
earlier module.)
13.5.5.2.5.1 A new object
Listing 12 (p. 171) instantiates a new object of the Vector2D class that encapsulates the value stored in
the variable named scale for the horizontal and vertical scale factors.
A value of 4 was assigned to the variable named scale when it was initialized in Listing 1 (p. 162) .
13.5.5.2.5.2 Four times larger
Therefore, the sprite images that are drawn in the game window are actually four times larger than the
sprite images in the sprite sheet shown in Figure 1 (p. 160) . ( Note that the images were also scaled
in Figure 1 (p. 160) for display purposes in order to make them more visible. You need to take that into
account if you download the sprite sheet and use it to replicate this program.)
13.5.5.2.6 Causing the sprite to face in the correct direction
The eighth parameter to the Draw method in Listing 12 (p. 171) requires an object of type SpriteEects
. The contents of the variable named spriteEect are passed as this parameter. The contents of this
variable were set to one of the following two values by the code in Listing 9 (p. 170) :
• noEect - SpriteEects.None
• ipEect - SpriteEects.FlipHorizontally
The purpose of this parameter is to cause the sprite to face in the correct direction.
The sprite needs to face to the left when it is moving from right to left. This is the default state of the
sprite sheet shown in Figure 1 (p. 160) so no ip is required.
The sprite needs to face to the right when it is moving from left to right. This requires a horizontal ip
on the sprite images shown in Figure 1 (p. 160) .
13.5.5.3 Call the SpriteBatch.End method
The last statement in Listing 12 (p. 171) calls the
process for the current iteration of the game loop.
SpriteBatch.End
method to terminate the drawing
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
174
CHAPTER 13. XNA0122-FRAME ANIMATION USING A SPRITE SHEET
13.5.6 The visual frame rate
By default, the Update and Draw methods are each called approximately 60 times per second or
approximately once every 16.67 milliseconds. This can be changed by program code but it was not changed
in this program.
13.5.6.1 The fast frame rate
When the program is drawing the sprite images in the top row of Figure 1 (p. 160) , a new sprite image is
selected for drawing only once every 175 milliseconds (see the variable named fast ). Therefore, the same
sprite image is drawn during ten or eleven successive iterations of the game loop.
13.5.6.2 The slow frame rate
When the program is drawing the sprite images in the bottom row of Figure 1 (p. 160) , a new sprite image
is selected for drawing only once every 525 milliseconds (see the variable named slow ). Therefore, each
of the sprites in the bottom row is drawn during about 33 successive iterations of the game loop.
13.5.6.3 Avoid icker but animate more slowly
By drawing the sprite images 60 times per second, the image can be maintained on the computer screen with
no visible icker. By drawing the same image multiple times in succession, the overall visual frame rate can
be slowed down to produce a pleasing animation eect.
13.5.7 The end of the program
Listing 13 (p. 174) shows the required call to the superclass of the
method, the end of the class, and the end of the namespace.
Listing 13
Draw
method, the end of the
Draw
. The end of the program.
//Required standard code.
base.Draw(gameTime);
}//end Draw method
}//End class
}//End namespace
13.6 Run the program
I encourage you to copy the code from Listing 14 (p. 175) . Use that code to create an XNA project.
Compile and run the project. Experiment with the code, making changes, and observing the results of your
changes. Make certain that you can explain why your changes behave as they do. Be sure to take the scale
factor into account as mentioned earlier (p. 173) .
13.7 Run my program
Click here
10
to download a zip le containing my version of the program. Extract the folder named
XNA0122Proj from the zip le and save it somewhere on your disk. Start Visual C# 2010 Express
and select Open Project... from the File menu. Navigate to the project folder and select the le with
the extension of .sln . This should cause the project to open and be ready to run or debug as described
in the earlier module titled Getting Started
11
.
10 http://cnx.org/content/m49518/latest/XNA0122Proj.zip
11 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
13.8 Summary
Among other things, you learned:
•
•
•
•
•
•
How
How
How
How
How
How
to
to
to
to
to
to
create frame animation using a sprite sheet.
ip and scale sprite images when they are drawn.
implement dierent animation frame rates in the same program.
work with dierent groups of sprite images in the same program.
change the size of the game window.
create a Rectangle object.
13.9 Miscellaneous
This section contains a variety of miscellaneous information.
Housekeeping material
• Module name: Xna0122-Frame Animation using a Sprite Sheet
• File: Xna0122.htm
• Published: 02/28/14
Disclaimers: Financial : Although the Connexions site makes it possible for you to download
a PDF le for this module at no charge, and also makes it possible for you to purchase a pre-printed
version of the PDF le, you should be aware that some of the HTML elements in this module may
not translate well into PDF.
I also want you to know that, I receive no nancial compensation from the Connexions website even
if you purchase the PDF version of the module.
In the past, unknown individuals have copied my modules from cnx.org, converted them to Kindle
books, and placed them for sale on Amazon.com showing me as the author. I neither receive
compensation for those sales nor do I know who does receive compensation. If you purchase such
a book, please be aware that it is a copy of a module that is freely available on cnx.org and that it
was made and published without my prior knowledge.
Aliation : I am a professor of Computer Information Technology at Austin Community College
in Austin, TX.
13.10 Complete program listing
A complete listing of the XNA program discussed in this module is provided in Listing 14 (p. 175) .
Listing 14
. Class Game1 from the project named XNA0122Proj.
/*Project XNA0122Proj
R.G.Baldwin, 12/28/09
Animation demonstration. Animates a dog running, jumping,
and stopping to ponder and scratch the ground. Uses two
different frame rates and a 5x2 sprite sheet. Runs back
and forth across the game window always facing in the
right direction.
********************************************************/
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
175
176
CHAPTER 13. XNA0122-FRAME ANIMATION USING A SPRITE SHEET
using
using
using
using
using
using
using
using
using
using
using
using
System;
System.Collections.Generic;
System.Linq;
Microsoft.Xna.Framework;
Microsoft.Xna.Framework.Audio;
Microsoft.Xna.Framework.Content;
Microsoft.Xna.Framework.GamerServices;
Microsoft.Xna.Framework.Graphics;
Microsoft.Xna.Framework.Input;
Microsoft.Xna.Framework.Media;
Microsoft.Xna.Framework.Net;
Microsoft.Xna.Framework.Storage;
namespace XNA0122Proj {
public class Game1 : Microsoft.Xna.Framework.Game {
//Declare and populate instance variables
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D myTexture;
Vector2 spritePosition = new Vector2(0.0f,0.0f);
int slide = 8;//Used to move sprite across screen.
int scale = 4;//Size scale factor.
int fast = 175;//Used for fast frame rate.
int slow = 525;//Used for slow frame rate.
int msPerFrame = 0;//Gets set for fast or slow.
int msElapsed;//Time since last new frame.
int spriteCol;//Sprite column counter.
int spriteColLim = 5;//Number of sprite columns.
int spriteRow;//Sprite row counter.
int spriteRowLim = 2;//Number of sprite rows.
int frameWidth;//Width of an individual image
int frameHeight;//Height of an individual image
int xStart;//Corner of frame rectangle
int yStart;//Corner of frame rectangle
SpriteEffects noEffect = SpriteEffects.None;
SpriteEffects flipEffect =
SpriteEffects.FlipHorizontally;
SpriteEffects spriteEffect;//noEffect or flipEffect
int winWidth;//Width of the game window.
int funSequenceCnt = 0;
int pauseSequenceCnt = 0;
public Game1() {//constructor
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
//Set the size of the game window.
graphics.PreferredBackBufferWidth = 450;
graphics.PreferredBackBufferHeight = 100;
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
177
}// end constructor
protected override void Initialize() {
//No special initialization needed
base.Initialize();
}//end Initialize
protected override void LoadContent() {
//Create a new SpriteBatch object, which can be
// used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
//Load the image
myTexture = Content.Load<Texture2D>("dogcropped");
//Initialize instance variables
spriteCol = 0;
spriteRow = 0;
frameWidth = myTexture.Width / spriteColLim;
frameHeight = myTexture.Height / spriteRowLim;
msPerFrame = fast;
spriteEffect = flipEffect;
winWidth = Window.ClientBounds.Width;
}//end LoadContent
protected override void UnloadContent() {
//No unload code needed.
}//end UnloadContent
protected override void Update(GameTime gameTime) {
// Allows the game to exit
if(GamePad.GetState(PlayerIndex.One).Buttons.Back
== ButtonState.Pressed)
this.Exit();
//-----------------------------------------------//
//New code begins here.
//Compute the elapsed time since the last new
// frame. Draw a new frame only if this time
// exceeds the desired frame interval given by
// msPerFrame
msElapsed += gameTime.ElapsedGameTime.Milliseconds;
if(msElapsed > msPerFrame){
//Reset the elapsed time and draw the new frame.
msElapsed = 0;
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
178
CHAPTER 13. XNA0122-FRAME ANIMATION USING A SPRITE SHEET
//Compute the location of the next sprite to
// draw from the sprite sheet.
xStart = spriteCol * frameWidth;
yStart = spriteRow * frameHeight;
//Adjust sprite column and row counters to
// prepare for the next iteration.
if(++spriteCol == spriteColLim){
//Column limit has been hit, reset the
// column counter
spriteCol = 0;
//Increment the funSequenceCnt. The program
// plays five cycles of the fun sequence with
// the dog running and jumping using sprites
// from row 0 of the sprite sheet. Then it
// plays two cycles of the pause sequence
// using sprites from row 1 of the sprite
// sheet. Then the entire cycle repeats.
funSequenceCnt++;
if((funSequenceCnt == 5) || (spriteRow == 1)){
spriteRow = 1;//advance to second row
//Increment the pause sequence counter.
pauseSequenceCnt++;
//After two cycles in the pause mode, reset
// variables and start the overall cycle
// again.
if(pauseSequenceCnt == 3){
spriteRow = 0;
funSequenceCnt = 0;
pauseSequenceCnt = 0;
}//end if
}//end if
}//end if-else
//Adjust position of sprite in the output window.
//Also adjust the animation frame rate between
// fast and slow depending on which set of five
// sprite images will be drawn.
if((spriteRow == 0) ||
((spriteRow == 1) && (spriteCol == 0))) {
msPerFrame = fast;
spritePosition.X += frameWidth * scale / slide;
}
else if ((spriteRow == 1) ||
((spriteRow == 0) && (spriteCol == 0))){
//Stop and display images.
msPerFrame = slow;
}//end if-else
//Cause the image to move back and forth across
// the game window always facing in the right
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
179
// direction.
if(spritePosition.X >
winWidth - frameWidth * scale) {
slide *= -1;
spriteEffect = noEffect;
}//end if
if(spritePosition.X < 0){
slide *= -1;
spriteEffect = flipEffect;
}//end if
}//end if
//-----------------------------------------------//
//New code ends here.
base.Update(gameTime);
}//end Update
protected override void Draw(GameTime gameTime) {
GraphicsDevice.Clear(Color.White);//Background
//Select the sprite image from a rectangular area
// on the sprite sheet and draw it in the game
// window. Note that this sprite sheet has a white
// non-transparent background.
spriteBatch.Begin();
spriteBatch.Draw(myTexture,//sprite sheet
spritePosition,//position to draw
//Specify rectangular area of the
// sprite sheet.
new Rectangle(
xStart,//Upper left corner
yStart,// of rectangle.
frameWidth, //Width and height
frameHeight),// of rectangle
Color.White,//Don't tint sprite
0.0f,//Don't rotate sprite
//Origin of sprite. Can offset re
// position above.
new Vector2(0.0f,0.0f),
//X and Y scale size scale factor.
new Vector2(scale,scale),
spriteEffect,//Face correctly
0);//Layer number
spriteBatch.End();
//Required standard code.
base.Draw(gameTime);
}//end Draw method
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
180
CHAPTER 13. XNA0122-FRAME ANIMATION USING A SPRITE SHEET
}//End class
}//End namespace
-end-
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
Chapter 14
Xna0124-Using Background Images and
1
Color Key Transparency
Revised: Sun May 08 15:53:42 CDT 2016
This page is part of a Book titled XNA Game Studio
2
.
14.1 Table of Contents
• Table of Contents (p. 181)
• Preface (p. 182)
· Viewing tip (p. 183)
* Figures (p. 183)
* Listings (p. 183)
• General background information (p. 183)
·
·
·
·
·
Layer depth (p. 183)
Color key transparency (p. 183)
Transparent green (p. 184)
Store your image in a lossless image le (p. 184)
Be aware of the default values (p. 185)
• Preview (p. 185)
· The UFO image (p. 185)
· Image of the planet (p. 185)
· The screen output (p. 186)
*
*
*
*
*
Flying a relatively straight path (p. 187)
The background is moving (p. 187)
Preparing to change course (p. 190)
Flying towards the camera (p. 191)
Heading for home (p. 194)
• Discussion and sample code (p. 195)
1 This content is available online at <http://cnx.org/content/m49515/1.2/>.
2 http://cnx.org/contents/GY804-eY
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
181
CHAPTER 14. XNA0124-USING BACKGROUND IMAGES AND COLOR
KEY TRANSPARENCY
182
· The class named Game1 (p. 195)
· The constructor for the class named Game1 (p. 196)
* Setting the size of the game window (p. 196)
* Maintaining the aspect ratio (p. 196)
· The overridden LoadContent method (p. 196)
*
*
*
*
*
The new material (p. 197)
Compute the position of the UFO relative to the game window (p. 197)
Compute the base scale factor for the background image (p. 197)
Not the only scale factor (p. 198)
An optical illusion (p. 198)
· The overridden Update method (p. 198)
* Controlling the animation speed (p. 198)
·
·
·
·
·
Draw new material every 83 milliseconds (p. 198)
Apply dynamic scaling to the background image (p. 198)
Two components in the scale factor (p. 198)
An optical illusion (p. 199)
The dynamic portion of the overall scale factor (p. 199)
* Changing the origin of the background image (p. 199)
· Increasing the scale alone is insucient (p. 199)
· Need to stabilize the location of the planet in the game window (p. 199)
· Examples (p. 200)
* The end of the Update method (p. 200)
· The overridden Game.Draw method (p. 200)
* No need to clear the game window (p. 200)
* Two main sections of code (p. 200)
·
·
·
·
·
Draw the background image showing the planet (p. 201)
Draw the UFO (p. 201)
Center the UFO image on the position (p. 202)
The UFO does not move (p. 202)
The front of the z-order stack (p. 202)
· The end of the program (p. 202)
· Recap on the origin and position parameters (p. 203)
•
•
•
•
•
Run the program (p. 203)
Run my program (p. 203)
Summary (p. 203)
Miscellaneous (p. 203)
Complete program listing (p. 204)
14.2 Preface
This module is one in a collection of modules designed primarily for teaching GAME 1343 Game and
Simulation Programming I at Austin Community College in Austin, TX. These modules are intended
to supplement and not to replace the textbook.
An earlier module titled Getting Started 3 provided information on how to get started programming with
Microsoft's XNA Game Studio.
3 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
183
14.2.1 Viewing tip
I recommend that you open another copy of this module in a separate browser window and use the following
links to easily nd and view the Figures and Listings while you are reading about them.
14.2.1.1 Figures
•
•
•
•
•
•
•
•
•
•
•
•
Figure
Figure
Figure
Figure
Figure
Figure
Figure
Figure
Figure
Figure
Figure
Figure
1 (p. 184) . Property settings for green transparency.
2 (p. 185) . The UFO image.
3 (p. 185) . Reduced version of the alien planet image.
4 (p. 186) . The UFO approaching the planet from some distance away.
5 (p. 187) . Flying over the planet.
6 (p. 188) . Flying over the planet.
7 (p. 189) . Flying over the planet.
8 (p. 190) . Preparing to change course.
9 (p. 191) . Flying towards the camera very close to the surface of the planet.
10 (p. 192) . Still ying towards the camera very close to the surface of the planet.
11 (p. 193) . Almost touching the rings around the planet.
12 (p. 194) . Heading for home.
14.2.1.2 Listings
•
•
•
•
•
•
•
•
•
•
•
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
1 (p. 195) . Beginning of the class named Game1.
2 (p. 196) . The constructor for the class named Game1.
3 (p. 196) . Beginning of the overridden LoadContent method.
4 (p. 197) . Compute the position of the UFO relative to the game window.
5 (p. 197) . Compute the base scale factor for the background image.
6 (p. 198) . Beginning of the overridden Update method.
7 (p. 199) . Apply dynamic scaling to the background image.
8 (p. 200) . Adjust the origin for the background image.
9 (p. 201) . Beginning of the overridden Game.Draw method.
10 (p. 201) . Draw the UFO.
11 (p. 204) . The class named Game1 for the project named XNA0124Proj.
14.3 General background information
14.3.1 Layer depth
When you draw two or more sprites using the SpriteBatch.Draw method, you can specify the z-order
as the last ( layerDepth 4 ) parameter to the Draw method. By default, a non-transparent sprite that is
drawn with a layerDepth value of 0.0 will hide sprites drawn with greater layerDepth values. The
greater the layerDepth value, the further back will be the sprite in the z-order, up to a maximum value
of 1.0, which represents the extreme back of the z-order.
14.3.2 Color key transparency
During the design phase of project development, you can cause the program to interpret one specic color
as being transparent by setting the Color Key Color property to that color and setting the Color Key
Enabled property to true as shown in Figure 1 (p. 184) . Although I have never tried to do it, you can
also apparently accomplish this at runtime by setting properties of the TextureProcessor 5 class.
4 http://msdn.microsoft.com/en-us/library/bb196412.aspx
5 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.content.pipeline.processors.textureprocessor.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 14. XNA0124-USING BACKGROUND IMAGES AND COLOR
KEY TRANSPARENCY
184
Figure 1
. Property settings for green transparency.
14.3.3 Transparent green
Figure 1 (p. 184) shows the property settings required to cause every pixel having a color of pure green
(0,255,0) to be replaced with transparent black, regardless of the actual alpha value of the green pixel.
14.3.4 Store your image in a lossless image le
If you use this capability, you must be careful not to store your image in a le that uses lossy compression,
such as a JPEG le. If you do, the pure color that went into the le is not likely to be pure when you later
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
185
extract the image from the le. Instead, you should store your image in a lossless le such as a BMP le or
a PNG le.
14.3.5 Be aware of the default values
Even if you don't plan to use this capability, you need to be aware of it. It seems that any time you add an
existing image le to the content folder, the Color Key Enabled property will be true by default and
the Color Key Color property value will be magenta (255,0,255). If you fail to disable the Color Key
Enabled property, all of your pure magenta pixels will be replaced by transparent black pixels.
In the early days of computer graphics, magenta was the defacto standard transparency color. It
was referred to as "magic pink."
14.4 Preview
In this module, I will present and explain an animated sequence in which a UFO ies over an alien planet.
You will view the action as if the camera is trained on the UFO while maintaining a constant position relative
to the UFO.
As the program runs, the planet gets larger and larger creating the illusion that the UFO is getting closer
and closer to the surface of the planet. Finally, the program resets and the sequence repeats.
14.4.1 The UFO image
The UFO image is shown in Figure 2 (p. 185) . Note the green background. All of the green pixels in the
UFO image will be replaced by transparent black pixels when the program runs. I took advantage of the
Color Key transparency feature by setting the properties for the UFO image as shown in Figure 1 (p.
184) .
Figure 2
. The UFO image.
14.4.2 Image of the planet
Figure 3 (p. 185) shows a reduced view of the image showing the planet that I used as a background image.
Figure 3
. Reduced version of the alien planet image.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 14. XNA0124-USING BACKGROUND IMAGES AND COLOR
KEY TRANSPARENCY
186
The version that I used in the program was 640 pixels wide and 480 pixels high. However, that is too large
to publish comfortably in this narrow publication format, so I reduced the image in Figure 3 (p. 185) to
450x338 for publication purposes only. The source code that you will see later is based on an image size of
640x480.
14.4.3 The screen output
Figure 4 (p. 186) through Figure 12 (p. 194) show nine screen shots taken while the animation was running.
In Figure 4 (p. 186) , the UFO is approaching the planet from some distance away. Note that the
green portion of the UFO image has become transparent. Also note the size of the planet and its moon for
comparison with subsequent screen shots.
Figure 4
. The UFO approaching the planet from some distance away.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
187
14.4.3.1 Flying a relatively straight path
In Figure 5 (p. 187) through Figure 7 (p. 189) , the UFO continues along a relatively straight path ying
over the planet and getting closer to the surface of the planet all the time. Note how the planet increases
in size from one image to the next, giving the illusion that the UFO is getting closer to the surface of the
planet.
14.4.3.2 The background is moving
Note also that the position of the UFO in the game window is not changing. Instead an illusion of motion
is created by causing the background to change. This is a technique that was used for many years in the
movies to create the illusion that actors were in a car driving along the highway when in fact, the car was
standing still inside a studio. The image of the stationary car was superimposed on a moving background
image.
Figure 5
. Flying over the planet.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 14. XNA0124-USING BACKGROUND IMAGES AND COLOR
KEY TRANSPARENCY
188
Figure 6
. Flying over the planet.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
189
Figure 7
. Flying over the planet.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 14. XNA0124-USING BACKGROUND IMAGES AND COLOR
KEY TRANSPARENCY
190
14.4.3.3 Preparing to change course
In Figure 8 (p. 190) , the UFO has gone about as far as it is going to go along the original course. It
is preparing to change course and come back towards the camera getting ever closer to the surface of the
planet.
Figure 8
. Preparing to change course.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
191
14.4.3.4 Flying towards the camera
In Figure 9 (p. 191) and Figure 10 (p. 192) , the UFO has changed course. It is now ying towards the
camera very close to the surface of the planet. You can even see some surface features on the planet (due to
the distortion that is produced when a bitmap image is enlarged).
Figure 9
. Flying towards the camera very close to the surface of the planet.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 14. XNA0124-USING BACKGROUND IMAGES AND COLOR
KEY TRANSPARENCY
192
Figure 10
. Still ying towards the camera very close to the surface of the planet.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
193
In Figure 11 (p. 193) , the UFO is almost touching the rings around the planet.
Figure 11
. Almost touching the rings around the planet.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 14. XNA0124-USING BACKGROUND IMAGES AND COLOR
KEY TRANSPARENCY
194
14.4.3.5 Heading for home
In Figure 12 (p. 194) , the UFO leaves the planet and heads for home. Shortly after this, the program will
reset and repeat the animation sequence.
Figure 12
. Heading for home.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
195
14.5 Discussion and sample code
As usual, I will explain the code in this program in fragments. A complete listing of the class named
is provided in Listing 11 (p. 204) near the end of the module.
14.5.1 The class named Game1
The class named
Listing 1
Game1
begins in Listing 1 (p. 195) .
. Beginning of the class named Game1.
namespace XNA0124Proj {
public class Game1 : Microsoft.Xna.Framework.Game {
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
private Viewport viewport;
private Vector2 ufoPosition;
private float backgroundScale;
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
Game1
CHAPTER 14. XNA0124-USING BACKGROUND IMAGES AND COLOR
KEY TRANSPARENCY
196
private float backgroundBaseScale;
private float dynamicScale = 0.0f;
int msElapsed;//Time since last new frame.
int msPerFrame = 83;//30 updates per second
Texture2D spaceTexture;//background image
Texture2D ufoTexture;//ufo image
Vector2 spaceOrigin;//origin for drawing background
Listing 1 (p. 195) contains the declarations of several instance variables that will be used later in the
program. I will explain their purpose when they are used later.
14.5.2 The constructor for the class named Game1
The constructor for the class is shown in Listing 2 (p. 196) .
Listing 2
. The constructor for the class named Game1.
public Game1() {//constructor
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
//Set the size of the game window, causing the
// aspect ratio of the game window to match the
// aspect ratio of the background image, which is
// 640 wide by 480 high.
graphics.PreferredBackBufferWidth = 450;
graphics.PreferredBackBufferHeight =
(int)(450.0*480/640);
}//end constructor
14.5.2.1 Setting the size of the game window
The last two statements in Listing 2 (p. 196) set the size of the game window. You have seen code like this
before in an earlier module. The only thing new here is the expression used to compute the value for the
height of the game window.
14.5.2.2 Maintaining the aspect ratio
The background image shown in Figure 3 (p. 185) is 640 pixels wide by 480 pixels high. When it is initially
drawn, it will be scaled to a width of 450 pixels keeping the same width to height ratio (the aspect ratio).
The intent is that it will initially just t inside the game window.
The expression used to compute the new height of the game window in Listing 2 (p. 196) causes the
aspect ratio of the game window to match the aspect ratio of the background image.
14.5.3 The overridden LoadContent method
The overridden
Listing 3
LoadContent
method begins in Listing 3 (p. 196) .
. Beginning of the overridden LoadContent method.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
197
protected override void LoadContent() {
// Create a new SpriteBatch, which can be used to
// draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
//Load the two images.
spaceTexture = Content.Load<Texture2D>("space");
ufoTexture = Content.Load<Texture2D>("ufo");
//Get a reference to the viewport.
viewport = graphics.GraphicsDevice.Viewport;
14.5.3.1 The new material
The only thing that is new in Listing 3 (p. 196) is the last statement.
Viewport is a property of the GraphicsDevice 6 class and is a Structure 7 named Viewport .
The description in the documentation 8 doesn't make a lot of sense to me. I believe it is safe to say,
however, that in this XNA program, the viewport is synonymous with the game window. However, that is
not always the case. A game window can contain more than one viewport. See How To: Use Viewports for
Split Screen Gaming 9 for an example.
14.5.3.2 Compute the position of the UFO relative to the game window
The code in Listing 4 (p. 197) computes a position vector which, when applied during the drawing of the
UFO, will cause the UFO to occupy a position approximately like that shown in Figure 4 (p. 186) .
Listing 4
. Compute the position of the UFO relative to the game window.
ufoPosition.X = viewport.Width / 2;
ufoPosition.Y = viewport.Height - 70;
I say approximately because one additional adjustment to the position of the UFO will be made later using
the origin parameter of the SpriteBatch.Draw method. If that adjustment were not made, the upper
left corner of the rectangle that contains the UFO would be placed at the position computed in Listing 4 (p.
197) . The origin property will be used to center the UFO on the position that is computed in Listing 4 (p.
197) .
14.5.3.3 Compute the base scale factor for the background image
Listing 5 (p. 197) computes a scale factor which, when applied to the background image during the drawing
process, will cause the 640x480 background image to just t the game window. Recall that the size of the
game window was set in the constructor of Listing 2 (p. 196) taking the aspect ratio of the background
image into account.
Listing 5
. Compute the base scale factor for the background image.
backgroundBaseScale = (float)(450.0 / 640.0);
}//end LoadContent
6 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.graphicsdevice_properties.aspx
7 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.viewport.aspx
8 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.graphicsdevice.viewport.aspx
9 http://msdn.microsoft.com/en-us/library/bb313965.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 14. XNA0124-USING BACKGROUND IMAGES AND COLOR
KEY TRANSPARENCY
198
14.5.3.4 Not the only scale factor
The scale factor computed in Listing 5 (p. 197) will be applied to the background image when the program
rst starts running. However, during most of the running of the program, a dierent scale factor will be
applied to the background image and the scale factor computed in Listing 5 (p. 197) will be only one
component of that overall scale factor.
14.5.3.5 An optical illusion
The application of a scale factor to the background image that changes with time is what causes the planet
to grow in size giving the illusion that the UFO is approaching the surface of the planet.
Listing 5 (p. 197) signals the end of the LoadContent method.
14.5.4 The overridden Update method
The overridden
Listing 6
Update
method begins in Listing 6 (p. 198) .
. Beginning of the overridden Update method.
protected override void Update(GameTime gameTime) {
//Compute the elapsed time since the last update.
// Draw new data only if this time exceeds the
// desired frame interval given by msPerFrame
msElapsed += gameTime.ElapsedGameTime.Milliseconds;
if(msElapsed > msPerFrame) {
//Reset the elapsed time and draw the frame with
// new data.
msElapsed = 0;
14.5.4.1 Controlling the animation speed
The code in Listing 6 (p. 198) is not new to this module. I explained code like this in an earlier module.
Briey, the Update method is called sixty times per second by default (I didn't change the default).
That is too fast to produce an animation that looks the way I wanted it to look.
14.5.4.1.1 Draw new material every 83 milliseconds
The code in Listing 6 (p. 198) causes the sprites to be drawn sixty times per second, but new material is
drawn only once every msPerFrame milliseconds. The value for msPerFrame is set to 83 milliseconds
in Listing 1 (p. 195) , but as you learned in an earlier module, its value could be changed by program code
as the program is running.
Therefore, in this program, new material is drawn 30 times per second. Every other frame that is drawn
looks exactly like the one before it, but the human eye is not fast enough to be able to detect that.
14.5.4.1.2 Apply dynamic scaling to the background image
A dierent and ever increasing scale factor is applied to the background image each time the body of the
statement in the Update method in Listing 6 (p. 198) is executed.
if
14.5.4.1.2.1 Two components in the scale factor
This overall scale factor is composed of the xed backgroundBaseScale factor computed in Listing 5 (p.
197) and a dynamic scale factor named dynamicScale that is computed in Listing 7 (p. 199) .
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
199
14.5.4.1.2.2 An optical illusion
This is what causes the size of the planet to continue to increase during the animation sequence creating the
illusion that the UFO is getting closer to the surface of the planet.
14.5.4.1.2.3 The dynamic portion of the overall scale factor
The value of the dynamic portion of the overall scale factor increases by 0.03 each time this code is executed.
Once the dynamic portion reaches a value of 10, it is reset to zero and the animation sequence plays again
from the beginning.
This is accomplished by the scaling algorithm shown in Listing 7 (p. 199) .
Listing 7
. Apply dynamic scaling to the background image.
//Reset the animation if the dynamicScale factor
// is greater than 10.
if(dynamicScale > 10) {
dynamicScale = 0.0f;
} else {
//Increase the dynamicScale factor and use it
// to compute a new scale factor that will be
// applied to the background image in the next
// call to the Draw method.
dynamicScale += 0.03f;
backgroundScale =
backgroundBaseScale * (1 + dynamicScale);
}//end if-else on dynamicScale
14.5.4.2 Changing the origin of the background image
The SpriteBatch.Draw method has one parameter named position and another parameter named
origin . You can cause the origin to be any point in the image relative to its upper left corner.
Important: The position parameter species the position in the game window where the image's
origin will be drawn.
If you hold the position parameter constant and vary the
within the game window.
origin
, the image will appear to slide around
14.5.4.2.1 Increasing the scale alone is insucient
Consider the background image shown in Figure 3 (p. 185) . Assume that the origin of the image is the
upper left corner of the image. Also assume that the image will be drawn with that origin in the upper
left corner of the game window.
If we were to scale the image to make it larger, the planet would be pushed down and to the right and
would exit the game window near the bottom right corner of the game window. That is not what we are
looking for.
14.5.4.2.2 Need to stabilize the location of the planet in the game window
To achieve the eect we are looking for, we need to cause the planet to remain in pretty much the same
location within the game window as it gets larger. One way to accomplish that is by causing the origin to
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 14. XNA0124-USING BACKGROUND IMAGES AND COLOR
KEY TRANSPARENCY
200
move down and to the right within the background image as the planet becomes larger. (Obviously that is
not the only way to accomplish it.)
That eect is accomplished by the algorithm shown in Listing 8 (p. 200) .
Listing 8
. Adjust the origin for the background image.
//Also use the dynamicScale factor to compute a
// new value for the origin of the background.
// The origin is the point in the image that is
// drawn at the point in the game window that is
// passed as the second (position) parameter in
// the call to the SpriteBatch.Draw method. This
// has the effect of causing the background
// image to slide up and to the left at the same
// time that it is getting larger.
spaceOrigin =
new Vector2((float)(450 * (dynamicScale) / 12),
(float)(338 * (dynamicScale) / 10));
}//end if on msElapsed
base.Update(gameTime);
}//end Update
14.5.4.2.3 Examples
For example, in Figure 4 (p. 186) , the origin of the image (the point drawn in the upper left corner of the
game window) was out in space far removed from the planet. By Figure 10 (p. 192) the origin had shifted
to the surface of the planet causing a point on the surface of the planet to be drawn in the upper left corner
of the game window. By Figure 12 (p. 194) , the origin had shifted all the way down and to the right to be
on the surface of the ring that surrounds the planet.
14.5.4.3 The end of the Update method
Listing 8 (p. 200) signals the end of the overridden
Update
method.
14.5.5 The overridden Game.Draw method
To avoid confusion, I want to remind you that when programming in XNA, you override the Game.Draw
method. Within that method, you make calls to the SpriteBatch.Draw method. These are entirely
dierent methods belonging to dierent objects even though they have the same name.
14.5.5.1 No need to clear the game window
The overridden Game.Draw method begins in Listing 9 (p.
201) . Unlike the programs in earlier modules
in this book, there is no need to clear the game window to a constant color in this program because the
background image of the planet completely lls the game window.
14.5.5.2 Two main sections of code
In this program, the overridden Game.Draw method consists of two main sections of code. The rst
section consists of a Begin , Draw , End sequence with alpha blending turned o. This code is used
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
201
to draw the background image. Alpha blending is turned o to prevent any areas of the background image
that may have a low alpha value or any green pixels from appearing to be transparent.
The second section consists of another Begin , Draw , End sequence with alpha blending turned
on. This code is used to draw the UFO. Alpha blending is turned on to cause the green areas in the UFO
sprite shown in Figure 2 (p. 185) to be transparent and to let the background image show through.
14.5.5.2.1 Draw the background image showing the planet
The rst major section of code is shown in Listing 9 (p. 201) .
Note the change that was made in the call to the
program from XNA 3.1 to XNA 4.0.
Listing 9
SpriteBatch.Begin
method to upgrade the
. Beginning of the overridden Game.Draw method.
protected override void Draw(GameTime gameTime) {
//
// Turn off blending to draw the planet in the
// background. Note the update for XNA 4.0.
spriteBatch.Begin(SpriteBlendMode.None);
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque);
//Draw the background.
spriteBatch.Draw(spaceTexture,//sprite
Vector2.Zero,//position re window
null,//rectangle
Color.White,//tint
0,//rotation
spaceOrigin,//origin
backgroundScale,//scale
SpriteEffects.None,
1.0f);//layer, near the back
spriteBatch.End();
The only things that are new here are the interactions among the position parameter, the origin
parameter, and the scale parameter that I explained earlier.
It is probably also worth noting that the last parameter has a value of 1.0, which causes this image to
be displayed behind every other image.
14.5.5.2.2 Draw the UFO
The code in Listing 10 (p. 201) draws the UFO image with alpha blending turned on to cause the green
areas of the image to be transparent.
Again, note the change that was made in the call to the
the program from XNA 3.1 to XNA 4.0.
Listing 10
SpriteBatch.Begin
method to upgrade
. Draw the UFO.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 14. XNA0124-USING BACKGROUND IMAGES AND COLOR
KEY TRANSPARENCY
202
//
// Turn on blending to draw the UFO in the
// foreground. Note the update for XNA 4.0.
spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);
//Center the UFO at the point in the window
// identified as UFO position. The UFO image is
// 64x33 pixels.
Vector2 origin = new Vector2(32,16);
spriteBatch.Draw(ufoTexture,//sprite
ufoPosition,//position re window
null,//rectangle
Color.White,//tine
0,//rotation
origin,//origin
1,//scale
SpriteEffects.None,
0f);//layer, 0 is in front
spriteBatch.End();
base.Draw(gameTime);
}//end Draw method
}//end class
}//end namespace
14.5.5.2.2.1 Center the UFO image on the position
The UFO image is 64 pixels wide and 33 pixels high. The origin is set to the center of the image with
coordinates of (32,16) relative to the upper left corner of the image.
Then the image is drawn with the origin at the ufoPosition , which was computed in Listing 4 (p.
197) .
The value of ufoPosition species a point that is in the horizontal center of the game window and 70
pixels from the bottom of the game window. Therefore, the center of the UFO is positioned at the horizontal
center of the game window and 70 pixels up from the bottom of the game window as shown in Figure 4 (p.
186) through Figure12 (p. 194) .
14.5.5.2.2.2 The UFO does not move
The UFO doesn't move. It stays in one place and the background image is animated behind it.
14.5.5.2.2.3 The front of the z-order stack
The value of the last parameter in Listing 10 (p. 201) is zero. This places the UFO in front of every other
image. Of course, in this case, there are only two images: the background image at the back and the UFO
image at the front.
14.5.6 The end of the program
Listing 10 (p. 201) signals the end of the overridden Draw method, the end of the class, and the end of the
program.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
203
14.5.7 Recap on the origin and position parameters
The SpriteBatch.Draw method has an origin parameter and a position parameter.
The origin parameter species a point somewhere in the image being drawn relative to the upper left
corner of the image. The position parameter species where that point will be drawn relative to the upper
left corner of the game window.
14.6 Run the program
I encourage you to download Figure 2 (p. 185) and Figure 3 (p. 185) and to copy the code from Listing
11 (p. 204) . Use that code to create an XNA project. Compile and run the project. Experiment with the
code, making changes, and observing the results of your changes. Make certain that you can explain why
your changes behave as they do. Note that the image that you download from Figure 3 (p. 185) will be
smaller than the image that I used. Therefore, your scaling will probably need to be dierent.
14.7 Run my program
Click here
10
to download a zip le containing my version of the program. Extract the folder named
XNA0124Proj from the zip le and save it somewhere on your disk. Start Visual C# 2010 Express
and select Open Project... from the File menu. Navigate to that folder and select the le with the
extension of .sln . This should cause the project to open and be ready to run or debug as described in the
earlier module titled Getting Started
11
.
14.8 Summary
You learned about the following major topics in this module:
• How to display a sprite in front of a background image.
• The dierence between the position and origin parameters of the
SpriteBatch.Draw
method.
• How to cause the background image to change at runtime.
• How to deal with and use color key transparency.
14.9 Miscellaneous
This section contains a variety of miscellaneous information.
Housekeeping material
• Module name: Xna0124-Using Background Images and Color Key Transparency
• File: Xna0124.htm
• Published: 02/28/14
Disclaimers: Financial : Although the Connexions site makes it possible for you to download
a PDF le for this module at no charge, and also makes it possible for you to purchase a pre-printed
version of the PDF le, you should be aware that some of the HTML elements in this module may
not translate well into PDF.
10 http://cnx.org/content/m49515/latest/XNA0124Proj.zip
11 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 14. XNA0124-USING BACKGROUND IMAGES AND COLOR
KEY TRANSPARENCY
204
I also want you to know that, I receive no nancial compensation from the Connexions website even
if you purchase the PDF version of the module.
In the past, unknown individuals have copied my modules from cnx.org, converted them to Kindle
books, and placed them for sale on Amazon.com showing me as the author. I neither receive
compensation for those sales nor do I know who does receive compensation. If you purchase such
a book, please be aware that it is a copy of a module that is freely available on cnx.org and that it
was made and published without my prior knowledge.
Aliation : I: I am a professor of Computer Information Technology at Austin Community
College in Austin, TX.
14.10 Complete program listing
A complete listing of the XNA program discussed in this module is provided in Listing 11 (p. 204) . Note
the changes that were made in the calls to the SpriteBatch.Begin method to upgrade the program from
XNA 3.1 to XNA 4.0. Blending was turned o in the rst call just in case there were pixels in the background
image having a color that matches the key color value.
Listing 11
. The class named Game1 for the project named XNA0124Proj.
/*Project XNA0124Proj
* Illustrates displaying a sprite with color key
* transparency in front of a background image.
* Must modify the color key property value for the ufo
* sprite, changing the key color from the default of
* 255,0,255 (magenta or magic pink) to 0,255,0 (green).
* The scale and the origin of the background image is
* changed over time giving the illusion of a ufo
* flying over a planet.
********************************************************/
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace XNA0124Proj {
public class Game1 : Microsoft.Xna.Framework.Game {
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
private Viewport viewport;
private Vector2 ufoPosition;
private float backgroundScale;
private float backgroundBaseScale;
private float dynamicScale = 0.0f;
int msElapsed;//Time since last new frame.
int msPerFrame = 83;//30 updates per second
Texture2D spaceTexture;//background image
Texture2D ufoTexture;//ufo image
Vector2 spaceOrigin;//origin for drawing background
public Game1() {//constructor
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
205
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
//Set the size of the game window, causing the
// aspect ratio of the game window to match the
// aspect ratio of the background image, which is
// 640 wide by 480 high.
graphics.PreferredBackBufferWidth = 450;
graphics.PreferredBackBufferHeight =
(int)(450.0*480/640);
}//end constructor
//-------------------------------------------------//
protected override void Initialize() {
// No initialization required
base.Initialize();
}//end Initialize
//-------------------------------------------------//
protected override void LoadContent() {
// Create a new SpriteBatch, which can be used to
// draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
//Load the two images.
spaceTexture = Content.Load<Texture2D>("space");
ufoTexture = Content.Load<Texture2D>("ufo");
//Get a reference to the viewport.
viewport = graphics.GraphicsDevice.Viewport;
//Compute the position of the ufo relative to the
// game window.
ufoPosition.X = viewport.Width / 2;
ufoPosition.Y = viewport.Height - 70;
//Set the backgroundBaseScale factor such that
// the entire background image will fit in the
// game window. Note that the aspect ratio of the
// game window was set to match the aspect ratio
// of the background image in the constructor.
backgroundBaseScale = (float)(450.0 / 640.0);
}//end LoadContent
//-------------------------------------------------//
protected override void UnloadContent() {
// No unload required
}//end UnloadContent
//-------------------------------------------------//
protected override void Update(GameTime gameTime) {
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 14. XNA0124-USING BACKGROUND IMAGES AND COLOR
KEY TRANSPARENCY
206
//Compute the elapsed time since the last update.
// Draw new data only if this time exceeds the
// desired frame interval given by msPerFrame
msElapsed += gameTime.ElapsedGameTime.Milliseconds;
if(msElapsed > msPerFrame) {
//Reset the elapsed time and draw the frame with
// new data.
msElapsed = 0;
//Reset the animation if the dynamicScale factor
// is greater than 10.
if(dynamicScale > 10) {
dynamicScale = 0.0f;
} else {
//Increase the dynamicScale factor and use it
// to compute a new scale factor that will be
// applied to the background image in the next
// call to the Draw method.
dynamicScale += 0.03f;
backgroundScale =
backgroundBaseScale * (1 + dynamicScale);
}//end if-else on dynamicScale
//Also use the dynamicScale factor to compute a
// new value for the origin of the background.
// The origin is the point in the image that is
// drawn at the point in the game window that is
// passed as the second (position) parameter in
// the call to the SpriteBatch.Draw method. This
// has the effect of causing the background
// image to slide up and to the left at the same
// time that it is getting larger.
spaceOrigin =
new Vector2((float)(450 * (dynamicScale) / 12),
(float)(338 * (dynamicScale) / 10));
}//end if on msElapsed
base.Update(gameTime);
}//end Update
//-------------------------------------------------//
protected override void Draw(GameTime gameTime) {
//
// Turn off blending to draw the planet in the
// background. Note the update for XNA 4.0.
spriteBatch.Begin(SpriteBlendMode.None);
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque);
//Draw the background.
spriteBatch.Draw(spaceTexture,//sprite
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
207
Vector2.Zero,//position re window
null,//rectangle
Color.White,//tint
0,//rotation
spaceOrigin,//origin
backgroundScale,//scale
SpriteEffects.None,
1.0f);//layer, near the back
spriteBatch.End();
//
// Turn on blending to draw the UFO in the
// foreground. Note the update for XNA 4.0.
spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);
//Center the UFO at the point in the window
// identified as UFO position. The UFO image is
// 64x33 pixels.
Vector2 origin = new Vector2(32,16);
spriteBatch.Draw(ufoTexture,//sprite
ufoPosition,//position re window
null,//rectangle
Color.White,//tine
0,//rotation
origin,//origin
1,//scale
SpriteEffects.None,
0f);//layer, 0 is in front
spriteBatch.End();
base.Draw(gameTime);
}//end Draw method
}//end class
}//end namespace
-end-
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
208
CHAPTER 14. XNA0124-USING BACKGROUND IMAGES AND COLOR
KEY TRANSPARENCY
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
Chapter 15
Xna0126-Using OOP - A Simple Sprite
1
Class
Revised: Mon May 09 13:00:37 CDT 2016
This page is part of a Book titled XNA Game Studio
2
.
15.1 Table of Contents
• Table of Contents (p. 209)
• Preface (p. 211)
· Viewing tip (p. 211)
* Figures (p. 211)
* Listings (p. 211)
• General background information (p. 211)
· The three pillars of OOP (p. 211)
· Wanted, lots of sprites (p. 211)
· Encapsulation to the rescue (p. 212)
• Preview (p. 212)
·
·
·
·
Demonstrate how to use the Sprite class (p. 212)
Instantiate Sprite objects on the move (p. 212)
Seven Sprite objects (p. 213)
Move to the right and down (p. 213)
• Discussion and sample code (p. 214)
· The Sprite class (p. 215)
* Two instance variables and a property accessor method (p. 215)
* Two overloaded constructors (p. 215)
· No image when instantiated (p. 215)
· Image loaded during construction (p. 216)
1 This content is available online at <http://cnx.org/content/m49520/1.2/>.
2 http://cnx.org/contents/GY804-eY
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
209
210
CHAPTER 15. XNA0126-USING OOP - A SIMPLE SPRITE CLASS
· The rst parameter (p. 216)
· The second parameter (p. 216)
· Loading the image (p. 216)
* The SetImage method (p. 216)
· Load an image into a Sprite object (p. 216)
· The parameters and the body (p. 216)
* The Draw method of the Sprite class (p. 217)
· A single parameter (p. 217)
· The body of the Sprite.Draw method (p. 217)
* The end of the class (p. 217)
· The Game1 class (p. 217)
* The generic List class (p. 218)
* The modied Game1 constructor (p. 218)
* The overridden LoadContent method (p. 218)
· A new Sprite object (p. 219)
· List capacity considerations (p. 219)
· Assign an image to the Sprite object (p. 219)
* The overridden Update method (p. 219)
·
·
·
·
·
·
·
The Count property of the List object (p. 219)
The remaining 23 Sprite objects (p. 219)
Adding Sprite object references to the list (p. 220)
Instantiate new Sprite objects (p. 220)
The modulus operator (p. 220)
Every eighth iteration (p. 220)
Even and odd sprite images (p. 220)
· Make all the existing sprites move (p. 221)
· Use a for loop (p. 221)
· Put a new value in the Position property (p. 221)
· Load a green ball image in the topmost Sprite object (p. 221)
· Maintain the frame counter (p. 221)
· The end of the overridden Update method (p. 222)
* The overridden Game1.Draw method (p. 222)
· Draw all Sprite objects (p. 222)
· Call the SpriteBatch.Draw method (p. 222)
· The end of the Game1.Draw method (p. 223)
•
•
•
•
•
Run the program (p. 223)
Run my program (p. 223)
Summary (p. 223)
Miscellaneous (p. 223)
Complete program listing (p. 224)
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
211
15.2 Preface
This module is one in a collection of modules designed primarily for teaching GAME 1343 Game and
Simulation Programming I at Austin Community College in Austin, TX. These modules are intended
to supplement and not to replace the textbook.
An earlier module titled Getting Started 3 provided information on how to get started programming with
Microsoft's XNA Game Studio.
15.2.1 Viewing tip
I recommend that you open another copy of this module in a separate browser window and use the following
links to easily nd and view the Figures and Listings while you are reading about them.
15.2.1.1 Figures
• Figure 1 (p. 212) . Seven Sprite objects.
• Figure 2 (p. 214) . Twenty-four Sprite objects with a green one at the top.
15.2.1.2 Listings
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
1 (p. 215) . Beginning of the Sprite class.
2 (p. 215) . Two overloaded constructors.
3 (p. 216) . The SetImage method.
4 (p. 217) . The Draw method of the Sprite class.
5 (p. 217) . Beginning of the class named Game1.
6 (p. 218) . The modied constructor for the Game1 class.
7 (p. 218) . The overridden LoadContent method.
8 (p. 220) . Beginning of the overridden Update method.
9 (p. 220) . Instantiate new Sprite objects.
10 (p. 221) . Make all the existing sprites move.
11 (p. 221) . Load a green ball image in the topmost Sprite object.
12 (p. 221) . Maintain the frame counter.
13 (p. 222) . The overridden Game1.Draw method.
14 (p. 224) . Contents of the le named Sprite.cs
15 (p. 225) . Contents of the le named Game1.cs.
15.3 General background information
15.3.1 The three pillars of OOP
An object-oriented programming language like C# supports encapsulation , inheritance , and polymorphism . In this module, you will learn how to take advantage of encapsulation.
15.3.2 Wanted, lots of sprites
Assume that you are writing a game program in which you need to have several dozen similar sprites on
the screen at the same time. Creating and controlling that many sprites without using encapsulation would
require you to write a lot of program code.
3 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
212
CHAPTER 15. XNA0126-USING OOP - A SIMPLE SPRITE CLASS
15.3.3 Encapsulation to the rescue
However, by encapsulating the characteristics of a sprite into a class, which is a blueprint for an object, you
can instantiate sprite objects just like cutting out cookies with a cookie cutter.
In this module, you will learn to dene and use a very simple Sprite class. In future modules, you
will learn how to modify the Sprite class to make it more sophisticated by making use of inheritance and
polymorphism in addition to encapsulation
15.4 Preview
The XNA project that I will explain in this module is named XNA0126Proj . This project demonstrates
how to design and use a very simple version of a Sprite class. An object instantiated from the Sprite
class has the following general characteristics:
•
•
•
•
•
It
It
It
It
It
can
can
can
can
can
be instantiated without an image.
be instantiated with an image.
have its image set.
have its position within the game window set.
cause itself to be drawn.
15.4.1 Demonstrate how to use the Sprite class
Methods are overridden in the standard XNA Game1 class that demonstrate the use of the Sprite class.
One Sprite object is instantiated in the overridden LoadContent method of the Game1 class. The
object's reference is saved in a generic List object. Twenty-three more Sprite objects are instantiated
in the overridden Update method while the game loop is running.
15.4.2 Instantiate Sprite objects on the move
Sprite object is instantiated in the Update method every 8th iteration of the game loop until
twenty-four Sprite objects have been instantiated. The object's references are saved in the same generic
List object mentioned above.
A new
An image of a blue ball is stored in 12 of the objects and an image of a red ball is stored in the other 12
objects. The red and blue balls alternate and the Sprite objects are drawn in a diagonal line as shown in
Figure 1 (p. 212) .
Figure 1
. Seven Sprite objects.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
213
15.4.3 Seven Sprite objects
Figure 1 (p. 212) shows the game window after seven of the twenty-four
instantiated and drawn in the game window.
Sprite
objects have been
15.4.4 Move to the right and down
The line of Sprite objects moves across the game window from upper left to lower right as the Sprite
objects are being instantiated. They stop moving when they reach the bottom right corner of the game
window.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
214
CHAPTER 15. XNA0126-USING OOP - A SIMPLE SPRITE CLASS
When the objects stop moving, the image in the topmost
a green ball as shown in Figure 2 (p. 214) .
Figure 2
Sprite
object is changed from a blue ball to
. Twenty-four Sprite objects with a green one at the top.
15.5 Discussion and sample code
As usual, I will explain the program code in fragments. A complete listing of the class named Sprite is
provided in Listing 14 (p. 224) and a complete listing of the class named Game1 is provided in Listing 15
(p. 225) .
I will begin my explanation with the class named Sprite .
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
215
15.5.1 The Sprite class
The le named Sprite.cs (see Listing 14 (p. 224) ) denes a simple version of a Sprite class from which
multiple Sprite objects can be instantiated, loaded with an image, and drawn in the game window. The
Position property of each Sprite object can be accessed by the user to control the position at which the
sprite is drawn.
The denition of the Sprite class begins in Listing 1 (p. 215) .
Listing 1
. Beginning of the Sprite class.
namespace XNA0126Proj {
class Sprite {
private Texture2D texture;
private Vector2 position = new Vector2(0,0);
//-------------------------------------------------//
public Vector2 Position {
get {
return position;
}//end get
set {
position = value;
}//end set
}//end Position property accessor
15.5.1.1 Two instance variables and a property accessor method
Listing 1 (p. 215) declares two instance variables and denes an accessor for the Position property.
The rst instance variable named texture will be used to store the image for the sprite. The second
instance variable named position will be used to store the value for the Position property.
15.5.1.2 Two overloaded constructors
Listing 2 (p. 215) denes two overloaded constructors for the
Listing 2
Sprite
class.
. Two overloaded constructors.
public Sprite() {//constructor
}//end noarg constructor
//-------------------------------------------------//
public Sprite(String assetName,
ContentManager contentManager) {
texture =
contentManager.Load<Texture2D>(assetName);
}//end constructor
15.5.1.2.1 No image when instantiated
The rst overloaded constructor, which requires no parameters, makes it possible to instantiate a Sprite
object without loading an image for the object when it is constructed. A method named SetImage can
be called later to load an image for the object.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
216
CHAPTER 15. XNA0126-USING OOP - A SIMPLE SPRITE CLASS
Be aware that if you instantiate a Sprite object using this constructor and then attempt to
draw the object without rst calling the SetImage method to load an image into the object, the
program will fail with a runtime error.
15.5.1.2.2 Image loaded during construction
The second overloaded constructor, which requires two parameters, makes it possible to load an image for
the Sprite object when it is constructed.
15.5.1.2.2.1 The rst parameter
The rst parameter required by the second constructor is the Asset
is added to the Content folder during the project design phase.
Name
property for an image le that
15.5.1.2.2.2 The second parameter
The second parameter is a reference to the
object from the Game class.
ContentManager
object that is inherited into the
Game1
15.5.1.2.2.3 Loading the image
You are already familiar with the code in the body of the constructor that is used to load the image into the
object.
15.5.1.3 The SetImage method
The SetImage method is shown in its entirety in Listing 3 (p.
Listing 3
216) .
. The SetImage method.
public void SetImage(String assetName,
ContentManager contentManager) {
texture =
contentManager.Load<Texture2D>(assetName);
}//end SetImage
15.5.1.3.1 Load an image into a Sprite object
The SetImage method makes it possible to load
an image into a Sprite object that was originally
constructed without an image (p. 215) , or to change the image in a Sprite object that already contains
an image.
15.5.1.3.2 The parameters and the body
The parameters required by this method and the body of the method are the same as for the rst constructor
(p. 215) discussed above.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
217
15.5.1.4 The Draw method of the Sprite class
Listing 4 (p. 217) shows the Draw method of the Sprite
Listing 4
class in its entirety.
. The Draw method of the Sprite class.
public void Draw(SpriteBatch spriteBatch) {
spriteBatch.Draw(texture,position,Color.White);
}//end Draw method
//-------------------------------------------------//
}//end Sprite class
}//end namespace
This method should be called after a call to the
SpriteBatch.End method.
SpriteBatch.Begin
method and before a call to the
15.5.1.4.1 A single parameter
The method requires a single parameter, which is a reference to the
Begin method has been called.
SpriteBatch
object on which the
15.5.1.4.2 The body of the Sprite.Draw method
You should recognize the single statement in the method as a call to the simplest available Draw method
belonging to the SpriteBatch object. This version of the SpriteBatch.Draw method allows the caller
to specify
• The texture or image to be drawn.
• The position in the game window relative to the upper left corner of the window at which the image
will be drawn.
• A color tint that will be applied to the image with White being no change in color.
15.5.1.5 The end of the class
Listing 4 (p. 217) signals the end of the
Sprite
class.
15.5.2 The Game1 class
Methods of the Game1 class were overridden to demonstrate the use of the
output described earlier (p. 212) .
The class named Game1 begins in Listing 5 (p. 217) .
Listing 5
Sprite
. Beginning of the class named Game1.
namespace XNA0126Proj {
public class Game1 : Microsoft.Xna.Framework.Game {
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
//References to the Sprite objects are stored in this
// List object.
List<Sprite> sprites = new List<Sprite>();
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
class to produce the
218
CHAPTER 15. XNA0126-USING OOP - A SIMPLE SPRITE CLASS
int maxSprites = 24;//Max number of sprites.
int frameCnt = 0;//Game loop frame counter
//This is the limit on the number of frames in which
// the sprites are moved.
int moveLim = 200;
Listing 5 (p. 217) simply declares several instance variables, most of which you should recognize. The others
are well described by the comments. However, one of the instance variables, sprites , introduces a concept
that is new to this module.
15.5.2.1 The generic List class
Listing 5 (p. 217) declares a variable named sprites and populates it with a reference to a new generic
List object that is conditioned to store and retrieve references to objects of the class Sprite .
Here is some of what the documentation 4 has to say about the generic List class:
"Represents a strongly typed list of objects that can be accessed by index. Provides methods to
search, sort, and manipulate lists."
The generic List class provides many more capabilities than I will use in this program. I will use an object
of the generic List class to store references to Sprite objects that I can later access using a zero-based
index. This will eliminate the requirement to declare a separate reference variable for each of the Sprite
objects that I instantiate.
15.5.2.2 The modied Game1 constructor
The constructor for the Game1 class was modied to set the size of the game window as shown in Listing
6 (p. 218) .
Listing 6
. The modied constructor for the Game1 class.
public Game1() {//constructor
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
//Set the size of the game window.
graphics.PreferredBackBufferWidth = 450;
graphics.PreferredBackBufferHeight = 450;
}//end constructor
You have seen code identical to this code in earlier modules so there is nothing new to discuss here.
15.5.2.3 The overridden LoadContent method
The overridden LoadContent method is shown in Listing 7 (p.
Listing 7
218) .
. The overridden LoadContent method.
4 http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
219
protected override void LoadContent() {
spriteBatch = new SpriteBatch(GraphicsDevice);
//Create the first sprite in the LoadContent
// method using the noarg constructor.
sprites.Add(new Sprite());
//Assign an image to the sprite.
sprites[0].SetImage("blueball",Content);
}//end LoadContent
15.5.2.3.1 A new Sprite object
A statement near the center of Listing 7 (p. 218) instantiates a new Sprite object with no image and calls
the Add method of the generic List object to add the object's reference to the end of the list.
15.5.2.3.2 List capacity considerations
One advantage of using a generic List object as an alternative to a simple array is that it is not necessary
to declare the capacity of the List object when the program is compiled. The capacity of the List object
increases automatically as necessary to accommodate all of the references that are added to the list.
Since this is the rst reference added to the list, it can be accessed later using an index value of 0.
15.5.2.3.3 Assign an image to the Sprite object
The last statement in Listing 7 (p. 218) retrieves the reference from the list at index 0 and uses that reference
to call the SetImage method on the Sprite object to which it refers.
You learned about the SetImage method in the discussion of the code in Listing 3 (p. 216) . This call
causes the Sprite object whose reference is stored at index 0 in the list to load the image from the image
le with the Asset Name property value of "blueball".
As mentioned earlier, the second parameter named Content is a reference to a ContentManager
object that is inherited from the Game class.
15.5.2.4 The overridden Update method
As you learned earlier (p. 212) , this program creates, manipulates, and draws 24 objects of the Sprite
class in the game window as shown in Figure 2 (p. 214) . The rst Sprite object was created in the
overridden LoadContent method when it was called earlier.
15.5.2.4.1 The Count property of the List object
The generic List object referred to by sprites has a property named Count that keeps track of the
number of references contained in the object. The rst time the Update method is called, the value of
Count is 1 as a result of the Sprite object's reference having been added to the list in the LoadContent
method earlier.
15.5.2.4.2 The remaining 23 Sprite objects
This program creates the remaining 23 sprites in the Update method to simulate a game in which sprites
come and go as the game progresses.
The overridden Update method begins in Listing 8 (p. 220) .
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
220
CHAPTER 15. XNA0126-USING OOP - A SIMPLE SPRITE CLASS
Listing 8
. Beginning of the overridden Update method.
protected override void Update(GameTime gameTime) {
if(sprites.Count < (maxSprites)) {
15.5.2.4.3 Adding Sprite object references to the list
Listing 8 (p. 220) shows the beginning of an if statement in which a new Sprite
objects reference will
be added to the list every eighth iteration (frame) of the game loop until all 24 sprites have been added.
Note that a frame counter named frameCnt is declared and initialized to zero in Listing 5 (p. 217)
and is incremented near the end of the overridden Update method in Listing 12 (p. 221) .
15.5.2.4.4 Instantiate new Sprite objects
The code in Listing 9 (p. 220) uses the modulus (%) operator to identify every eighth iteration of the game
loop and to instantiate a new Sprite object during those iterations.
Listing 9
. Instantiate new Sprite objects.
if(frameCnt % 8 == 0) {
//Instantiate a new sprite every 8th frame.
if((sprites.Count) % 2 == 0) {
//Even numbered sprites
sprites.Add(new Sprite("blueball",Content));
}
else {
//Odd numbered sprites
sprites.Add(new Sprite("redball",Content));
}//end else
}//end if on frameCnt
}//end if on sprites.Count
15.5.2.4.4.1 The modulus operator
In case you have forgotten, the modulus operator returns the remainder of a division instead of returning
the quotient. If an integer value is divided by 8, the returned value is 0 only when the integer value is a
multiple of 8. (Also by denition, 0 % 8 returns 0.)
15.5.2.4.4.2 Every eighth iteration
Therefore, the conditional expression in the rst if statement in Listing 9 (p. 220) will allow the statements
contained in the body of the if statement, (which instantiate new Sprite objects), to be executed only
during every eighth iteration of the game loop.
Further, the conditional expression in Listing 8 (p. 220) will not allow the code in Listing 9 (p. 220) to
be executed after 24 Sprite objects have been instantiated.
15.5.2.4.4.3 Even and odd sprite images
The conditional expression in the second if statement in Listing 9 (p.
220) causes the new Sprite objects
that are instantiated to alternate between the "blueball" and "redball" images shown in Figure 1 (p. 212) .
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
221
15.5.2.4.5 Make all the existing sprites move
Listing 5 (p. 217) declares an instance variable named moveLim and sets its value to 200. The code in
Listing 10 (p. 221) causes all of the existing sprites to move to the right and down if the value of the frame
counter is less than 200.
Listing 10
. Make all the existing sprites move.
if(frameCnt < moveLim) {
for(int cnt = 0;cnt < sprites.Count;cnt++) {
sprites[cnt].Position = new Vector2(
10 * cnt + frameCnt,10 * cnt + frameCnt);
}//end for loop
}//end if
15.5.2.4.5.1 Use a for loop
The code in Listing 10 (p. 221) uses a for loop to access each of the Sprite object references currently
stored in the list, iterating from 0 to one less than the count of references stored in the list given by
sprites.Count .
15.5.2.4.5.2 Put a new value in the Position property
Once a Sprite object's reference has been accessed, Listing 10 (p. 221) sets the Position property stored
in the object to a new Vector2 object for which the X and Y values have been modied on the basis
of the frame counter.
The new X and Y values cause the object to be drawn a little further down and to the right the next
time it is drawn relative to its current position. This causes the entire diagonal line of Sprite objects to
move down and to the right in the game window.
15.5.2.4.6 Load a green ball image in the topmost Sprite object
Listing 11 (p. 221) calls the SetImage method to change the image in the topmost sprite at the end of
the run from a blue ball to a green ball.
Listing 11
. Load a green ball image in the topmost Sprite object.
if(frameCnt == moveLim) {
sprites[0].SetImage("greenball",Content);
}//end if
This capability would be useful, for example to change a sprite's image into a reball in the event of a
collision with another sprite.
15.5.2.4.7 Maintain the frame counter
The code in Listing 12 (p. 221) keeps track of the count of the rst
Listing 12
moveLim
iterations of the game loop.
. Maintain the frame counter.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
222
CHAPTER 15. XNA0126-USING OOP - A SIMPLE SPRITE CLASS
if(frameCnt < moveLim) {
frameCnt++;
}//end if
base.Update(gameTime);
}//end Update method
15.5.2.4.8 The end of the overridden Update method
Then Listing 12 (p. 221) makes the required call to the Update
end of the method.
method in the superclass and signals the
15.5.2.5 The overridden Game1.Draw method
The overridden Game1.Draw method is shown in its entirety in Listing 13 (p.
Listing 13 . The overridden Game1.Draw method.
222) .
protected override void Draw(GameTime gameTime) {
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
//Draw all sprites.
for(int cnt = 0;cnt < sprites.Count;cnt++) {
sprites[cnt].Draw(spriteBatch);
}//end for loop
spriteBatch.End();
base.Draw(gameTime);
}//end Draw method
//-------------------------------------------------//
}//end class
}//end namespace
15.5.2.5.1 Draw all Sprite objects
After calling the SpriteBatch.Begin method and before calling the SpriteBatch.End method, Listing
13 (p. 222) uses a for loop to call the Sprite.Draw method on every Sprite object whose reference
is stored in the list, passing a reference to the SpriteBatch object as a parameter in each call.
15.5.2.5.2 Call the SpriteBatch.Draw method
This causes each object to execute the single statement belonging to the Draw method shown in Listing 4
(p. 217) . Thus, the code in Listing 13 (p. 222) causes each Sprite object to call the SpriteBatch.Draw
method to draw itself at the position specied by the current value of its Position property.
Note that there are three dierent methods named Draw being used here:
• Game1.Draw
• SpriteBatch.Draw
• Sprite.Draw
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
223
15.5.2.5.3 The end of the Game1.Draw method
After calling the SpriteBatch.End method, Listing 13 (p. 222) makes the required call to the superclass'
Game.Draw method and then signals the end of the Game1.Draw method. Listing 13 (p. 222) also
signals the end of the class and the end of the program.
15.6 Run the program
I encourage you to copy the code from Listing 14 (p. 224) and Listing 15 (p. 225) . Use that code to create
an XNA project. Compile and run the project. Experiment with the code, making changes, and observing
the results of your changes. Make certain that you can explain why your changes behave as they do.
15.7 Run my program
Click here
5
to download a zip le containing my version of the program. Extract the folder named
XNA0126Proj from the zip le and save it somewhere on your disk. Start Visual C# 2010 Express
and select Open Project... from the File menu. Navigate to the project folder and select the le with
the extension of .sln . This should cause the project to open and be ready to run or debug as described
in the earlier module titled Getting Started
6
.
15.8 Summary
You learned how to design, create, and use a simple
object.
List
Sprite
class. You also learned how to use a generic
15.9 Miscellaneous
This section contains a variety of miscellaneous information.
Housekeeping material
• Module name: Xna0126-Using OOP - A Simple Sprite Class
• File: Xna0126.htm
• Published: 02/28/14
Disclaimers: Financial : Although the Connexions site makes it possible for you to download
a PDF le for this module at no charge, and also makes it possible for you to purchase a pre-printed
version of the PDF le, you should be aware that some of the HTML elements in this module may
not translate well into PDF.
I also want you to know that, I receive no nancial compensation from the Connexions website even
if you purchase the PDF version of the module.
In the past, unknown individuals have copied my modules from cnx.org, converted them to Kindle
books, and placed them for sale on Amazon.com showing me as the author. I neither receive
compensation for those sales nor do I know who does receive compensation. If you purchase such
a book, please be aware that it is a copy of a module that is freely available on cnx.org and that it
was made and published without my prior knowledge.
Aliation : : I am a professor of Computer Information Technology at Austin Community
College in Austin, TX.
5 http://cnx.org/content/m49520/latest/XNA0126Proj.zip
6 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
224
CHAPTER 15. XNA0126-USING OOP - A SIMPLE SPRITE CLASS
15.10 Complete program listing
Complete listings of the XNA program les discussed in this module are provided in Listing 14 (p. 224) and
Listing 15 (p. 225) below.
Listing 14
. Contents of the le named Sprite.cs
/*Project XNA0126Proj
* This file defines a very simple version of a Sprite
* class from which multiple Sprite objects can be
* instantiated, loaded with an image, and drawn.
* The Position property can be accessed by the user
* to control the position at which the sprite is drawn.
*******************************************************/
using
using
using
using
System;
Microsoft.Xna.Framework;
Microsoft.Xna.Framework.Content;
Microsoft.Xna.Framework.Graphics;
namespace XNA0126Proj {
class Sprite {
private Texture2D texture;
private Vector2 position = new Vector2(0,0);
//-------------------------------------------------//
public Vector2 Position {
get {
return position;
}
set {
position = value;
}//end set
}//end Position property accessor
//-------------------------------------------------//
//This constructor makes it possible to instantiate
// a sprite without assigning an image to the sprite.
public Sprite() {//constructor
}//end noarg constructor
//-------------------------------------------------//
//This constructor makes it possible to assign an
// image to the sprite when it is instantiated.
public Sprite(String assetName,
ContentManager contentManager) {
texture =
contentManager.Load<Texture2D>(assetName);
}//end constructor
//-------------------------------------------------//
//This method makes it possible to assign a new image
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
225
// to the sprite.
public void SetImage(String assetName,
ContentManager contentManager) {
texture =
contentManager.Load<Texture2D>(assetName);
}//end SetImage
//-------------------------------------------------//
public void Draw(SpriteBatch spriteBatch) {
//Call the simplest available version of
// SpriteBatch.Draw
spriteBatch.Draw(texture,position,Color.White);
}//end Draw method
//-------------------------------------------------//
}//end class
}//end namespace
Listing 15
. Contents of the le named Game1.cs.
/*Project XNA0126Proj
* This project demonstrates how to design and use a very
* simple version of a Sprite class.
*
* One Sprite object is instantiated in the LoadContent
* method. The object's reference is saved in a generic
* List object.
*
* Twenty-three more Sprite objects are instantiated
* while the game loop is running. A new object is
* instantiated every 8th iteration of the game loop
* until 24 objects have been instantiated. Their
* references are saved in a generic List object.
*
* An image of a blueball is stored in 12 of the objects
* and an image of a redball is stored in the other 12
* objects.
*
* The Sprite objects are drawn in a diagonal line in
* the game window. The line of Sprite objects moves
* across the game window from upper left to lower right.
* The Sprite objects stop moving when they reach the
* bottom right corner of the game window.
*
* When the objects stop moving, the image in the
* topmost Sprite object is changed from a blueball to a
* greenball.
* *****************************************************/
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
226
CHAPTER 15. XNA0126-USING OOP - A SIMPLE SPRITE CLASS
using XNA0126Proj;
namespace XNA0126Proj {
public class Game1 : Microsoft.Xna.Framework.Game {
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
//References to the Sprite objects are stored in this
// List object.
List<Sprite> sprites = new List<Sprite>();
int maxSprites = 24;//Max number of sprites.
int frameCnt = 0;//Game loop frame counter
//This is the limit on the number of frames in which
// the sprites are moved.
int moveLim = 200;
//-------------------------------------------------//
public Game1() {//constructor
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
//Set the size of the game window.
graphics.PreferredBackBufferWidth = 450;
graphics.PreferredBackBufferHeight = 450;
}//end constructor
//-------------------------------------------------//
protected override void Initialize() {
//No initialization required.
base.Initialize();
}//end Initialize
//-------------------------------------------------//
protected override void LoadContent() {
spriteBatch = new SpriteBatch(GraphicsDevice);
//Create the first sprite in the LoadContent
// method using the noarg constructor.
sprites.Add(new Sprite());
//Assign an image to the sprite.
sprites[0].SetImage("blueball",Content);
//More content is loaded in the Update method.
}//end LoadContent
//-------------------------------------------------//
protected override void UnloadContent() {
//No content unload required.
}//end unloadContent
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
227
//-------------------------------------------------//
protected override void Update(GameTime gameTime) {
//Create remaining sprites in the Update method to
// simulate a game in which sprites come and go as
// the game progresses.
if(sprites.Count < (maxSprites)) {
if(frameCnt % 8 == 0) {
//Instantiate a new sprite every 8th frame.
if((sprites.Count) % 2 == 0) {
//Even numbered sprites
sprites.Add(new Sprite("blueball",Content));
}
else {
//Odd numbered sprites
sprites.Add(new Sprite("redball",Content));
}//end else
}//end if on frameCnt
}//end if on sprites.Count
//Make all the sprites move.
if(frameCnt < moveLim) {
for(int cnt = 0;cnt < sprites.Count;cnt++) {
sprites[cnt].Position = new Vector2(
10 * cnt + frameCnt,10 * cnt + frameCnt);
}//end for loop
}//end if
//Change the image on the first sprite at the end
// of the run. Could be used, for example to
// change a sprite's image to a fireball in the
// event of a collision.
if(frameCnt == moveLim) {
sprites[0].SetImage("greenball",Content);
}//end if
//Keep track of the count of the first moveLim
// iterations of the game loop.
if(frameCnt < moveLim) {
frameCnt++;
}//end if
base.Update(gameTime);
}//end Update method
//-------------------------------------------------//
protected override void Draw(GameTime gameTime) {
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
228
CHAPTER 15. XNA0126-USING OOP - A SIMPLE SPRITE CLASS
//Draw all sprites.
for(int cnt = 0;cnt < sprites.Count;cnt++) {
sprites[cnt].Draw(spriteBatch);
}//end for loop
spriteBatch.End();
base.Draw(gameTime);
}//end Draw method
//-------------------------------------------------//
}//end class
}//end namespace
-end-
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
Chapter 16
1
Xna0128-Improving the Sprite Class
Revised: Mon May 09 17:04:23 CDT 2016
This page is part of a Book titled XNA Game Studio
2
.
16.1 Table of Contents
• Table of Contents (p. 229)
• Preface (p. 231)
· Viewing tip (p. 231)
* Figures (p. 231)
* Listings (p. 231)
• General background information (p. 231)
• Preview (p. 231)
· The screen output (p. 232)
· Lots and lots of sprites (p. 232)
• Discussion and sample code (p. 232)
· The Sprite class (p. 232)
*
*
*
*
Properties (p. 233)
Other instance variables (p. 233)
The rst three property accessor methods (p. 233)
The property accessor method for Speed (p. 234)
· The set side (p. 235)
· The get side (p. 235)
* The constructor (p. 235)
· Load an image (p. 235)
· A random number generator (p. 235)
* The SetImage method (p. 235)
* The Move method (p. 236)
1 This content is available online at <http://cnx.org/content/m49527/1.2/>.
2 http://cnx.org/contents/GY804-eY
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
229
230
CHAPTER 16. XNA0128-IMPROVING THE SPRITE CLASS
·
·
·
·
·
·
To move or not to move (p. 236)
Keeping up on the average (p. 236)
Code in the body of the if statement (p. 236)
Add the direction vector to the position vector (p. 236)
A collision with an edge of the game window (p. 237)
Modify the position and call the NewDirection method (p. 237)
· The end of the Move method (p. 238)
* The method named NewDirection (p. 238)
· The length of the direction vector and the signs of the components (p. 238)
· Compute the hypotenuse (p. 238)
· Use the conditional operator (p. 238)
·
·
·
·
·
Compute components of a new direction vector (p. 238)
Compute a new random value for the X component (p. 238)
Compute a consistent value for the Y component (p. 239)
Adjust the signs of the X and Y components (p. 239)
A new direction vector with the same length in the same quadrant (p. 239)
* The Draw method (p. 239)
* The end of the Sprite class (p. 240)
· The Game1 class (p. 240)
* Instance variables (p. 241)
* The modied constructor (p. 241)
* The overridden LoadContent method (p. 241)
· Instantiate all of the space rock sprites (p. 242)
· Call the Sprite constructor (p. 242)
· Set the property values (p. 242)
· Instantiate and set properties on the power pills and the UFOs (p. 243)
* The private DirectionVector method (p. 244)
· Return a direction vector (p. 244)
· The signs of the components (p. 244)
* The overridden Update method (p. 244)
· Very simple code (p. 245)
· To move or not to move, that is the question (p. 245)
· A characteristic of an object-oriented program (p. 245)
* The overridden Game.Draw method (p. 245)
· Erase and redraw the entire game window (p. 246)
· Not the same approach as some other game engines (p. 246)
· The end of the program (p. 246)
•
•
•
•
•
•
Run the program (p. 246)
Run my program (p. 247)
Summary (p. 247)
What's next? (p. 247)
Miscellaneous (p. 247)
Complete program listing (p. 248)
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
231
16.2 Preface
This module is one in a collection of modules designed primarily for teaching GAME 1343 Game and
Simulation Programming I at Austin Community College in Austin, TX. These modules are intended
to supplement and not to replace the textbook.
An earlier module titled Getting Started 3 provided information on how to get started programming with
Microsoft's XNA Game Studio.
16.2.1 Viewing tip
I recommend that you open another copy of this module in a separate browser window and use the following
links to easily nd and view the Figures and Listings while you are reading about them.
16.2.1.1 Figures
• Figure 1 (p. 232) . Screen shot of the running program.
16.2.1.2 Listings
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
1 (p. 232) . Beginning of the Sprite class.
2 (p. 233) . The rst three property accessor methods.
3 (p. 234) . The property accessor method for Speed.
4 (p. 235) . The constructor for the Sprite class.
5 (p. 235) . The SetImage method.
6 (p. 236) . Beginning of the Move method.
7 (p. 237) . Add the direction vector to the position vector.
8 (p. 237) . Process a collision with an edge of the game window.
9 (p. 238) . Beginning of the method named NewDirection.
10 (p. 238) . Compute components of a new direction vector.
11 (p. 239) . The Sprite.Draw method.
12 (p. 240) . Beginning of the Game1 class.
13 (p. 241) . The modied constructor.
14 (p. 241) . Beginning of the overridden LoadContent method.
15 (p. 243) . Instantiate and set properties on the power pills and the UFOs.
16 (p. 244) . The private DirectionVector method.
17 (p. 244) . Tell the sprites to move.
18 (p. 245) . The overridden Game.Draw method.
19 (p. 248) . The class named Sprite for the project named XNA0128Proj.
20 (p. 252) . The class named Game1 for the project named XNA0128Proj.
16.3 General background information
You learned how to design, create, and use a simple Sprite class in an earlier module. You also learned to
use a generic List object to store references to objects of the Sprite class.
16.4 Preview
I will explain improvements made to the Sprite class and will show you how to write a
that takes advantage of those improvements.
3 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
Game1
class
232
CHAPTER 16. XNA0128-IMPROVING THE SPRITE CLASS
16.4.1 The screen output
Figure 1 (p. 232) shows a reduced screen shot of the program while it is running.
Figure 1
. Screen shot of the running program.
16.4.2 Lots and lots of sprites
When this screen shot was taken, the program had 24 space rocks, 12 red power pills and six UFOs all
navigating in the game window.
16.5 Discussion and sample code
As usual, I will discuss and explain the program code in fragments. A complete listing of the Sprite class
is provided in Listing 19 (p. 248) and a complete listing of the Game1 class is provided in Listing 20 (p.
252) .
16.5.1 The Sprite class
The
Sprite
class begins in Listing 1 (p. 232) with the declaration of several instance variables.
Listing 1
. Beginning of the Sprite class.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
233
namespace XNA0128Proj {
class Sprite {
private Texture2D image;
private Vector2 position = new Vector2(0,0);
private Vector2 direction = new Vector2(0,0);
private Point windowSize;
private Random random;
double elapsedTime;//in milliseconds
//The following value is the inverse of speed in
// moves/msec expressed in msec/move.
double elapsedTimeTarget;
The purpose of these instance variables will become clear later in the discussion.
16.5.1.1 Properties
This class has the following properties:
Position - position of the sprite in the game window relative to the upper left corner of the game
window. The X and Y values are saved as type Vector2 in the instance variable named position
.
• WindowSize - The dimensions of the game window. The width and height values are saved as type
Point in the instance variable named windowSize .
• Direction - Direction of motion of the sprite expressed as a 2D vector with an X and a Y component.
The length of this vector is the distance that the sprite moves each time it moves. The values are saved
as type Vector2 in the instance variable named direction .
• Speed - Speed of the sprite expressed in moves per millisecond. The actual speed in pixels per
millisecond is the product of this value and the length of the direction vector.
•
16.5.1.2 Other instance variables
In addition, the class has the following instance variables that are set either by the constructor or by a
method:
image - the image that is drawn to represent the sprite in the game window. The image can be
modied as the game progresses through calls to the SetImage method.
• random - a reference to a random number generator.
• elapsedTime - used to keep track of the amount of elapsed time in milliseconds since the last time
that the sprite was actually moved.
• elapsedTimeTarget - will be explained later.
•
16.5.1.3 The rst three property accessor methods
The rst three property accessor methods are shown in Listing 2 (p. 233) .
Listing 2
. The rst three property accessor methods.
//Position property accessor
public Vector2 Position {
get {
return position;
}
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
234
CHAPTER 16. XNA0128-IMPROVING THE SPRITE CLASS
set {
position = value;
}//end set
}//end Position property accessor
//-------------------------------------------------//
//WindowSize property accessor
public Point WindowSize {
set {
windowSize = value;
}//end set
}//end WindowSize property accessor
//-------------------------------------------------//
//Direction property accessor
public Vector2 Direction {
get {
return direction;
}
set {
direction = value;
}//end set
}//end Direction property accessor
These three accessor methods are straightforward and shouldn't require further explanation.
16.5.1.4 The property accessor method for Speed
Humans usually nd it easier to think in terms of speed such as miles per hour while it is sometimes easier
to write computer programs that deal with the reciprocal of speed such as hours per mile .
The property accessor method for the property named Speed is shown in Listing 3 (p. 234) .
Listing 3
. The property accessor method for Speed.
//Speed property accessor. The set side should be
// called with speed in moves/msec. The get side
// returns speed moves/msec.
public double Speed {
get {
//Convert from elapsed time in msec/move to
// speed in moves/msec.
return elapsedTimeTarget/1000;
}
set {
//Convert from speed in moves/msec to
// elapsed time in milliseconds/move.
elapsedTimeTarget = 1000/value;
}//end set
}//end Speed property accessor
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
235
16.5.1.4.1 The set side
The set side of the property accessor method for the Speed
property receives the incoming value as
moves per millisecond . The code converts this value to milliseconds per move and saves it in the instance
variable named elapsedTimeTarget mentioned earlier.
This is the target for the elapsed time in milliseconds from one movement to the next movement of the
sprite in the game window. Every time a sprite moves, it moves the same distance. Therefore, the apparent
speed of sprite movement as seen by the viewer can be controlled by controlling the elapsed time between
movements.
16.5.1.4.2 The get side
The get side of the property accessor method for the Speed
milliseconds per move back to moves per millisecond .
property converts the returned value from
16.5.1.5 The constructor
The constructor for the Sprite class is shown in Listing 4 (p. 235) .
Listing 4
. The constructor for the Sprite class.
public Sprite(String assetName,
ContentManager contentManager,
Random random) {
image = contentManager.Load<Texture2D>(assetName);
this.random = random;
}//end constructor
16.5.1.5.1 Load an image
The constructor loads an image for the sprite when it is instantiated. Therefore, it requires an
for the image and a reference to a ContentManager object.
Asset Name
16.5.1.5.2 A random number generator
The constructor also requires a reference to a
random values of type double .
Random
object capable of generating a sequence of pseudo
The purpose of the random number generator will become clear later.
The program should use the same Random object for all sprites to avoid getting the same sequence of
values for dierent sprites when two or more sprites are instantiated in a very short period of time.
16.5.1.6 The SetImage method
The SetImage method is shown in Listing 5 (p.
Listing 5 . The SetImage method.
235) .
public void SetImage(String assetName,
ContentManager contentManager) {
image = contentManager.Load<Texture2D>(assetName);
}//end SetImage
This method can be called to load a new image for an existing sprite. The method is essentially the same
as a method having the same name that I explained in an earlier module, so no further explanation should
be required.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
236
CHAPTER 16. XNA0128-IMPROVING THE SPRITE CLASS
16.5.1.7 The Move method
This method causes the sprite to move in the direction of the direction vector if the elapsed time since the
last move exceeds the elapsed time target based on the specied speed.
The Move method begins in Listing 6 (p. 236) .
Listing 6
. Beginning of the Move method.
public void Move(GameTime gameTime) {
//Accumulate elapsed time since the last move.
elapsedTime +=
gameTime.ElapsedGameTime.Milliseconds;
if(elapsedTime > elapsedTimeTarget){
//It's time to make a move. Set the elapsed
// time to a value that will attempt to produce
// the specified speed on the average.
elapsedTime -= elapsedTimeTarget;
The sprite doesn't necessarily move every time the Move method is called. Instead, it uses the incoming
parameter to compute the elapsed time since the last time that it actually moved.
16.5.1.7.1 To move or not to move
If that elapsed time exceeds the target that is based on the specied speed in moves/millisecond , then
it reduces the elapsed time value by the target value and makes an adjustment to the position value.
Changing the position value will cause the sprite to move in the game window the next time it is drawn.
16.5.1.7.2 Keeping up on the average
By reducing the elapsed time by the target time instead of setting it to zero, the sprite attempts to achieve
the target speed on the average . For example, assume that for some reason, there is a long delay between
calls to the Move method and the elapsed time value is two or three times greater than the target time.
This means that the sprite has gotten behind and is not in the position that it should be in. In that case, the
sprite will move every time the Move method is called for several successive calls to the Move method.
(In other words, the sprite will experience a short spurt in speed.) This should cause it to catch up and be
in the correct position once it does catch up.
Of course, if the elapsed time between calls to the Move method is greater than the target time over
the long term, the sprite will never be able to keep up.
16.5.1.7.3 Code in the body of the if statement
If the conditional expression for the if statement in Listing 6 (p.
236) returns true, then the last statement
in Listing 6 (p. 236) along with the remainder of the body of the if statement will be executed. Otherwise,
that statement and the remaining body of the if statement will be skipped.
The remaining body of the if statement begins in Listing 7 (p. 237) .
16.5.1.7.3.1 Add the direction vector to the position vector
One of the advantages of treating the position and the direction as 2D vectors based on the structure named
Vector2 4 is that the Vector2 structure provides various methods 5 that can be used to manipulate vectors.
4 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.vector2.aspx
5 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.vector2_methods.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
237
The code in Listing 7 (p. 237) calls the Add 6 method of the Vector2 class to add the direction vector
to the position vector returning the sum of the two vectors. The sum is saved as the new position vector.
Listing 7
. Add the direction vector to the position vector.
position = Vector2.Add(position,direction);
In case you are unfamiliar with the addition of 2D vectors, if you add a pair of 2D vectors, the X component
of the sum is the sum of the X components and the Y component of the sum is the sum of the Y components.
16.5.1.7.3.2 A collision with an edge of the game window
The code in Listing 8 (p. 237) checks for a collision with an edge of the game window.
If the sprite collides with an edge, the code in Listing 8 (p. 237) causes the sprite to wrap around and
reappear at the opposite edge, moving at the same speed in a dierent direction within the same quadrant
as before. In other words, if a sprite is moving down and to the right and collides with the right edge of the
window, it will reappear at the left edge, still moving down and to the right but not in exactly the same
direction down and to the right.
Listing 8
. Process a collision with an edge of the game window.
if(position.X < -image.Width){
position.X = windowSize.X;
NewDirection();
}//end if
if(position.X > windowSize.X){
position.X = -image.Width/2;
NewDirection();
}//end if
if(position.Y < -image.Height) {
position.Y = windowSize.Y;
NewDirection();
}//end if
if(position.Y > windowSize.Y){
position.Y = -image.Height / 2;
NewDirection();
}//end if on position.Y
}//end if on elapsed time
}//end Move
16.5.1.7.3.3 Modify the position and call the NewDirection method
In all cases shown in Listing 8 (p. 237) , if a collision occurs, the position of the sprite is modied to position
the sprite at the opposite edge. Then the method named NewDirection is called to modify the direction
pointed to by the direction vector.
The NewDirection method is declared private to prevent it from being accessible to code outside
the Sprite class because it has no meaning outside the Sprite class.
6 http://msdn.microsoft.com/en-us/library/bb194948.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
238
CHAPTER 16. XNA0128-IMPROVING THE SPRITE CLASS
16.5.1.7.4 The end of the Move method
Listing 8 (p. 237) signals the end of the Move
method.
16.5.1.8 The method named NewDirection
The method named NewDirection begins in Listing 9 (p.
Listing 9
238) .
. Beginning of the method named NewDirection.
private void NewDirection() {
double length = Math.Sqrt(
direction.X * direction.X +
direction.Y * direction.Y);
Boolean xNegative = (direction.X < 0)?true:false;
Boolean yNegative = (direction.Y < 0)?true:false;
16.5.1.8.1 The length of the direction vector and the signs of the components
Listing 9 (p. 238) begins by determining the length of the current direction vector along with the signs of
the X and Y components of the vector.
16.5.1.8.1.1 Compute the hypotenuse
The rst statement in the method in Listing 9 (p. 238) calls the Math.Sqrt 7 method and uses the
Pythagorean Theorem 8 to compute the length of the hypotenuse of the right triangle formed by the X
and Y components of the direction vector. This is the length of the direction vector.
16.5.1.8.1.2 Use the conditional operator
Then the last two statements in Listing 9 (p. 238) use the conditional operator
the components are negative. If so, the variables named xNegative and/or
9
to determine if the signs of
are set to true.
yNegative
16.5.1.8.2 Compute components of a new direction vector
Having accomplished that task, the code in Listing 10 (p. 238) computes the components for a new direction
vector of the same length with the X and Y components having random (but consistent) lengths and
the same signs as before.
16.5.1.8.2.1 Compute a new random value for the X component
For the code in Listing 10 (p. 238) to make any sense at all, you must know that the call to
NextDouble 10 returns a pseudo-random value, uniformly distributed between 0.0 and 1.0.
Listing 10
. Compute components of a new direction vector.
7 http://msdn.microsoft.com/en-us/library/system.math.sqrt.aspx
8 http://en.wikipedia.org/wiki/Pythagorean_theorem
9 http://msdn.microsoft.com/en-us/library/ty67wk28.aspx
10 http://msdn.microsoft.com/en-us/library/system.random.nextdouble.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
random
.
239
//Compute a new X component as a random portion of
// the vector length.
direction.X =
(float)(length * random.NextDouble());
//Compute a corresponding Y component that will
// keep the same vector length.
direction.Y = (float)Math.Sqrt(length*length direction.X*direction.X);
//Set the signs on the X and Y components to match
// the signs from the original direction vector.
if(xNegative)
direction.X = -direction.X;
if(yNegative)
direction.Y = -direction.Y;
}//end NewDirection
The rst statement in Listing 10 (p. 238) computes a new value for the X component of the current direction
vector, which is a random portion of the length of the current direction vector ranging from 0 to the full
length of the vector.
16.5.1.8.2.2 Compute a consistent value for the Y component
Then the second statement in Listing 10 (p. 238) uses the Sqrt method along with the Pythagorean
Theorem to compute a new value for the Y component, which when combined with the new X component
will produce a direction vector having the same length as before.
16.5.1.8.2.3 Adjust the signs of the X and Y components
Finally, the last two statements in Listing 10 (p. 238) use the information gleaned earlier to cause the signs
of the new X and Y components to match the signs of the original components.
16.5.1.8.2.4 A new direction vector with the same length in the same quadrant
By modifying the lengths of the X and Y components, the code in Listing 10 (p. 238) causes the direction
pointed to by the new vector to be dierent from the direction pointed to by the original direction vector.
By causing the X and Y components to have the same signs, the code in Listing 10 (p. 238) causes the
new direction vector to point into the same quadrant as before.
16.5.1.9 The Draw method
The Sprite.Draw method is shown in its entirety in Listing 11 (p.
Listing 11 . The Sprite.Draw method.
239) .
public void Draw(SpriteBatch spriteBatch) {
//Call the simplest available version of
// SpriteBatch.Draw
spriteBatch.Draw(image,position,Color.White);
}//end Draw method
//-------------------------------------------------//
}//end Sprite class
}//end namespace
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
240
CHAPTER 16. XNA0128-IMPROVING THE SPRITE CLASS
This Draw method is essentially the same as the Draw method that I explained in an earlier module so
it shouldn't require further explanation.
To avoid becoming confused, however, you should keep in mind that this program deals with the following
three methods having the name Draw :
• Overridden Game.Draw method.
• Sprite.Draw method.
• SpriteBatch.Draw method.
16.5.1.10 The end of the Sprite class
Listing 11 (p. 239) also signals the end of the
Sprite
class.
16.5.2 The Game1 class
The
Game1
class begins in Listing 12 (p. 240) .
Listing 12
. Beginning of the Game1 class.
namespace XNA0128Proj {
public class Game1 : Microsoft.Xna.Framework.Game {
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
//Use the following values to set the size of the
// client area of the game window. The actual window
// with its frame is somewhat larger depending on
// the OS display options. On my machine with its
// current display options, these dimensions
// produce a 1024x768 game window.
int windowWidth = 1017;
int windowHeight = 738;
//This is the length of the greatest distance in
// pixels that any sprite will move in a single
// frame of the game loop.
double maxVectorLength = 5.0;
//References to the space rocks are stored in this
// List object.
List<Sprite> rocks = new List<Sprite>();
int numRocks = 24;//Number of rocks.
//The following value should never exceed 60 moves
// per second unless the default frame rate is also
// increased to more than 60 frames per second.
double maxRockSpeed = 50;//moves per second
//References to the power pills are stored in
// this List.
List<Sprite> pills = new List<Sprite>();
int numPills = 12;//Number of pills.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
241
double maxPillSpeed = 40;//moves per second
//References to the UFOs are stored in this List.
List<Sprite> ufos = new List<Sprite>();
int numUfos = 6;//Max number of ufos
double maxUfoSpeed = 30;
//Random number generator. It is best to use a single
// object of the Random class to avoid the
// possibility of using different streams that
// produce the same sequence of values.
//Note that the random.NextDouble() method produces
// a pseudo-random value where the sequence of values
// is uniformly distributed between 0.0 and 1.0.
Random random = new Random();
16.5.2.1 Instance variables
Listing 12 (p. 240) declares several instance variables. Comments are provided to explain most of the
instance variables. No explanation beyond the comments in Listing 12 (p. 240) should be required.
16.5.2.2 The modied constructor
The constructor is shown in its entirety in Listing 13 (p. 241) .
Listing 13
. The modied constructor.
public Game1() {//constructor
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
//Set the size of the game window.
graphics.PreferredBackBufferWidth = windowWidth;
graphics.PreferredBackBufferHeight = windowHeight;
}//end constructor
Nothing in this constructor is new to this module. Therefore, no further explanation should be required.
16.5.2.3 The overridden LoadContent method
The overridden LoadContent method begins in Listing 14 (p.
Listing 14
241) .
. Beginning of the overridden LoadContent method.
protected override void LoadContent() {
spriteBatch = new SpriteBatch(GraphicsDevice);
for(int cnt = 0;cnt < numRocks;cnt++){
rocks.Add(new Sprite("Rock",Content,random));
//Set the position of the current rock at a
// random location within the game window.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
242
CHAPTER 16. XNA0128-IMPROVING THE SPRITE CLASS
rocks[cnt].Position = new Vector2(
(float)(windowWidth * random.NextDouble()),
(float)(windowHeight * random.NextDouble()));
//Get a direction vector for the current rock.
// Make both components positive to cause the
// vector to point down and to the right.
rocks[cnt].Direction = DirectionVector(
(float)maxVectorLength,
(float)(maxVectorLength * random.NextDouble()),
false,//xNeg
false);//yNeg
//Notify the Sprite object of the size of the
// game window.
rocks[cnt].WindowSize =
new Point(windowWidth,windowHeight);
//Set the speed in moves per second for the
// current sprite to a random value between
// maxRockSpeed/2 and maxRockSpeed.
rocks[cnt].Speed = maxRockSpeed/2
+ maxRockSpeed * random.NextDouble()/2;
}//end for loop
16.5.2.3.1 Instantiate all of the space rock sprites
The code in Listing 14 (p. 241) uses a for loop to instantiate all of the space rocks and to prepare them
to move from left to right, top to bottom across the game window.
16.5.2.3.1.1 Call the Sprite constructor
The for loop begins with a call to the Sprite
constructor to construct a new object of the
As mentioned earlier, you should pass a reference to the same
make a call to the Sprite constructor.
Each new
.
Sprite
Random
Sprite
class.
object each time you
object's reference is added to the list referred to by the instance variable named
rocks
16.5.2.3.1.2 Set the property values
Once the new Sprite object is constructed, the object's reference is accessed and used to set the following
property values:
Position property of each rock is set to a random position within the game window.
Direction property for each rock is set to a value obtained by calling the private DirectionVector method. (I will explain the DirectionVector method later.)
The WindowSize property for each rock is set to the common windowWidth and windowHeight
• The
• The
•
values.
• The Speed property for each rock is set to a bounded random value.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
243
Except for the call to the DirectionVector method, the code in Listing 14 (p. 241) is straightforward and
should not require an explanation beyond the embedded comments. I will explain the DirectionVector
method shortly.
16.5.2.3.2 Instantiate and set properties on the power pills and the UFOs
Listing 15 (p. 243) uses essentially the same code to instantiate and set the property values on the power
pill sprites and the UFO sprites.
Listing 15
. Instantiate and set properties on the power pills and the UFOs.
//Use the same process to instantiate all of the
// power pills and cause them to move from right
// to left, top to bottom.
for(int cnt = 0;cnt < numPills;cnt++) {
pills.Add(new Sprite("ball",Content,random));
pills[cnt].Position = new Vector2(
(float)(windowWidth * random.NextDouble()),
(float)(windowHeight * random.NextDouble()));
pills[cnt].Direction = DirectionVector(
(float)maxVectorLength,
(float)(maxVectorLength * random.NextDouble()),
true,//xNeg
false);//yNeg
pills[cnt].WindowSize =
new Point(windowWidth,windowHeight);
pills[cnt].Speed = maxPillSpeed/2
+ maxPillSpeed * random.NextDouble()/2;
}//end for loop
//Use the same process to instantiate all of the
// ufos and cause them to move from right to left,
// bottom to top.
for(int cnt = 0;cnt < numUfos;cnt++) {
ufos.Add(new Sprite("ufo",Content,random));
ufos[cnt].Position = new Vector2(
(float)(windowWidth * random.NextDouble()),
(float)(windowHeight * random.NextDouble()));
ufos[cnt].Direction = DirectionVector(
(float)maxVectorLength,
(float)(maxVectorLength * random.NextDouble()),
true,//xNeg
true);//yNeg
ufos[cnt].WindowSize =
new Point(windowWidth,windowHeight);
ufos[cnt].Speed = maxUfoSpeed/2
+ maxUfoSpeed * random.NextDouble()/2;
}//end for loop
}//end LoadContent
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
244
CHAPTER 16. XNA0128-IMPROVING THE SPRITE CLASS
16.5.2.4 The private DirectionVector method
The DirectionVector method shown in Listing 16 (p. 244) is called each time the code in Listing 14 (p.
241) and Listing 15 (p. 243) needs to set the Direction property on a sprite. This method is declared
private because it has no meaning outside the Sprite class.
Listing 16
. The private DirectionVector method.
private Vector2 DirectionVector(float vecLen,
float xLen,
Boolean negX,
Boolean negY){
Vector2 result = new Vector2(xLen,0);
result.Y = (float)Math.Sqrt(vecLen*vecLen
- xLen*xLen);
if(negX)
result.X = -result.X;
if(negY)
result.Y = -result.Y;
return result;
}//end DirectionVector
16.5.2.4.1 Return a direction vector
The DirectionVector method returns a direction vector as type Vector2
given the length of the vector,
the length of the X component of the vector, the sign of the X component, and the sign of the Y component.
16.5.2.4.2 The signs of the components
You should set negX and/or negY to true to cause them to be negative.
By adjusting the signs on the X and Y components, the vector can be caused to point into any of the
four quadrants. The relationships between these two values and the direction of motion of the sprite are as
follows:
•
•
•
•
false, false = down and to the right.
true, false = down and to the left
true, true = up and to the left
false, true = up and to the right
The code in Listing 16 (p. 244) is very similar to the code that I explained earlier in Listing 10 (p. 238) ,
so the code in Listing 16 (p. 244) should not require further explanation.
16.5.2.5 The overridden Update method
The overridden Update method is shown in its entirety in Listing 17 (p.
Listing 17
244) .
. Tell the sprites to move.
protected override void Update(GameTime gameTime) {
//Tell all the rocks in the list to move.
for(int cnt = 0;cnt < rocks.Count;cnt++) {
rocks[cnt].Move(gameTime);
}//end for loop
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
245
//Tell all the power pills in the list to move.
for(int cnt = 0;cnt < pills.Count;cnt++) {
pills[cnt].Move(gameTime);
}//end for loop
//Tell all the ufos in the list to move.
for(int cnt = 0;cnt < ufos.Count;cnt++) {
ufos[cnt].Move(gameTime);
}//end for loop
base.Update(gameTime);
}//end Update method
16.5.2.5.1 Very simple code
Because the code to instantiate the Sprite objects and set their properties was placed in the LoadContent
method in this program, the code in the overridden Update method is very simple.
The code in Listing 17 (p. 244) uses for loops to access the references and call the Move method on
every Sprite object.
16.5.2.5.2 To move or not to move, that is the question
As you learned earlier, when the Move method is called on an individual Sprite object, the sprite may
or it may not actually move depending on the value of its Speed property and the elapsed time since its
last actual move.
16.5.2.5.3 A characteristic of an object-oriented program
One of the characteristics of an object-oriented program is that the individual objects know how to behave
with minimal supervision. In eect, a call to the Sprite.Move method in Listing 17 (p. 244) tells the
object to make its own decision and to move if it is time for it to move.
16.5.2.6 The overridden Game.Draw method
Listing 18 (p. 245) shows the overridden Game.Draw
Listing 18
method in its entirety.
. The overridden Game.Draw method.
protected override void Draw(GameTime gameTime) {
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
//Draw all rocks.
for(int cnt = 0;cnt < rocks.Count;cnt++) {
rocks[cnt].Draw(spriteBatch);
}//end for loop
//Draw all power pills.
for(int cnt = 0;cnt < pills.Count;cnt++) {
pills[cnt].Draw(spriteBatch);
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
246
CHAPTER 16. XNA0128-IMPROVING THE SPRITE CLASS
}//end for loop
//Draw all ufos.
for(int cnt = 0;cnt < ufos.Count;cnt++) {
ufos[cnt].Draw(spriteBatch);
}//end for loop
spriteBatch.End();
base.Draw(gameTime);
}//end Draw method
//-------------------------------------------------//
}//end class
}//end namespace
16.5.2.6.1 Erase and redraw the entire game window
Listing 18 (p. 245) begins by painting over everything in the game window with CornowerBlue pixels. Then
it uses for loops to access and call the Sprite.Draw method on every Sprite object.
Each call to a Sprite object's Draw method in Listing 18 (p. 245) is a notication to the Sprite
object that it should cause itself to be redrawn in the appropriate position with the appropriate image in
the game window.
This is another manifestation of an object knowing how to behave with minimal supervision. The
overridden Game.Draw method doesn't know and doesn't care where the Sprite object should be
positioned or what image it should draw to represent itself. The Game.Draw method simply knows
that every Sprite object must redraw itself at the appropriate position with the appropriate image
at this point in time. Decisions regarding position and image are left up to the Sprite object.
Regardless of whether or not a sprite has decided to move, it should cause itself to be redrawn in the game
window because its image has just been replaced by a bunch of CornowerBlue pixels.
16.5.3 Not the same approach as some other game engines
Some game engines take a dierent approach and in the interest of speed, redraw only those portions of the
game window that have changed. This can lead to a great deal of complexity and it is often the responsibility
of the game programmer to manage that complexity.
That is not the case with XNA. From the viewpoint of the XNA game programmer, the entire game
window is redrawn once during every iteration of the game loop. This causes XNA to be easier to program
than some other game engines. On the other hand, it might be argued that XNA sacrices speed for
simplicity.
16.5.4 The end of the program
Listing 18 (p. 245) also signals the end of the overridden
class, and the end of the program.
Game.Draw
method, the end of the
Game1
16.6 Run the program
I encourage you to copy the code from Listing 19 (p. 248) and Listing 20 (p. 252) . Use that code to create
an XNA project. (You should be able to use any small image les as sprites.) Compile and run the project.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
247
Experiment with the code, making changes, and observing the results of your changes. Make certain that
you can explain why your changes behave as they do.
16.7 Run my program
Click here
11
to download a zip le containing my version of the program. Extract the folder named
XNA0128Proj from the zip le and save it somewhere on your disk. Start Visual C# 2010 Express
and select Open Project... from the File menu. Navigate to the project folder and select the le with
the extension of .sln . This should cause the project to open and be ready to run or debug as described
in the earlier module titled Getting Started
12
.
16.8 Summary
In this module, you learned how to add more sophistication to the
earlier module.
Sprite
class that was introduced in an
16.9 What's next?
We're getting very close to being able to write a 2D arcade style game involving UFOs, space rocks, and
power pills. There are just a few more tools that we need and I will show you how to create those tools in
the upcoming modules.
The critical tools are:
• The ability to detect collisions between sprites.
• The ability to control the movement of one or more sprites using the keyboard or the mouse.
Once we have those tools, we can write a game where the challenge is to cause the UFO to successfully
navigate across the game window without colliding with a space rock, so I will concentrate on those two
tools in the next few modules. (There are probably other games that we could also create using those tools.)
Beyond that, there are several other tools that will make it possible for us to create more sophisticated
and interesting games:
• The ability to play sounds.
• The ability to create onscreen text.
• The ability to create a game with multiple levels, increasing diculty at each level, scorekeeping, etc.
I may show you how to create some of those tools later in this series of modules. On the other hand, I may
decide to leave that as an exercise for the student.
16.10 Miscellaneous
This section contains a variety of miscellaneous information.
Housekeeping material
• Module name: Xna0128-Improving the Sprite Class
• File: Xna0128.htm
• Published: 02/28/14
11 http://cnx.org/content/m49527/latest/XNA0128Proj.zip
12 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
248
CHAPTER 16. XNA0128-IMPROVING THE SPRITE CLASS
Disclaimers: Financial : Although the Connexions site makes it possible for you to download
a PDF le for this module at no charge, and also makes it possible for you to purchase a pre-printed
version of the PDF le, you should be aware that some of the HTML elements in this module may
not translate well into PDF.
I also want you to know that, I receive no nancial compensation from the Connexions website even
if you purchase the PDF version of the module.
In the past, unknown individuals have copied my modules from cnx.org, converted them to Kindle
books, and placed them for sale on Amazon.com showing me as the author. I neither receive
compensation for those sales nor do I know who does receive compensation. If you purchase such
a book, please be aware that it is a copy of a module that is freely available on cnx.org and that it
was made and published without my prior knowledge.
Aliation :: I am a professor of Computer Information Technology at Austin Community College
in Austin, TX.
16.11 Complete program listing
Complete listings of the XNA program les discussed in this module are provided in Listing 19 (p. 248) and
Listing 20 (p. 252) .
Listing 19
. The class named Sprite for the project named XNA0128Proj.
/*Project XNA0128Proj
* This file defines a Sprite class from which a Sprite
* object can be instantiated.
*******************************************************/
using
using
using
using
System;
Microsoft.Xna.Framework;
Microsoft.Xna.Framework.Content;
Microsoft.Xna.Framework.Graphics;
namespace XNA0128Proj {
class Sprite {
private Texture2D image;
private Vector2 position = new Vector2(0,0);
private Vector2 direction = new Vector2(0,0);
private Point windowSize;
private Random random;
double elapsedTime;//in milliseconds
//The following value is the inverse of speed in
// moves/msec expressed in msec/move.
double elapsedTimeTarget;
//-------------------------------------------------//
//Position property accessor
public Vector2 Position {
get {
return position;
}
set {
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
249
position = value;
}//end set
}//end Position property accessor
//-------------------------------------------------//
//WindowSize property accessor
public Point WindowSize {
set {
windowSize = value;
}//end set
}//end WindowSize property accessor
//-------------------------------------------------//
//Direction property accessor
public Vector2 Direction {
get {
return direction;
}
set {
direction = value;
}//end set
}//end Direction property accessor
//-------------------------------------------------//
//Speed property accessor. The set side should be
// called with speed in moves/msec. The get side
// returns speed moves/msec.
public double Speed {
get {
//Convert from elapsed time in msec/move to
// speed in moves/msec.
return elapsedTimeTarget/1000;
}
set {
//Convert from speed in moves/msec to
// elapsed time in msec/move.
elapsedTimeTarget = 1000/value;
}//end set
}//end Speed property accessor
//-------------------------------------------------//
//This constructor loads an image for the sprite
// when it is instantiated. Therefore, it requires
// an asset name for the image and a reference to a
// ContentManager object.
//Requires a reference to a Random object. Should
// use the same Random object for all sprites to
// avoid getting the same sequence for different
// sprites.
public Sprite(String assetName,
ContentManager contentManager,
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
250
CHAPTER 16. XNA0128-IMPROVING THE SPRITE CLASS
Random random) {
image = contentManager.Load<Texture2D>(assetName);
this.random = random;
}//end constructor
//-------------------------------------------------//
//This method can be called to load a new image
// for the sprite.
public void SetImage(String assetName,
ContentManager contentManager) {
image = contentManager.Load<Texture2D>(assetName);
}//end SetImage
//-------------------------------------------------//
//This method causes the sprite to move in the
// direction of the direction vector if the elapsed
// time since the last move exceeds the elapsed
// time target based on the specified speed.
public void Move(GameTime gameTime) {
//Accumulate elapsed time since the last move.
elapsedTime +=
gameTime.ElapsedGameTime.Milliseconds;
if(elapsedTime > elapsedTimeTarget){
//It's time to make a move. Set the elapsed
// time to a value that will attempt to produce
// the specified speed on the average.
elapsedTime -= elapsedTimeTarget;
//Add the direction vector to the position
// vector to get a new position vector.
position = Vector2.Add(position,direction);
//Check for a collision with an edge of the game
// window. If the sprite reaches an edge, cause
// the sprite to wrap around and reappear at the
// other edge, moving at the same speed in a
// different direction within the same quadrant
// as before.
if(position.X < -image.Width){
position.X = windowSize.X;
NewDirection();
}//end if
if(position.X > windowSize.X){
position.X = -image.Width/2;
NewDirection();
}//end if
if(position.Y < -image.Height) {
position.Y = windowSize.Y;
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
251
NewDirection();
}//end if
if(position.Y > windowSize.Y){
position.Y = -image.Height / 2;
NewDirection();
}//end if on position.Y
}//end if on elapsed time
}//end Move
//-------------------------------------------------//
//This method determines the length of the current
// direction vector along with the signs of the X
// and Y components of the current direction vector.
// It computes a new direction vector of the same
// length with the X and Y components having random
// lengths and the same signs.
//Note that random.NextDouble returns a
// pseudo-random value, uniformly distributed
// between 0.0 and 1.0.
private void NewDirection() {
//Get information about the current direction
// vector.
double length = Math.Sqrt(
direction.X * direction.X +
direction.Y * direction.Y);
Boolean xNegative = (direction.X < 0)?true:false;
Boolean yNegative = (direction.Y < 0)?true:false;
//Compute a new X component as a random portion of
// the vector length.
direction.X =
(float)(length * random.NextDouble());
//Compute a corresponding Y component that will
// keep the same vector length.
direction.Y = (float)Math.Sqrt(length*length direction.X*direction.X);
//Set the signs on the X and Y components to match
// the signs from the original direction vector.
if(xNegative)
direction.X = -direction.X;
if(yNegative)
direction.Y = -direction.Y;
}//end NewDirection
//-------------------------------------------------//
public void Draw(SpriteBatch spriteBatch) {
//Call the simplest available version of
// SpriteBatch.Draw
spriteBatch.Draw(image,position,Color.White);
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
252
CHAPTER 16. XNA0128-IMPROVING THE SPRITE CLASS
}//end Draw method
//-------------------------------------------------//
}//end class
}//end namespace
Listing 20
. The class named Game1 for the project named XNA0128Proj.
/*Project XNA0128Proj
* This project demonstrates how to integrate space
* rocks, power pills, and ufos in a program using
* objects of a Sprite class. This could be the
* beginnings of a space game.
* *****************************************************/
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using XNA0128Proj;
namespace XNA0128Proj {
public class Game1 : Microsoft.Xna.Framework.Game {
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
//Use the following values to set the size of the
// client area of the game window. The actual window
// with its frame is somewhat larger depending on
// the OS display options. On my machine with its
// current display options, these dimensions
// produce a 1024x768 game window.
int windowWidth = 1017;
int windowHeight = 738;
//This is the length of the greatest distance in
// pixels that any sprite will move in a single
// frame of the game loop.
double maxVectorLength = 5.0;
//References to the space rocks are stored in this
// List object.
List<Sprite> rocks = new List<Sprite>();
int numRocks = 24;//Number of rocks.
//The following value should never exceed 60 moves
// per second unless the default frame rate is also
// increased to more than 60 frames per second.
double maxRockSpeed = 50;//frames per second
//References to the power pills are stored in
// this List.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
253
List<Sprite> pills = new List<Sprite>();
int numPills = 12;//Number of pills.
double maxPillSpeed = 40;//moves per second
//References to the UFOs are stored in this List.
List<Sprite> ufos = new List<Sprite>();
int numUfos = 6;//Max number of ufos
double maxUfoSpeed = 30;
//Random number generator. It is best to use a single
// object of the Random class to avoid the
// possibility of using different streams that
// produce the same sequence of values.
//Note that the random.NextDouble() method produces
// a pseudo-random value where the sequence of values
// is uniformly distributed between 0.0 and 1.0.
Random random = new Random();
//-------------------------------------------------//
public Game1() {//constructor
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
//Set the size of the game window.
graphics.PreferredBackBufferWidth = windowWidth;
graphics.PreferredBackBufferHeight = windowHeight;
}//end constructor
//-------------------------------------------------//
protected override void Initialize() {
//No initialization required.
base.Initialize();
}//end Initialize
//-------------------------------------------------//
protected override void LoadContent() {
spriteBatch = new SpriteBatch(GraphicsDevice);
//Instantiate all of the rocks and cause them to
// move from left to right, top to
// bottom. Pass a reference to the same Random
// object to all of the sprites.
for(int cnt = 0;cnt < numRocks;cnt++){
rocks.Add(new Sprite("Rock",Content,random));
//Set the position of the current rock at a
// random location within the game window.
rocks[cnt].Position = new Vector2(
(float)(windowWidth * random.NextDouble()),
(float)(windowHeight * random.NextDouble()));
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
254
CHAPTER 16. XNA0128-IMPROVING THE SPRITE CLASS
//Get a direction vector for the current rock.
// Make both components positive to cause the
// vector to point down and to the right.
rocks[cnt].Direction = DirectionVector(
(float)maxVectorLength,
(float)(maxVectorLength * random.NextDouble()),
false,//xNeg
false);//yNeg
//Notify the Sprite object of the size of the
// game window.
rocks[cnt].WindowSize =
new Point(windowWidth,windowHeight);
//Set the speed in moves per second for the
// current sprite to a random value between
// maxRockSpeed/2 and maxRockSpeed.
rocks[cnt].Speed = maxRockSpeed/2
+ maxRockSpeed * random.NextDouble()/2;
}//end for loop
//Use the same process to instantiate all of the
// power pills and cause them to move from right
// to left, top to bottom.
for(int cnt = 0;cnt < numPills;cnt++) {
pills.Add(new Sprite("ball",Content,random));
pills[cnt].Position = new Vector2(
(float)(windowWidth * random.NextDouble()),
(float)(windowHeight * random.NextDouble()));
pills[cnt].Direction = DirectionVector(
(float)maxVectorLength,
(float)(maxVectorLength * random.NextDouble()),
true,//xNeg
false);//yNeg
pills[cnt].WindowSize =
new Point(windowWidth,windowHeight);
pills[cnt].Speed = maxPillSpeed/2
+ maxPillSpeed * random.NextDouble()/2;
}//end for loop
//Use the same process to instantiate all of the
// ufos and cause them to move from right to left,
// bottom to top.
for(int cnt = 0;cnt < numUfos;cnt++) {
ufos.Add(new Sprite("ufo",Content,random));
ufos[cnt].Position = new Vector2(
(float)(windowWidth * random.NextDouble()),
(float)(windowHeight * random.NextDouble()));
ufos[cnt].Direction = DirectionVector(
(float)maxVectorLength,
(float)(maxVectorLength * random.NextDouble()),
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
255
true,//xNeg
true);//yNeg
ufos[cnt].WindowSize =
new Point(windowWidth,windowHeight);
ufos[cnt].Speed = maxUfoSpeed/2
+ maxUfoSpeed * random.NextDouble()/2;
}//end for loop
}//end LoadContent
//-------------------------------------------------//
//This method returns a direction vector given the
// length of the vector, the length of the
// X component, the sign of the X component, and the
// sign of the Y component. Set negX and/or negY to
// true to cause them to be negative. By adjusting
// the signs on the X and Y components, the vector
// can be caused to point into any of the four
// quadrants.
private Vector2 DirectionVector(float vecLen,
float xLen,
Boolean negX,
Boolean negY){
Vector2 result = new Vector2(xLen,0);
result.Y = (float)Math.Sqrt(vecLen*vecLen
- xLen*xLen);
if(negX)
result.X = -result.X;
if(negY)
result.Y = -result.Y;
return result;
}//end DirectionVector
//-------------------------------------------------//
protected override void UnloadContent() {
//No content unload required.
}//end unloadContent
//-------------------------------------------------//
protected override void Update(GameTime gameTime) {
//Tell all the rocks in the list to move.
for(int cnt = 0;cnt < rocks.Count;cnt++) {
rocks[cnt].Move(gameTime);
}//end for loop
//Tell all the power pills in the list to move.
for(int cnt = 0;cnt < pills.Count;cnt++) {
pills[cnt].Move(gameTime);
}//end for loop
//Tell all the ufos in the list to move.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
256
CHAPTER 16. XNA0128-IMPROVING THE SPRITE CLASS
for(int cnt = 0;cnt < ufos.Count;cnt++) {
ufos[cnt].Move(gameTime);
}//end for loop
base.Update(gameTime);
}//end Update method
//-------------------------------------------------//
protected override void Draw(GameTime gameTime) {
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
//Draw all rocks.
for(int cnt = 0;cnt < rocks.Count;cnt++) {
rocks[cnt].Draw(spriteBatch);
}//end for loop
//Draw all power pills.
for(int cnt = 0;cnt < pills.Count;cnt++) {
pills[cnt].Draw(spriteBatch);
}//end for loop
//Draw all ufos.
for(int cnt = 0;cnt < ufos.Count;cnt++) {
ufos[cnt].Draw(spriteBatch);
}//end for loop
spriteBatch.End();
base.Draw(gameTime);
}//end Draw method
//-------------------------------------------------//
}//end class
}//end namespace
-end-
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
Chapter 17
1
Xna0130-Collision Detection
Revised: Mon May 09 18:37:13 CDT 2016
This page is part of a Book titled XNA Game Studio
2
.
17.1 Table of Contents
• Table of Contents (p. 257)
• Preface (p. 258)
· Viewing tip (p. 259)
* Figures (p. 259)
* Listings (p. 259)
• General background information (p. 259)
· Another improvement (p. 259)
· Denition of a collision (p. 259)
• Preview (p. 259)
· An epic battle (p. 260)
* Spider and ladybug motion (p. 260)
* The spiders don't die easily (p. 260)
* The death blow (p. 260)
· Output screen images (p. 260)
* The top image (p. 260)
* The middle image (p. 262)
* The bottom image (p. 262)
• Discussion and sample code (p. 262)
· The Sprite class (p. 262)
* A new Image property accessor method (p. 262)
* A modied constructor (p. 262)
1 This content is available online at <http://cnx.org/content/m49524/1.2/>.
2 http://cnx.org/contents/GY804-eY
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
257
258
CHAPTER 17. XNA0130-COLLISION DETECTION
* A modied SetImage method (p. 263)
* A new method named GetRectangle (p. 263)
* A new method named IsCollision (p. 263)
·
·
·
·
·
·
·
What is a collision? (p. 263)
What is the overall behavior? (p. 263)
The IsCollision method code (p. 264)
Test for a collision with other sprites (p. 264)
A while loop (p. 264)
The rst return statement (p. 264)
The second return statement (p. 265)
* The end of the Sprite class (p. 265)
· The Game1 class (p. 265)
* The overridden LoadContent method (p. 265)
· A background sprite with a spider web image (p. 265)
· Instantiate the spider and ladybug Sprite objects (p. 265)
* The overridden Update method (p. 267)
·
·
·
·
·
·
·
·
·
·
A for loop that controls the collision testing (p. 267)
Not every spider is tested during each iteration (p. 267)
Test for a collision (p. 267)
Return a reference to a spider or null (p. 268)
If a collision was detected... (p. 268)
Why change the spider's position? (p. 268)
Spiders don't die easily (p. 268)
The Name property (p. 269)
Killing the brown spider (p. 269)
Is a Dispose Method needed? (p. 269)
· The end of the Update method (p. 269)
* The overridden Game.Draw method (p. 269)
· The end of the program (p. 270)
•
•
•
•
•
•
Run the program (p. 270)
Run my program (p. 270)
Summary (p. 271)
What's next? (p. 271)
Miscellaneous (p. 271)
Complete program listing (p. 272)
17.2 Preface
This module is one in a collection of modules designed primarily for teaching GAME 1343 Game and
Simulation Programming I at Austin Community College in Austin, TX. These modules are intended
to supplement and not to replace the textbook.
An earlier module titled Getting Started 3 provided information on how to get started programming with
Microsoft's XNA Game Studio.
3 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
259
17.2.1 Viewing tip
I recommend that you open another copy of this module in a separate browser window and use the following
links to easily nd and view the Figures and Listings while you are reading about them.
17.2.1.1 Figures
• Figure 1 (p. 260) . Screen output at three dierent times while the program was running.
17.2.1.2 Listings
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
Listing
1 (p. 262) . A new Image property accessor method in the Sprite class.
2 (p. 262) . A modied constructor.
3 (p. 263) . A modied SetImage method.
4 (p. 263) . A new method named GetRectangle.
5 (p. 264) . Beginning of a new IsCollision method.
6 (p. 264) . Test for a collision with other sprites.
7 (p. 265) . Beginning of the overridden LoadContent method of the Game1 class.
8 (p. 265) . Instantiate the spider and ladybug Sprite objects.
9 (p. 267) . Beginning of the overridden Update method.
10 (p. 267) . Beginning of a for loop that controls the collision testing.
11 (p. 267) . Test for a collision.
12 (p. 268) . Spiders don't die easily.
13 (p. 269) . The overridden Game.Draw method.
14 (p. 272) . The Sprite class for the project named XNA0130Proj.
15 (p. 276) . The Game1 class for the project named XNA0130Proj.
17.3 General background information
While studying the past couple of modules, you have learned how to create and then to improve a class
named Sprite from which you can instantiate objects that behave as sprites.
17.3.1 Another improvement
In this module, we will once again improve the Sprite class, this time adding the capability for a sprite
to determine if it has collided with another sprite. This capability is critical for game development in many
areas.
17.3.2 Denition of a collision
Our denition of a collision is based on the bounding rectangles for the images that represent the two sprites.
If the rectangles intersect in their current positions, a collision is deemed to have occurred. If they don't
intersect, there is no collision.
17.4 Preview
This program is not yet a game because it doesn't provide player interaction. Instead it is a demonstration
program that demonstrates sprite collision detection is a rather interesting way.
Figure 1 (p. 260) shows a screen snapshot at three dierent points in time while the program was running.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
260
CHAPTER 17. XNA0130-COLLISION DETECTION
17.4.1 An epic battle
The demonstration program chronicles a battle between spiders and ladybugs. When the program starts,
there are 200 black widow spiders and ve ladybugs on the web in the game window. (See the top image in
Figure 1 (p. 260) .)
17.4.1.1 Spider and ladybug motion
The spiders move at dierent speeds in dierent directions but generally toward the southeast. When a
spider goes outside the game window on the right side or the bottom, it reappears on the left side or the
top.
The ladybugs also move at dierent speeds in dierent directions but generally toward the northwest.
When a ladybug goes outside the game window on the left side or the top, it reappears on the right side or
on the bottom.
17.4.1.2 The spiders don't die easily
When a ladybug collides with a black widow spider, the spider disappears but is reincarnated as a green
spider 128 pixels to the right of its original position.
When a ladybug collides with a green spider, the spider disappears but is reincarnated again as a small
brown spider 128 pixels to the right of its original position.
17.4.1.3 The death blow
Finally, when a ladybug collides with a brown spider, the spider is eaten and is removed from the population of
spiders. Therefore, all of the spiders eventually disappear and the ladybugs continue marching on victorious.
17.4.2 Output screen images
17.4.2.1 The top image
The top image in Figure 1 (p. 260) was taken shortly after the program began running. Therefore, the game
window was mostly populated with ladybugs and black widow spiders. There are a few green spiders and I
believe I see one brown spider at the very bottom border near the right side.
Figure 1
. Screen output at three dierent times while the program was running.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
261
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
262
CHAPTER 17. XNA0130-COLLISION DETECTION
17.4.2.2 The middle image
The middle image in Figure 1 (p. 260) shows the program output after the program has been running for
awhile. At this point in time, the game window is mainly populated with ladybugs, green spiders, and brown
spiders. However, there are a few black widow spiders still in the picture.
17.4.2.3 The bottom image
The bottom image shows the program output at an even later point in time. At this point, the game window
is populated with ladybugs, one green spider and a few brown spiders. Given enough time and a little luck,
the ladybugs will collide with and destroy even these remaining spiders.
17.5 Discussion and sample code
As usual, I will discuss the program in fragments, beginning with the Sprite class. Furthermore, I will
discuss only those portions of the Sprite class that are dierent from the versions of the Sprite class
that I explained in earlier modules.
A complete listing of the Sprite class is provided in Listing 14 (p. 272) and a complete listing of the
Game1 class is provided in Listing 15 (p. 276) near the end of the module.
17.5.1 The Sprite class
17.5.1.1 A new Image property accessor method
Scanning down the Sprite class denition in Listing 14 (p. 272) , the rst thing that we nd that is new
to this version is a new read-only Image property accessor method. The method is shown in Listing 1 (p.
262) .
Listing 1
. A new Image property accessor method in the Sprite class.
//Image property accessor - new to this version.
public Texture2D Image {
get {
return image;
}//end get
}//end Image property accessor
There is nothing unusual about this property accessor method, so it shouldn't require further explanation.
You will learn why it is needed later when I explain the Game1 class.
17.5.1.2 A modied constructor
The second statement from the end in Listing 2 (p. 262) was added to the constructor from the previous
version of the Sprite class.
Listing 2
. A modied constructor.
public Sprite(String assetName,
ContentManager contentManager,
Random random) {
image = contentManager.Load<Texture2D>(assetName);
image.Name = assetName;
this.random = random;
}//end constructor
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
263
The new statement assigns the assetName to the Name property of the Texture2D object that
represents the sprite's image. You will see why this modication was needed later.
17.5.1.3 A modied SetImage method
As with the modied constructor, the last statement in Listing 3 (p. 263) was added to the
method from the previous version of the Sprite class.
Listing 3
SetImage
. A modied SetImage method.
public void SetImage(String assetName,
ContentManager contentManager) {
image = contentManager.Load<Texture2D>(assetName);
image.Name = assetName;
}//end SetImage
Once again, the new statement assigns the assetName to the Name property of the Texture2D
object that represents the sprite's image. You will also see why this modication was needed later.
17.5.1.4 A new method named GetRectangle
Listing 4 (p. 263) shows a new method named GetRectangle
Listing 4
.
. A new method named GetRectangle.
public Rectangle GetRectangle() {
return new Rectangle((int)(position.X),
(int)(position.Y),
image.Width,
image.Height);
}//end GetRectangle
This method returns the current rectangle occupied by the sprite's image as type Rectangle . This
rectangle is needed for the collision detection process. The code in Listing 4 (p. 263) is straightforward and
shouldn't require further explanation.
17.5.1.5 A new method named IsCollision
This version of the Sprite class denes a new method named IsCollision
method is to detect a collision between this sprite and some other sprite.
. The purpose of this new
17.5.1.5.1 What is a collision?
A collision is called if the rectangle containing this
a target sprite's image.
Sprite
object's image intersects the rectangle containing
17.5.1.5.2 What is the overall behavior?
This method receives a list of Sprite objects as an incoming parameter.
It tests for a collision with each
sprite in the list beginning with the sprite at the head of the list. If it detects a collision with a sprite, it
stops testing immediately and returns a reference to the Sprite object for which it found the collision. If
it doesn't nd a collision with any sprite in the list, it returns null.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
264
CHAPTER 17. XNA0130-COLLISION DETECTION
17.5.1.5.3 The IsCollision method code
The new method named IsCollision begins in Listing 5 (p.
Listing 5
264) .
. Beginning of a new IsCollision method.
public Sprite IsCollision(List<Sprite> target) {
Rectangle thisRectangle =
new Rectangle((int)(position.X),
(int)(position.Y),
image.Width,
image.Height);
The code in Listing 5 (p. 264) constructs a new Rectangle object that describes the rectangular area currently occupied by the sprite's image relative to the upper left corner of the game window. The Rectangle
object's reference is saved in the local variable named thisRectangle .
17.5.1.5.3.1 Test for a collision with other sprites
Listing 6 (p. 264) begins by declaring a pair of local variables named targetRectangle and cnt .
The variable named targetRectangle will be used to store a reference to a rectangular area that
currently contains a target sprite. The variable named cnt will be used as a loop counter.
Listing 6
. Test for a collision with other sprites.
Rectangle targetRectangle;
int cnt = 0;
while(cnt < target.Count){
targetRectangle = target[cnt].GetRectangle();
if(thisRectangle.Intersects(targetRectangle)){
return target[cnt];
}//end if
cnt++;
}//end while loop
return null;//no collision detected
}//end IsCollision
17.5.1.5.3.2 A while loop
Then Listing 6 (p. 264) executes a
while
loop that:
• Calls the GetRectangle method on the next Sprite object in the list (see Listing 4 (p. 263) ).
• Tests to determine if this sprite's rectangle intersects the target sprite's rectangle.
• Returns a reference to the target Sprite object if a collision is detected; keeps looping otherwise.
17.5.1.5.3.3 The rst return statement
Executing a return statement in the middle of the while
the method.
loop terminates the loop and also terminates
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
265
17.5.1.5.3.4 The second return statement
If no collision is detected, the loop terminates when all of the Sprite objects in the list have been tested.
At that point, Listing 6 (p. 264) executes a dierent return statement to return null and terminate
the method. The null value is returned in place of a reference to a Sprite object to signal that no
collision was detected.
17.5.1.6 The end of the Sprite class
Listing 6 (p. 264) signals the end of the IsCollision method, which is also the end of the changes made
to the Sprite class. Once again, you will nd a complete listing of the new Sprite class in Listing 14 (p.
272) near the end of the module.
17.5.2 The Game1 class
I will explain only those portions of the Game1 class that are substantially new to this program. You will
nd a complete listing of the Game1 class in Listing 15 (p. 276) near the end of the module.
17.5.2.1 The overridden LoadContent method
The only code that is substantially new prior to the LoadContent method is the declaration of an instance
variable of type Sprite named spiderWeb . Therefore, I will skip down and begin my discussion with
the overridden LoadContent method, which begins in Listing 7 (p. 265) .
Listing 7
. Beginning of the overridden LoadContent method of the Game1 class.
protected override void LoadContent() {
spriteBatch = new SpriteBatch(GraphicsDevice);
//Create a sprite for the background image.
spiderWeb =
new Sprite("spiderwebB",Content,random);
spiderWeb.Position = new Vector2(0f,0f);
17.5.2.1.1 A background sprite with a spider web image
The last two statements in Listing 7 (p. 265) instantiate a new Sprite object and load it with an image
of the spider web shown in Figure 1 (p. 260) .
The purpose of this sprite is to serve as a background image. Before adding the image of the spider web
to the Content folder during the design phase, I used an external program to scale the image to the same
size as the game window established by the constructor in Listing 15 (p. 276) .
The code in Listing 7 (p. 265) positions the upper left corner of the sprite at the upper left corner of the
game window so that it just lls the game window as shown in Figure 1 (p. 260) .
17.5.2.1.2 Instantiate the spider and ladybug Sprite objects
The code in Listing 8 (p. 265) instantiates all of the spider and ladybug
properties.
Listing 8
Sprite
. Instantiate the spider and ladybug Sprite objects.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
objects and sets their
266
CHAPTER 17. XNA0130-COLLISION DETECTION
//Instantiate all of the spiders and cause them to
// move from left to right, top to
// bottom. Pass a reference to the same Random
// object to all of the sprites.
for(int cnt = 0;cnt < numSpiders;cnt++) {
spiders.Add(
new Sprite("blackWidowSpider",Content,random));
//Set the position of the current spider at a
// random location within the game window.
spiders[cnt].Position = new Vector2(
(float)(windowWidth * random.NextDouble()),
(float)(windowHeight * random.NextDouble()));
//Get a direction vector for the current spider.
// Make both components positive to cause the
// vector to point down and to the right.
spiders[cnt].Direction = DirectionVector(
(float)maxVectorLength,
(float)(maxVectorLength * random.NextDouble()),
false,//xNeg
false);//yNeg
//Notify the spider object of the size of the
// game window.
spiders[cnt].WindowSize =
new Point(windowWidth,windowHeight);
//Set the speed in moves per second for the
// current spider to a random value between
// maxSpiderSpeed/2 and maxSpiderSpeed.
spiders[cnt].Speed = maxSpiderSpeed / 2
+ maxSpiderSpeed * random.NextDouble() / 2;
}//end for loop
//Use the same process to instantiate all of the
// ladybugs and cause them to move from right to
// left, bottom to top.
for(int cnt = 0;cnt < numLadybugs;cnt++) {
ladybugs.Add(
new Sprite("ladybug",Content,random));
ladybugs[cnt].Position = new Vector2(
(float)(windowWidth * random.NextDouble()),
(float)(windowHeight * random.NextDouble()));
ladybugs[cnt].Direction = DirectionVector(
(float)maxVectorLength,
(float)(maxVectorLength * random.NextDouble()),
true,//xNeg
true);//yNeg
ladybugs[cnt].WindowSize =
new Point(windowWidth,windowHeight);
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
267
ladybugs[cnt].Speed = maxLadybugSpeed / 2
+ maxLadybugSpeed * random.NextDouble() / 2;
}//end for loop
}//end LoadContent
The code in Listing 8 (p. 265) is essentially the same as code that I explained in an earlier module so no
explanation beyond the embedded comments should be necessary.
17.5.2.2 The overridden Update method
The overridden Update method begins in Listing 9 (p.
Listing 9
267) .
. Beginning of the overridden Update method.
protected override void Update(GameTime gameTime) {
//Tell all the spiders in the list to move.
for(int cnt = 0;cnt < spiders.Count;cnt++) {
spiders[cnt].Move(gameTime);
}//end for loop
//Tell all the ladybugs in the list to move.
for(int cnt = 0;cnt < ladybugs.Count;cnt++) {
ladybugs[cnt].Move(gameTime);
}//end for loop
The code in Listing 9 (p. 267) is essentially the same as code that I have explained in earlier modules, so no
further explanation should be necessary.
17.5.2.2.1 A for loop that controls the collision testing
Listing 10 (p. 267) shows the beginning of a for loop that causes each ladybug Sprite
object to test for
a collision with the spiders in the list of spiders once during each iteration of the game loop.
If a ladybug detects a collision with a spider, the action described earlier (p. 259) is taken.
Listing 10
. Beginning of a for loop that controls the collision testing.
for(int cnt = 0;cnt < ladybugs.Count;cnt++) {
17.5.2.2.1.1 Not every spider is tested during each iteration
If a ladybug detects a collision with a spider, the remaining spiders in the list are not tested by that ladybug
during that iteration of the game loop. In other words, if a ladybug's rectangle intersects the rectangles
belonging to two or more spiders, only the spider closest to the top of the list will register a collision.
17.5.2.2.1.2 Test for a collision
The code that tests for a collision is shown in Listing 11 (p. 267) .
Listing 11
. Test for a collision.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
268
CHAPTER 17. XNA0130-COLLISION DETECTION
//Test for a collision between this ladybug and
// all of the spiders in the list of spiders.
Sprite target =
ladybugs[cnt].IsCollision(spiders);
if(target != null) {
//There was a collision. Cause the spider to
// move 128 pixels to the right.
target.Position =
new Vector2(target.Position.X + 128,
target.Position.Y);
The rst statement in Listing 11 (p. 267) calls the IsCollision method on the current ladybug object,
passing a reference to the list of spiders as a parameter. As you learned earlier, this will cause the ladybug
object to test each spider in the list for a collision until either a collision is found or the list is exhausted.
17.5.2.2.1.3 Return a reference to a spider or null
When the IsCollision method returns and control moves to the beginning of the if statement, the
variable named target will either contain null , (meaning that no collision was detected), or will contain
a reference to the spider object involved in a collision.
17.5.2.2.1.4 If a collision was detected...
The body of the if statement shown in Listing 11 (p. 267) is executed if a collision was detected and the
target variable does not contain null. In this case, the X component of the spider's position vector is
increased by a value of 128. This will cause the spider to move 128 pixels to the right the next time it is
drawn.
17.5.2.2.1.5 Why change the spider's position?
This change is position is necessary to prevent the same ladybug from registering a collision with the same
spider during the next iteration of the game loop. The forward movement of the spiders and the ladybugs each
time they move is less than the dimensions of the intersecting rectangles. Therefore, without a purposeful
shift in position of either the ladybug or the spider, the pair would continue to register collisions until the
rectangles nally separate from one another.
17.5.2.2.1.6 Spiders don't die easily
As you read in the Preview (p. 259) section, if a black widow spider collides with a ladybug, it simply moves
128 pixels to the right and changes into a green spider. If a green spider collides with a ladybug, it simply
moves 128 pixels to the right and changes into a brown spider. Finally, if a brown spider collides with a
ladybug, it is eaten and removed from the population of spiders. This is accomplished by the code in Listing
12 (p. 268) .
Listing 12
. Spiders don't die easily.
//If the collision was with a black widow
// spider, cause it to reincarnate into a
// green spider.
if(target.Image.Name == "blackWidowSpider") {
target.SetImage("greenspider",Content);
}
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
269
//If the collision was with a green spider,
// cause it to reincarnate into a brown
// spider.
else if(target.Image.Name == "greenspider") {
target.SetImage("brownSpider",Content);
}
//If the collision was with a brown spider,
// it gets eaten. Remove it from the list of
// spiders.
else if(target.Image.Name == "brownSpider") {
spiders.Remove(target);
}// end else-if
}//end if
}//end for loop
base.Update(gameTime);
}//end Update method
The code in Listing 12 (p. 268) is relatively straightforward.
17.5.2.2.1.7 The Name property
The rst statement in Listing 12 (p. 268) shows why I needed to set an identiable string value into the
Name property of the Texture2D object referred to by image in Listing 2 (p. 262) and Listing 3
(p. 263) . I needed a way to determine the type of spider involved in each collision so that I could take the
appropriate action when a collision was detected.
17.5.2.2.1.8 Killing the brown spider
The call to the spiders.Remove method
in Listing 12 (p. 268) removes the target Sprite object's
reference from the list of spiders. Since this is the only reference to the Sprite object, this action should
make the memory occupied by the Sprite object eligible for garbage collection.
17.5.2.2.1.9 Is a Dispose Method needed?
However, I'm not certain that garbage collection is sucient to free up all of the resources owned by the
now defunct object and its image. It might also be advisable to use a Dispose Method 4 . At the time I am
writing this, I simply don't know.
17.5.2.2.2 The end of the Update method
Listing 12 (p. 268) also signals the end of the overridden
Update
method.
17.5.2.3 The overridden Game.Draw method
The overridden Game.Draw method is shown in its entirety in Listing 13 (p.
Listing 13
269) .
. The overridden Game.Draw method.
protected override void Draw(GameTime gameTime) {
spriteBatch.Begin();
4 http://msdn.microsoft.com/en-us/library/fs2xkftw.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
270
CHAPTER 17. XNA0130-COLLISION DETECTION
spiderWeb.Draw(spriteBatch);//draw background
//Draw all spiders.
for(int cnt = 0;cnt < spiders.Count;cnt++) {
spiders[cnt].Draw(spriteBatch);
}//end for loop
//Draw all ladybugs.
for(int cnt = 0;cnt < ladybugs.Count;cnt++) {
ladybugs[cnt].Draw(spriteBatch);
}//end for loop
spriteBatch.End();
base.Draw(gameTime);
}//end Draw method
//-------------------------------------------------//
}//end class
}//end namespace
There is nothing new in Listing 13 (p. 269) . However, the call to spiderWeb.Draw is a little dierent
from the code that you have seen in the overridden Game.Draw methods in the last couple of modules.
This code calls the Sprite.Draw method, which in turn calls the SpriteBatch.Draw method to draw
the image of the spider web as a background image in Figure 1 (p. 260) .
17.5.3 The end of the program
Listing 13 (p. 269) also signals the end of the overridden
class, and the end of the program.
Game.Draw
method, the end of the
Game1
17.6 Run the program
I encourage you to copy the code from Listing 14 (p. 272) and Listing 15 (p. 276) . Use that code to create
an XNA project. You should be able to nd and download suitable image les from the web. Any small
images can be used as substitutes for the spiders and the ladybugs. A larger image can be used for the
background.
Compile and run the project. Experiment with the code, making changes, and observing the results of
your changes. Make sure that you can explain why your changes behave as they do.
17.7 Run my program
Click here
5
to download a zip le containing my version of the program. Extract the folder named
XNA0130Proj from the zip le and save it somewhere on your disk. Start Visual C# 2010 Express
and select Open Project... from the File menu. Navigate to the project folder and select the le with
the extension of .sln . This should cause the project to open and be ready to run or debug as described
in the earlier module titled Getting Started
6
.
5 http://cnx.org/content/m49524/latest/XNA0130Proj.zip
6 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
17.8 Summary
271
You learned how to design and create a Sprite class that provides collision detection. You also learned
how to write an XNA program that takes advantage of that collision detection capability.
17.9 What's next?
The one critical element that is preventing us from using the Sprite class to create a 2D arcade style game
is the ability of the user to control the motion of one or more sprites using keyboard and/or mouse input.
That will be the topic for the next module.
Once we have that tool, we can write a game where the challenge is to prevent the spiders from successfully
navigating across the game window without being eaten by a ladybug. Or, we could write a game where the
challenge is to cause the ladybugs to successfully navigate across the game window without being bitten by
a spider.
Beyond that, there are several other tools that will make it possible for us to create more sophisticated
and interesting games:
• The ability to play sounds.
• The ability to create onscreen text.
• The ability to create a game with multiple levels and increasing diculty at each level, scorekeeping,
etc.
I will show you how to create some of those tools later in this series of modules and will leave others as an
exercise for the student.
17.10 Miscellaneous
This section contains a variety of miscellaneous information.
Housekeeping material
• Module name: Xna0130-Collision Detection
• File: Xna0130.htm
• Published: 02/28/14
Disclaimers: Financial : Although the Connexions site makes it possible for you to download
a PDF le for this module at no charge, and also makes it possible for you to purchase a pre-printed
version of the PDF le, you should be aware that some of the HTML elements in this module may
not translate well into PDF.
I also want you to know that, I receive no nancial compensation from the Connexions website even
if you purchase the PDF version of the module.
In the past, unknown individuals have copied my modules from cnx.org, converted them to Kindle
books, and placed them for sale on Amazon.com showing me as the author. I neither receive
compensation for those sales nor do I know who does receive compensation. If you purchase such
a book, please be aware that it is a copy of a module that is freely available on cnx.org and that it
was made and published without my prior knowledge.
Aliation : I am a professor of Computer Information Technology at Austin Community College
in Austin, TX.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
272
17.11 Complete program listing
CHAPTER 17. XNA0130-COLLISION DETECTION
Complete listings of the program les discussed in this module are provided in Listing 14 (p. 272) and Listing
15 (p. 276) below.
Listing 14
. The Sprite class for the project named XNA0130Proj.
/*Project XNA0130Proj
* This file defines a Sprite class from which a Sprite
* object can be instantiated. This version supports
* collision detection based on intersecting rectangles.
*******************************************************/
using
using
using
using
using
System;
System.Collections.Generic;
Microsoft.Xna.Framework;
Microsoft.Xna.Framework.Content;
Microsoft.Xna.Framework.Graphics;
namespace XNA0130Proj {
class Sprite {
private Texture2D image;
private Vector2 position = new Vector2(0,0);
private Vector2 direction = new Vector2(0,0);
private Point windowSize;
private Random random;
double elapsedTime;//in milliseconds
//The following value is the inverse of speed in
// moves/msec expressed in msec/move.
double elapsedTimeTarget;
//-------------------------------------------------//
//Image property accessor - new to this version.
public Texture2D Image {
get {
return image;
}//end get
}//end Image property accessor
//-------------------------------------------------//
//Position property accessor
public Vector2 Position {
get {
return position;
}
set {
position = value;
}//end set
}//end Position property accessor
//-------------------------------------------------//
//WindowSize property accessor
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
273
public Point WindowSize {
set {
windowSize = value;
}//end set
}//end WindowSize property accessor
//-------------------------------------------------//
//Direction property accessor
public Vector2 Direction {
get {
return direction;
}
set {
direction = value;
}//end set
}//end Direction property accessor
//-------------------------------------------------//
//Speed property accessor. The set side should be
// called with speed in moves/msec. The get side
// returns speed moves/msec.
public double Speed {
get {
//Convert from elapsed time in msec/move to
// speed in moves/msec.
return elapsedTimeTarget/1000;
}
set {
//Convert from speed in moves/msec to
// elapsed time in msec/move.
elapsedTimeTarget = 1000/value;
}//end set
}//end Speed property accessor
//-------------------------------------------------//
//This constructor loads an image for the sprite
// when it is instantiated. Therefore, it requires
// an asset name for the image and a reference to a
// ContentManager object.
//Requires a reference to a Random object. Should
// use the same Random object for all sprites to
// avoid getting the same sequence for different
// sprites.
public Sprite(String assetName,
ContentManager contentManager,
Random random) {
image = contentManager.Load<Texture2D>(assetName);
image.Name = assetName;//new to this version
this.random = random;
}//end constructor
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
274
CHAPTER 17. XNA0130-COLLISION DETECTION
//-------------------------------------------------//
//This method can be called to load a new image
// for the sprite.
public void SetImage(String assetName,
ContentManager contentManager) {
image = contentManager.Load<Texture2D>(assetName);
image.Name = assetName;//new to this version
}//end SetImage
//-------------------------------------------------//
//This method causes the sprite to move in the
// direction of the direction vector if the elapsed
// time since the last move exceeds the elapsed
// time target based on the specified speed.
public void Move(GameTime gameTime) {
//Accumulate elapsed time since the last move.
elapsedTime +=
gameTime.ElapsedGameTime.Milliseconds;
if(elapsedTime > elapsedTimeTarget){
//It's time to make a move. Set the elapsed
// time to a value that will attempt to produce
// the specified speed on the average.
elapsedTime -= elapsedTimeTarget;
//Add the direction vector to the position
// vector to get a new position vector.
position = Vector2.Add(position,direction);
//Check for a collision with an edge of the game
// window. If the sprite reaches an edge, cause
// the sprite to wrap around and reappear at the
// other edge, moving at the same speed in a
// different direction within the same quadrant
// as before.
if(position.X < -image.Width){
position.X = windowSize.X;
NewDirection();
}//end if
if(position.X > windowSize.X){
position.X = -image.Width/2;
NewDirection();
}//end if
if(position.Y < -image.Height) {
position.Y = windowSize.Y;
NewDirection();
}//end if
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
275
if(position.Y > windowSize.Y){
position.Y = -image.Height / 2;
NewDirection();
}//end if on position.Y
}//end if on elapsed time
}//end Move
//-------------------------------------------------//
//This method determines the length of the current
// direction vector along with the signs of the X
// and Y components of the current direction vector.
// It computes a new direction vector of the same
// length with the X and Y components having random
// lengths and the same signs.
//Note that random.NextDouble returns a
// pseudo-random value, uniformly distributed
// between 0.0 and 1.0.
private void NewDirection() {
//Get information about the current direction
// vector.
double length = Math.Sqrt(
direction.X * direction.X +
direction.Y * direction.Y);
Boolean xNegative = (direction.X < 0)?true:false;
Boolean yNegative = (direction.Y < 0)?true:false;
//Compute a new X component as a random portion of
// the vector length.
direction.X =
(float)(length * random.NextDouble());
//Compute a corresponding Y component that will
// keep the same vector length.
direction.Y = (float)Math.Sqrt(length*length direction.X*direction.X);
//Set the signs on the X and Y components to match
// the signs from the original direction vector.
if(xNegative)
direction.X = -direction.X;
if(yNegative)
direction.Y = -direction.Y;
}//end NewDirection
//-------------------------------------------------//
public void Draw(SpriteBatch spriteBatch) {
//Call the simplest available version of
// SpriteBatch.Draw
spriteBatch.Draw(image,position,Color.White);
}//end Draw method
//-------------------------------------------------//
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
276
CHAPTER 17. XNA0130-COLLISION DETECTION
//This method is new to this version of the Sprite
// class.
//Returns the current rectangle occupied by the
// sprite.
public Rectangle GetRectangle() {
return new Rectangle((int)(position.X),
(int)(position.Y),
image.Width,
image.Height);
}//end GetRectangle
//-------------------------------------------------//
//This method is new to this version of the Sprite
// class.
//This method receives a list of Sprite objects as
// an incoming parameter. It tests for a collision
// with the sprites in the list beginning with the
// sprite at the head of the list. If it detects a
// collision, it stops testing immediately and
// returns a reference to the Sprite object for
// which it found the collision. If it doesn't find
// a collision with any sprite in the list, it
// returns null.
//A collision is called if the rectangle containing
// this object's image intersects the rectangle
// containing a target sprite's image.
public Sprite IsCollision(List<Sprite> target) {
Rectangle thisRectangle =
new Rectangle((int)(position.X),
(int)(position.Y),
image.Width,
image.Height);
Rectangle targetRectangle;
int cnt = 0;
while(cnt < target.Count){
targetRectangle = target[cnt].GetRectangle();
if(thisRectangle.Intersects(targetRectangle)){
return target[cnt];
}//end if
cnt++;
}//end while loop
return null;//no collision detected
}//end IsCollision
//-------------------------------------------------//
}//end class
}//end namespace
Listing 15
. The Game1 class for the project named XNA0130Proj.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
277
/*Project XNA0130Proj
* This project demonstrates how to integrate
* spiders, and ladybugs in a program using
* objects of a Sprite class with collision
* detection.
* *****************************************************/
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using XNA0130Proj;
namespace XNA0130Proj {
public class Game1 : Microsoft.Xna.Framework.Game {
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
//Use the following values to set the size of the
// client area of the game window. The actual window
// with its frame is somewhat larger depending on
// the OS display options. On my machine with its
// current display options, these dimensions
// produce a 1024x768 game window.
int windowWidth = 1017;
int windowHeight = 738;
//This is the length of the greatest distance in
// pixels that any sprite will move in a single
// frame of the game loop.
double maxVectorLength = 5.0;
Sprite spiderWeb;//reference to a background sprite.
//References to the spiders are stored in this
// List object.
List<Sprite> spiders = new List<Sprite>();
int numSpiders = 200;//Number of spiders.
//The following value should never exceed 60 moves
// per second unless the default frame rate is also
// increased to more than 60 frames per second.
double maxSpiderSpeed = 30;//moves per second
//References to the Ladybugs are stored in this List.
List<Sprite> ladybugs = new List<Sprite>();
int numLadybugs = 5;//Max number of ladybugs
double maxLadybugSpeed = 15;
//Random number generator. It is best to use a single
// object of the Random class to avoid the
// possibility of using different streams that
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
278
CHAPTER 17. XNA0130-COLLISION DETECTION
// produce the same sequence of values.
//Note that the random.NextDouble() method produces
// a pseudo-random value where the sequence of values
// is uniformly distributed between 0.0 and 1.0.
Random random = new Random();
//-------------------------------------------------//
public Game1() {//constructor
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
//Set the size of the game window.
graphics.PreferredBackBufferWidth = windowWidth;
graphics.PreferredBackBufferHeight = windowHeight;
}//end constructor
//-------------------------------------------------//
protected override void Initialize() {
//No initialization required.
base.Initialize();
}//end Initialize
//-------------------------------------------------//
protected override void LoadContent() {
spriteBatch = new SpriteBatch(GraphicsDevice);
//Create a sprite for the background image.
spiderWeb =
new Sprite("spiderwebB",Content,random);
spiderWeb.Position = new Vector2(0f,0f);
//Instantiate all of the spiders and cause them to
// move from left to right, top to
// bottom. Pass a reference to the same Random
// object to all of the sprites.
for(int cnt = 0;cnt < numSpiders;cnt++) {
spiders.Add(
new Sprite("blackWidowSpider",Content,random));
//Set the position of the current spider at a
// random location within the game window.
spiders[cnt].Position = new Vector2(
(float)(windowWidth * random.NextDouble()),
(float)(windowHeight * random.NextDouble()));
//Get a direction vector for the current spider.
// Make both components positive to cause the
// vector to point down and to the right.
spiders[cnt].Direction = DirectionVector(
(float)maxVectorLength,
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
279
(float)(maxVectorLength * random.NextDouble()),
false,//xNeg
false);//yNeg
//Notify the spider object of the size of the
// game window.
spiders[cnt].WindowSize =
new Point(windowWidth,windowHeight);
//Set the speed in moves per second for the
// current spider to a random value between
// maxSpiderSpeed/2 and maxSpiderSpeed.
spiders[cnt].Speed = maxSpiderSpeed / 2
+ maxSpiderSpeed * random.NextDouble() / 2;
}//end for loop
//Use the same process to instantiate all of the
// ladybugs and cause them to move from right to
// left, bottom to top.
for(int cnt = 0;cnt < numLadybugs;cnt++) {
ladybugs.Add(
new Sprite("ladybug",Content,random));
ladybugs[cnt].Position = new Vector2(
(float)(windowWidth * random.NextDouble()),
(float)(windowHeight * random.NextDouble()));
ladybugs[cnt].Direction = DirectionVector(
(float)maxVectorLength,
(float)(maxVectorLength * random.NextDouble()),
true,//xNeg
true);//yNeg
ladybugs[cnt].WindowSize =
new Point(windowWidth,windowHeight);
ladybugs[cnt].Speed = maxLadybugSpeed / 2
+ maxLadybugSpeed * random.NextDouble() / 2;
}//end for loop
}//end LoadContent
//-------------------------------------------------//
//This method returns a direction vector given the
// length of the vector, the length of the
// X component, the sign of the X component, and the
// sign of the Y component. Set negX and/or negY to
// true to cause them to be negative. By adjusting
// the signs on the X and Y components, the vector
// can be caused to point into any of the four
// quadrants.
private Vector2 DirectionVector(float vecLen,
float xLen,
Boolean negX,
Boolean negY) {
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
280
CHAPTER 17. XNA0130-COLLISION DETECTION
Vector2 result = new Vector2(xLen,0);
result.Y = (float)Math.Sqrt(vecLen * vecLen
- xLen * xLen);
if(negX)
result.X = -result.X;
if(negY)
result.Y = -result.Y;
return result;
}//end DirectionVector
//-------------------------------------------------//
protected override void UnloadContent() {
//No content unload required.
}//end unloadContent
//-------------------------------------------------//
protected override void Update(GameTime gameTime) {
//Tell all the spiders in the list to move.
for(int cnt = 0;cnt < spiders.Count;cnt++) {
spiders[cnt].Move(gameTime);
}//end for loop
//Tell all the ladybugs in the list to move.
for(int cnt = 0;cnt < ladybugs.Count;cnt++) {
ladybugs[cnt].Move(gameTime);
}//end for loop
//Tell each ladybug to test for a collision with a
// spider and to return a reference to the spider
// if there is a collision. Return null if there is
// no collision.
for(int cnt = 0;cnt < ladybugs.Count;cnt++) {
//Test for a collision between this ladybug and
// all of the spiders in the list of spiders.
Sprite target =
ladybugs[cnt].IsCollision(spiders);
if(target != null) {
//There was a collision. Cause the spider to
// move 128 pixels to the right.
target.Position =
new Vector2(target.Position.X + 128,
target.Position.Y);
//If the collision was with a black widow
// spider, cause it to reincarnate into a
// green spider.
if(target.Image.Name == "blackWidowSpider") {
target.SetImage("greenspider",Content);
}//If the collision was with a green spider,
// cause it to reincarnate into a brown
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
281
// spider.
else if(target.Image.Name == "greenspider") {
target.SetImage("brownSpider",Content);
}//If the collision was with a brown spider,
// it gets eaten. Remove it from the list of
// spiders.
else if(target.Image.Name == "brownSpider") {
spiders.Remove(target);
}// end else-if
}//end if
}//end for loop
base.Update(gameTime);
}//end Update method
//-------------------------------------------------//
protected override void Draw(GameTime gameTime) {
spriteBatch.Begin();
spiderWeb.Draw(spriteBatch);//draw background
//Draw all spiders.
for(int cnt = 0;cnt < spiders.Count;cnt++) {
spiders[cnt].Draw(spriteBatch);
}//end for loop
//Draw all ladybugs.
for(int cnt = 0;cnt < ladybugs.Count;cnt++) {
ladybugs[cnt].Draw(spriteBatch);
}//end for loop
spriteBatch.End();
base.Draw(gameTime);
}//end Draw method
//-------------------------------------------------//
}//end class
}//end namespace
-end-
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
282
CHAPTER 17. XNA0130-COLLISION DETECTION
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
Chapter 18
Xna0132-A Simple Game Program with
1
On-Screen Text
Revised: Tue May 10 11:37:12 CDT 2016
This page is part of a Book titled XNA Game Studio
2
.
18.1 Table of Contents
• Table of Contents (p. 283)
• Preface (p. 284)
· Viewing tip (p. 285)
* Figures (p. 285)
* Listings (p. 285)
• General background information (p. 285)
• Preview (p. 285)
·
·
·
·
·
·
·
Spider behavior (p. 286)
The ladybugs (p. 286)
Using the keyboard (p. 286)
Using the mouse (p. 287)
Diculty level (p. 287)
A useful student project (p. 287)
Alternatives to the arrow keys (p. 287)
• Discussion and sample code (p. 287)
· How to draw on-screen text (p. 287)
* Font availability (p. 287)
· Identifying the font families (p. 288)
· Matching font families to font les (p. 288)
* Getting ready to draw on-screen text (p. 288)
* Drawing the on-screen text (p. 289)
1 This content is available online at <http://cnx.org/content/m49529/1.3/>.
2 http://cnx.org/contents/GY804-eY
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
283
CHAPTER 18. XNA0132-A SIMPLE GAME PROGRAM WITH ON-SCREEN
TEXT
284
* The project named XNA0132ProjA (p. 290)
·
·
·
·
The overridden Game.Draw method (p. 291)
The SpriteBatch.DrawString method (p. 291)
Purpose of the origin parameter (p. 292)
All I need to say (p. 292)
· The Sprite class for XNA0132Proj (p. 292)
*
*
*
*
The new code (p. 292)
A new read-only property accessor method for the Edge property (p. 293)
New version of the Move method (p. 293)
End of discussion of the Sprite class (p. 294)
· The Game1 class for XNA0132Proj (p. 294)
* General information (p. 294)
·
·
·
·
The objective of the game (p. 294)
Ladybug control (p. 295)
Spiders are recycled (p. 295)
Keeping score (p. 295)
* Code for the Game1 Class (p. 295)
·
·
·
·
·
·
The overridden LoadContent method (p. 296)
The font family (p. 296)
Make the mouse pointer visible (p. 296)
Position the on-screen text (p. 296)
Set the initial position of the mouse pointer (p. 296)
End of the LoadContent method (p. 296)
· The overridden Update method (p. 296)
· Moving the ladybug objects using the keyboard (p. 297)
· Dragging the ladybug objects with the mouse (p. 300)
· The overridden Game.Draw method (p. 302)
· The MeasureString method (p. 303)
· Overloaded division operator (p. 303)
* The end of the program (p. 303)
•
•
•
•
•
•
•
Run the program (p. 303)
Run my text program (p. 303)
Run my game program (p. 303)
Summary (p. 304)
What's next? (p. 304)
Miscellaneous (p. 304)
Complete program listing (p. 304)
18.2 Preface
This module is one in a collection of modules designed primarily for teaching GAME 1343 Game and
Simulation Programming I at Austin Community College in Austin, TX. These modules are intended
to supplement and not to replace the textbook.
An earlier module titled Getting Started 3 provided information on how to get started programming with
Microsoft's XNA Game Studio.
3 http://cnx.org/contents/GY804-eY:g-5oRzQu
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
285
18.2.1 Viewing tip
I recommend that you open another copy of this module in a separate browser window and use the following
links to easily nd and view the Figures and Listings while you are reading about them.
18.2.1.1 Figures
•
•
•
•
Figure
Figure
Figure
Figure
1
2
3
4
(p.
(p.
(p.
(p.
285)
287)
288)
289)
.
.
.
.
Reduced view of the game window.
Information on fonts from the XNA documentation.
Correspondence between font-family names and font le names.
Demonstration of on-screen text with the Lindsey font.
18.2.1.2 Listings
• Listing 1 (p. 290) . Beginning of the Game1 class and the overridden LoadContent method for the
project named XNA0132ProjA.
• Listing 2 (p. 291) . Beginning of the overridden Game.Draw method.
• Listing 3 (p. 291) . Remainder of the overridden Game.Draw method.
• Listing 4 (p. 293) . A new read-only property accessor method for the Edge property for the Sprite
class.
• Listing 5 (p. 293) . New version of the Move method.
• Listing 6 (p. 295) . Abbreviated beginning of the Game1 Class for the XNA0132Proj project.
• Listing 7 (p. 296) . Abbreviated overridden LoadContent method.
• Listing 8 (p. 296) . Beginning of an abbreviated overridden Update method.
• Listing 9 (p. 297) . Service the keyboard.
• Listing 10 (p. 300) . Moving the ladybug objects with the mouse.
• Listing 11 (p. 302) . The overridden Game.Draw method.
• Listing 12 (p. 304) . Game1 class for project XNA0132ProjA.
• Listing 13 (p. 306) . Sprite class for project XNA0132Proj.
• Listing 14 (p. 312) . Game1 class for project XNA0132Proj.
18.3 General background information
When we nished the previous module, we were missing one critical element needed to write a simple game
program:
user input . We were also lacking some other tools that would make it possible for us to
create more interesting 2D arcade-style games including the ability to create on-screen text.
In this module, you will learn how to program for user input with the keyboard and the mouse. You will
also learn how to create on-screen text.
18.4 Preview
The program that I will explain in this module is a simple game in which ten spiders attempt to traverse
their web from top to bottom. Two large ladybugs are there to eat them if possible. (See Figure 1 (p. 285)
.) The objective of the game is to prevent as many of the spiders as possible from making it to the bottom
of the game window.
Figure 1 (p. 285) shows a reduced view of the game window shortly after the game is started and before
any spiders make it to the bottom.
Figure 1
. Reduced view of the game window.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
286
CHAPTER 18. XNA0132-A SIMPLE GAME PROGRAM WITH ON-SCREEN
TEXT
18.4.1 Spider behavior
The spiders move at dierent speeds in dierent directions but generally towards the southeast. If a spider
makes it to the bottom, it is counted as having crossed the nish line. That spider wraps back up to the top
and starts the journey again. If a spider hits the right edge of the window, it wraps around to the left edge
and continues the journey.
18.4.2 The ladybugs
The movements of the ladybugs are under the control of the player. If the player causes a ladybug to collide
with a spider, the spider is eaten and removed from the spider population.
18.4.3 Using the keyboard
The player can move the ladybugs using either the keyboard or the mouse. Pressing the four arrow keys
causes one of the ladybugs to move in the corresponding directions. Pressing two arrow keys at the same
time causes the ladybug to move in a diagonal direction.
Holding down the A key and pressing the arrow keys causes the other ladybug to move.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
287
18.4.4 Using the mouse
Pressing the left mouse button makes it possible to drag a ladybug with the mouse. Pressing the right mouse
button makes it possible to drag the other ladybug with the mouse. Pressing both mouse buttons makes it
possible to drag both ladybugs with the mouse.
18.4.5 Diculty level
The game has only one level. I personally nd it very dicult to prevent all ten spiders from making it to
the bottom using the arrow keys only. On the other hand, it is easy to defeat the spiders by dragging a
ladybug with the mouse.
18.4.6 A useful student project
A useful student project would be to upgrade the program to provide varying levels of diculty. This could
be accomplished by changing the speed of the spiders, changing the number of spiders, or a combination of
the two.
18.4.7 Alternatives to the arrow keys
Another useful student project would be to dene alternatives to the arrow keys, such as the keys labeled 4,
8, 6, and 2 on the keypad, or four keys in a diamond pattern on the left side of the keyboard.
18.5 Discussion and sample code
As usual, I will break this program down and explain it in fragments. However, I will only explain those
portions of the code that are substantially dierent from code that I have explained in earlier modules.
Complete listings of the Sprite class and the Game1 class are provided in Listing 13 (p. 306) and
Listing 14 (p. 312) near the end of the module.
Before getting into the game program, however, I will explain a very short program that illustrates how
to draw on-screen text. I personally found the documentation 4 on this topic to be somewhat confusing.
18.5.1 How to draw on-screen text
The diculty that I encountered in drawing on-screen text was not in the writing of program code. Rather,
it was in getting ready to write program code.
18.5.1.1 Font availability
Before getting into the details, I want to mention that fonts are typically copyrighted items of intellectual
property and you must be careful when distributing fonts for which you don't have distribution rights. To
assist you in this regard, the information shown in Figure 2 (p. 287) currently appears in the documentation
5 .
Figure 2
. Information on fonts from the XNA documentation.
The following list of fonts are installed by XNA Game Studio and are redistributable:
• Kooten.ttf
• Linds.ttf
• Miramo.ttf
4 http://msdn.microsoft.com/en-us/library/bb447673.aspx
5 http://msdn.microsoft.com/en-us/library/bb447673.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 18. XNA0132-A SIMPLE GAME PROGRAM WITH ON-SCREEN
TEXT
288
•
•
•
•
•
Bold Miramob.ttf
Peric.ttf
Pericl.ttf
Pesca.ttf
Pescab.ttf
These OpenType fonts, created by Ascender Corporation and licensed by Microsoft, are free for
you to use in your XNA Game Studio game. You may redistribute these fonts in their original
format as part of your game. These fonts were previously available only on the XNA Creators Club
Online website.
18.5.1.1.1 Identifying the font families
You should be able to select Fonts in the Windows Control Panel
and see a list of the font families
that are installed on your machine. The problem is that the names of the font families are not an exact
match for the le names listed in Figure 2 (p. 287) .
18.5.1.1.2 Matching font families to font les
Figure 3 (p. 288) shows my best guess as to the correspondence between font-family names and the le
names listed in Figure 2 (p. 287) . Font family names are on the left and font le names are on the right.
Figure 3
•
•
•
•
•
•
•
•
. Correspondence between font-family names and font le names.
Kootenay - Kooten.ttf
Lindsey - Linds.ttf
Miramonte - Miramo.ttf
Miramonte Bold - Bold Miramob.ttf
Pericles - Peric.ttf
Pericles Light - Pericl.ttf
Pescadero - Pesca.ttf
Pescadero Bold -Pescab.ttf
You will learn shortly why we care about the font-family names.
18.5.1.2 Getting ready to draw on-screen text
Before you can draw on-screen text you must add a Sprite Font
design process.
Here is my interpretation of the steps required to add the
to your project from the IDE during the
Sprite Font
to your project:
• Open your project in Visual C#.
• Right-click your Content folder in Solution Explorer , select Add , and then click New Item
.
• From the Add New Item dialog box, click Sprite Font .
• Change the text in the Name eld to [Font-Family Name].spritefont , such as Lindsey.spritefont for example.
• Click the Add button. This will create a new XML le named [Font-Family Name].spritefont
, which will be opened in the IDE editor.
• Click the le named [ Font-Family Name].spritefont in the Solution Explorer and note the
Asset Name that is displayed in the Properties window (such as Lindsey for example).
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
289
• The XML le contains an element named FontName . Change the contents of that element, if
necessary, to match the name of the font family that you specied.
• Change the contents of the Size element in the XML le to the point size you desire for your font.
• Change the contents of the Style element in the XML le to the style of font to import. You can
specify Regular , Bold , Italic , or Bold, Italic . Note that the contents of the XML elements
are case sensitive.
• Specify the character regions to import for this font. Character regions specify which characters in
the font are rendered by the SpriteFont . You can specify the start and end of the region by using
the characters themselves, or by using their decimal values with an ampersand-pound sign prex. The
default character region includes all the characters between the space and tilde characters, inclusive.
• Follow the instructions in the comments in the XML le to make any other changes to the contents of
the XML elements that are appropriate for your program.
18.5.1.3 Drawing the on-screen text
I will explain the code in the Game1 class of the project named XNA0132ProjA
screen output shown in reduced form in Figure 4 (p. 289) .
Figure 4
that produced the
. Demonstration of on-screen text with the Lindsey font.
As usual, I will explain the code in fragments. A complete listing of the Game1 class is provided in Listing
12 (p. 304) near the end of the module.
Here is my interpretation of the coding steps for displaying on-screen text in the game window.
• Add a Sprite Font to your project as described above.
• Declare an instance variable of type SpriteFont 6 to refer to a
290) .)
SpriteFont
object. (See Listing 1 (p.
6 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.spritefont.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
290
CHAPTER 18. XNA0132-A SIMPLE GAME PROGRAM WITH ON-SCREEN
TEXT
• Declare an instance variable of type Vector2 to refer to an object that species the position of your
text relative to the upper left corner of the game window. (See Listing 1 (p. 290) .)
• Create a SpriteFont object by calling the ContentManager.Load method, specifying the
SpriteFont class and the Asset Name of the imported font in your overridden LoadContent
method. Save the object's reference in the instance variable declared earlier. (See Listing 1 (p. 290) .)
• Instantiate a new Vector2 object to specify the position of the text in the game window in the
LoadContent method. Save the object's reference in the instance variable declared earlier. (See
Listing 1 (p. 290) .) The X and Y component values of the Vector2 object can be modied later in
the Update method if needed.
• Write the typical SpriteBatch.Begin and SpriteBatch.End method calls in the overridden
Game.Draw method. (See Listing 2 (p. 291) and Listing 3 (p. 291) .)
• Create a string in the LoadContent , Update , or Draw methods containing the text that will
be displayed in the game window. (See Listing 2 (p. 291) .)
• Instantiate a Vector2 object in the LoadContent , Update , or Draw methods that species
the origin of the text string, which will be drawn at the previously computed position of the text string.
(See Listing 2 (p. 291) .)
• Compute values for the other parameters to the SpriteBatch.DrawString method as needed.
• Call the SpriteBatch.DrawString method between the SpriteBatch.Begin and SpriteBatch.End methods. (See Listing 3 (p. 291) .)
18.5.1.4 The project named XNA0132ProjA
Listing 1 (p. 290) shows the beginning of the Game1 class and the overridden LoadContent method
for the project named XNA0132ProjA . This project, which is dierent from XNA0132Proj to be
discussed later, demonstrates how to draw onscreen text.
Listing 1 . Beginning of the Game1 class and the overridden LoadContent method for the
project named XNA0132ProjA.
namespace XNA0132ProjA {
public class Game1 : Microsoft.Xna.Framework.Game {
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
SpriteFont Font1;
Vector2 FontPos;
//-------------------------------------------------//
protected override void LoadContent() {
spriteBatch = new SpriteBatch(GraphicsDevice);
//Create a new SpriteFont object.
Font1 = Content.Load<SpriteFont>("Lindsey");
//Create a new Vector2 object to center the text
// in the game window.
FontPos = new Vector2(
graphics.GraphicsDevice.Viewport.Width / 2,
graphics.GraphicsDevice.Viewport.Height / 2);
}//end LoadContent method
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
291
There is no unusual code in Listing 1 (p. 290) . However, if my memory serves me correctly, this is the rst
time in this series of modules that I have called the Content.Load method to load a resource other than
an image.
18.5.1.4.1 The overridden Game.Draw method
The Game.Draw method begins in Listing 2 (p. 291) .
Listing 2
. Beginning of the overridden Game.Draw method.
protected override void Draw(GameTime gameTime) {
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
// Draw Lindsey Font
string output = "Lindsey Font";
// Find the center of the string
Vector2 FontOrigin =
Font1.MeasureString(output) / 2;
There is nothing new or unusual in Listing 2 (p. 291) .
The remainder of the Game.Draw method is shown in Listing 3 (p. 291) .
Listing 3
. Remainder of the overridden Game.Draw method.
//Draw the string with the FontOrigin at the
// position specified by FontPos.
spriteBatch.DrawString(
Font1,//font to use
output,//output text
FontPos,//position re upper left corner
Color.LightGreen,//color of text
-45,//rotation of text
FontOrigin,//place this at FontPos
2.5f,//scale
SpriteEffects.None,
0.5f);//z-order layer
spriteBatch.End();
base.Draw(gameTime);
}//end Draw method
There is nothing unusual in the code in Listing 3 (p. 291) . However, this is the rst time in this series of
modules that I have called the SpriteBatch.DrawString method.
18.5.1.4.2 The SpriteBatch.DrawString method
There are several overloaded versions of the SpriteBatch.DrawString
method. The dierent versions
depend on the features provided by the parameters. The version called in Listing 3 (p. 291) is one of the
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
292
CHAPTER 18. XNA0132-A SIMPLE GAME PROGRAM WITH ON-SCREEN
TEXT
most complex. Here is part of what the documentation 7 has to say about this version of the DrawString
method:
"Adds a mutable sprite string to the batch of sprites to be rendered, specifying the font, output text,
screen position, color tint, rotation, origin, scale, and eects.
Parameters
•
•
•
•
•
•
•
•
•
spriteFont - The sprite font.
text - The mutable (read/write) string to draw.
position - The location, in screen coordinates, where the text will be drawn.
color - The desired color of the text.
rotation - The angle, in radians, to rotate the text around the origin.
origin - The origin of the string. Specify (0,0) for the upper-left corner.
scale - Uniform multiple by which to scale the sprite width and height.
eects - Rotations to apply prior to rendering.
layerDepth - The sorting depth of the sprite, between 0 (front) and 1 (back)."
18.5.1.4.2.1 Purpose of the origin parameter
Everything in this description is reasonably clear except for the description of the origin parameter so I
will attempt to clarify it. The origin species a point within the text string relative to its upper left corner
that will be drawn at the point in the game window specied by the position parameter.
18.5.1.4.2.2 All I need to say
That is probably all I need to say about the on-screen display of text. It's time to move on to an explanation
of our simple game program named XNA0132Proj beginning with the Sprite class.
18.5.2 The Sprite class for XNA0132Proj
The Sprite class that I used in this program is very similar to the class that I explained in the previous
module. Therefore, I won't have a lot to say about the Sprite class in this module. A complete listing of
the Sprite class is provided in Listing 13 (p. 306) near the end of the module.
This version of the Sprite class supports collision detection based on intersecting rectangles. It also
provides a new Edge property that records and returns the edge number (1, 2, 3, or 4) if a sprite collides
with an edge of the game window. However, the edge information is available for only one iteration of the
game loop before it changes back to the normal value of 0.
The value of the Edge property temporarily changes to 1, 2, 3, or 4 if a sprite collides with the top,
right, bottom, or left edge of the game window respectively.
Since the information regarding the collision with an edge is the only change to this Sprite class relative
to the version that I explained in a previous module, that is all I will discuss about the Sprite class in this
module.
18.5.2.1 The new code
The rst manifestation of change in this version of the Sprite class is the simple declaration of an instance
variable of type int named edge near the beginning of the class denition. You can view that statement
in Listing 13 (p. 306) .
7 http://msdn.microsoft.com/en-us/library/dd232003.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
293
18.5.2.2 A new read-only property accessor method for the Edge property
The next manifestation is the read-only property accessor method for the new Edge
Listing 4 (p. 293) .
Listing 4
class.
. A new read-only property accessor method for the Edge property for the Sprite
public int Edge {
get {
return edge;
}//end get
}//end Edge property accessor
18.5.2.3 New version of the Move method
Listing 5 (p. 293) shows the new version of the Move
Listing 5
property shown in
method.
. New version of the Move method.
//This method causes the sprite to move in the
// direction of the direction vector if the elapsed
// time since the last move exceeds the elapsed
// time target based on the specified speed.
public void Move(GameTime gameTime) {
//New to this version
//Clear the Edge property value. Edge information
// is available for only one iteration of the
// game loop.
edge = 0;
//Accumulate elapsed time since the last move.
elapsedTime +=
gameTime.ElapsedGameTime.Milliseconds;
if(elapsedTime > elapsedTimeTarget) {
//It's time to make a move. Set the elapsed
// time to a value that will attempt to produce
// the specified speed on the average.
elapsedTime -= elapsedTimeTarget;
//Add the direction vector to the position
// vector to get a new position vector.
position = Vector2.Add(position,direction);
//Check for a collision with an edge of the game
// window. If the sprite reaches an edge, cause
// the sprite to wrap around and reappear at the
// other edge, moving at the same speed in a
// different direction within the same quadrant
// as before. Also set the Edge property to
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
294
CHAPTER 18. XNA0132-A SIMPLE GAME PROGRAM WITH ON-SCREEN
TEXT
// indicate which edge was involved. 1 is top, 2
// is right, 3 is bottom, and 4 is left.
// Note that the Edge property will be cleared
// to 0 the next time the Move method is called.
if(position.X < -image.Width) {
position.X = windowSize.X;
edge = 4;//collision with the left edge - new
NewDirection();
}//end if
if(position.X > windowSize.X) {
position.X = -image.Width / 2;
edge = 2;//collision with the right edge - new
NewDirection();
}//end if
if(position.Y < -image.Height) {
position.Y = windowSize.Y;
edge = 1;//collision with the top - new
NewDirection();
}//end if
if(position.Y > windowSize.Y) {
position.Y = -image.Height / 2;
edge = 3;//collision with the bottom - new
NewDirection();
}//end if on position.Y
}//end if on elapsed time
}//end Move
There is nothing unusual about the code that was added to the
further explanation beyond the embedded comments.
18.5.2.4 End of discussion of the Sprite class
There were no additional changes made to the Sprite
end the discussion of the Sprite class.
Move
method, so it shouldn't require
class other than those identied above, so that will
18.5.3 The Game1 class for XNA0132Proj
18.5.3.1 General information
This project demonstrates how to write a simple 2D arcade style game.
Ten spiders try to make it from the top to the bottom of a spider web.
The bottom of the web is guarded by two large ladybugs that are intent on eating the spiders.
If a ladybug collides with a spider, the spider gets eaten by the ladybug and removed from the population
of spiders. If the player plays long enough, all of the spiders should eventually be eaten.
18.5.3.1.1 The objective of the game
The objective of the game is for the player to cause as many spiders as possible to be eaten by the ladybugs
before they make it to the bottom of the game window.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
295
18.5.3.1.2 Ladybug control
The ladybugs can be moved by the player using either the keyboard or the mouse. Pressing the arrow keys
moves one of the ladybugs. Holding down the A key and pressing the arrow keys moves the other ladybug.
The player can drag one of the ladybugs with the mouse pointer by pressing the left mouse button. The
player can drag the other ladybug by pressing the right mouse button.
18.5.3.1.3 Spiders are recycled
Even though the game begins with only ten spiders, those that are not eaten before they reach the bottom of
the game window are recycled. If a spider makes it from the top to the bottom of the game window without
being eaten, the spider wraps around and appears back at the top of the game window ready to start the
trip over.
18.5.3.1.4 Keeping score
A counter keeps track of the number of spiders that cross the bottom of the game window. On-screen text
displays that number as the game progresses as shown in Figure 1 (p. 285) . Note, however, that if two or
more spiders cross the bottom edge of the game window during the same iteration of the game loop, only
one will be counted. In hindsight, therefore, the elapsed time required to cause the ladybugs to eat all of the
spiders might be a better scorekeeping mechanism than counting spider crossings.
Modifying the program to keep score on the basis of elapsed time might make a good student project.
18.5.3.2 Code for the Game1 Class
Listing 6 (p. 295) shows the beginning of the Game1 class along with the constructor in abbreviated
form. By abbreviated form, I mean that much of the code is very similar to code that I have explained in
earlier modules so I deleted that code from Listing 6 (p. 295) for brevity. You can view all of the code for
the Game1 class in Listing 14 (p. 312) .
Listing 6
. Abbreviated beginning of the Game1 Class for the XNA0132Proj project.
namespace XNA0132Proj {
public class Game1 : Microsoft.Xna.Framework.Game {
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
//code deleted for brevity
//The following variable is used to count the number
// of spiders that make it past the ladybugs and
// reach the bottom of the game window.
int spiderCount = 0;
SpriteFont Font1;//font for on-screen text
Vector2 FontPos;//position of on-screen text
//-------------------------------------------------//
public Game1() {//constructor
//code deleted for brevity
}//end constructor
The variable declarations near the center of Listing 6 (p. 295) are new to this module.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 18. XNA0132-A SIMPLE GAME PROGRAM WITH ON-SCREEN
TEXT
296
18.5.3.2.1 The overridden LoadContent method
An abbreviated listing of the LoadContent method is shown in Listing 7 (p.
Listing 7
296) .
. Abbreviated overridden LoadContent method.
protected override void Update(GameTime gameTime) {
spriteBatch = new SpriteBatch(GraphicsDevice);
Font1 = Content.Load<SpriteFont>("Kootenay");
IsMouseVisible = true;//make mouse visible
//code deleted for brevity
//Position the on-screen text.
FontPos = new Vector2(windowWidth / 2, 50);
//Position the mouse pointer in the center of the
// game window.
Mouse.SetPosition(windowWidth / 2,windowHeight /2);
}//end LoadContent
18.5.3.2.1.1 The font family
To begin with, note that this program displays text on the screen using the font family named
(see Figure 3 (p. 288) for a reference to this font-family name).
Kootenay
18.5.3.2.1.2 Make the mouse pointer visible
By default, the mouse pointer is not visible when it is in the game window. The code in Listing 7 (p. 296)
sets the inherited variable named IsMouseVisible to true, which causes the mouse pointer to be visible.
18.5.3.2.1.3 Position the on-screen text
The code in Listing 7 (p. 296) causes the text to be centered horizontally, 50 pixels down from the top of
the game window as shown in Figure 1 (p. 285) .
18.5.3.2.1.4 Set the initial position of the mouse pointer
The code in Listing 7 (p. 296) causes the initial position of the mouse pointer to be centered in the game
window. (I will have more to say about this later.)
18.5.3.2.1.5 End of the LoadContent method
Listing 7 (p. 296) also signals the end of the overridden
18.5.3.2.2 The overridden Update method
An abbreviated listing of the overridden Update
Listing 8
LoadContent
method.
method begins in Listing 8 (p. 296) .
. Beginning of an abbreviated overridden Update method.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
297
protected override void Update(GameTime gameTime) {
//code deleted for brevity
//Check to see if any spiders have made it to the
// bottom edge.
for(int cnt = 0;cnt < spiders.Count;cnt++) {
if(spiders[cnt].Edge == 3)
//One or more made it to the bottom edge.
spiderCount += 1;
}//end for loop
The code in in Listing 8 (p. 296) counts the number of spiders that make it to the bottom and collide with
the bottom edge of the game window.
18.5.3.2.2.1 Moving the ladybug objects using the keyboard
The code required to service the keyboard is long, tedious, and repetitive, but is not complicated. That code
is shown in Listing 9 (p. 297) .
Listing 9
. Service the keyboard.
//The following code is used to move one or the
// other of two ladybugs using the arrow keys.
// Press only the arrow keys to move one of the
// ladybugs. Press the A plus the arrow keys
// to move the other ladybug.
//The ladybugs cannot be moved outside the game
// window using the keyboard.
//While an arrow key is pressed, the ladybug moves
//five pixels per call to the Update method.
//Get the state of the keyboard.
KeyboardState keyboardState = Keyboard.GetState();
//Execute moves on one ladybug with arrow keys plus
// the A or a key.
if(keyboardState.IsKeyDown(Keys.Left) &&
(keyboardState.IsKeyDown(Keys.A)) &&
(ladybugs[0].Position.X > 0)) {
ladybugs[0].Position = new Vector2(
ladybugs[0].Position.X - 5,
ladybugs[0].Position.Y);
}//end if
if(keyboardState.IsKeyDown(Keys.Right) &&
(keyboardState.IsKeyDown(Keys.A)) &&
(ladybugs[0].Position.X <
(windowWidth - ladybugs[1].Image.Width))) {
ladybugs[0].Position = new Vector2(
ladybugs[0].Position.X + 5,
ladybugs[0].Position.Y);
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 18. XNA0132-A SIMPLE GAME PROGRAM WITH ON-SCREEN
TEXT
298
}//end if
if(keyboardState.IsKeyDown(Keys.Up) &&
(keyboardState.IsKeyDown(Keys.A)) &&
(ladybugs[0].Position.Y > 0)) {
ladybugs[0].Position = new Vector2(
ladybugs[0].Position.X,
ladybugs[0].Position.Y - 5);
}//end if
if(keyboardState.IsKeyDown(Keys.Down) &&
(keyboardState.IsKeyDown(Keys.A)) &&
(ladybugs[0].Position.Y <
(windowHeight - ladybugs[1].Image.Height))) {
ladybugs[0].Position = new Vector2(
ladybugs[0].Position.X,
ladybugs[0].Position.Y + 5);
}//end if
//Execute moves on the other ladybug with arrow
// keys pressed but the A key not pressed.
if(keyboardState.IsKeyDown(Keys.Left) &&
!(keyboardState.IsKeyDown(Keys.A)) &&
(ladybugs[1].Position.X > 0)) {
ladybugs[1].Position = new Vector2(
ladybugs[1].Position.X - 5,
ladybugs[1].Position.Y);
}//end if
if(keyboardState.IsKeyDown(Keys.Right) &&
!(keyboardState.IsKeyDown(Keys.A)) &&
(ladybugs[1].Position.X <
(windowWidth - ladybugs[1].Image.Width))) {
ladybugs[1].Position = new Vector2(
ladybugs[1].Position.X + 5,
ladybugs[1].Position.Y);
}//end if
if(keyboardState.IsKeyDown(Keys.Up) &&
!(keyboardState.IsKeyDown(Keys.A)) &&
(ladybugs[1].Position.Y > 0)) {
ladybugs[1].Position = new Vector2(
ladybugs[1].Position.X,
ladybugs[1].Position.Y - 5);
}//end if
if(keyboardState.IsKeyDown(Keys.Down) &&
!(keyboardState.IsKeyDown(Keys.A)) &&
(ladybugs[1].Position.Y <
(windowHeight - ladybugs[1].Image.Height))) {
ladybugs[1].Position = new Vector2(
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
299
ladybugs[1].Position.X,
ladybugs[1].Position.Y + 5);
}//end if
Get the state of the keyboard
The code Listing 9 (p. 297) calls the static GetState method of the Keyboard class to get a
reference to an object of the KeyboardState structure. Here is part of what the documentation 8 for
KeyboardState has to say:
"Represents a state of keystrokes recorded by a keyboard input device."
Polling instead of handling events
Most business and scientic programs written using C# would sit idle and wait for a keyboard event to
occur. Then the event would be handled, and the program would sit idle and wait for the next event to
occur.
Game programs written using XNA don't sit idle. Such programs are always executing the game loop.
Therefore, XNA game programs poll the keyboard and the mouse instead of waiting to handle events red
by the keyboard or the mouse. This represents a major dierence in programming philosophy.
Key up or down queries
An object of the KeyboardState structure has several methods, including the following two which
are of particular interest:
• IsKeyDown 9 - Returns whether a specied key is currently being pressed.
• IsKeyUp 10 - Returns whether a specied key is currently not pressed.
In this program, our primary interest is in the rst of the two methods.
An incoming parameter of type Keys
Both methods require an incoming parameter of type Keys 11 , which is an "Enumerated value that
species the key to query."
The Keys Enumeration has many members, each one of which "Identies a particular key on a
keyboard." In this program, we are interested in the following enumerated values:
•
•
•
•
•
Down - DOWN ARROW key
Left - LEFT ARROW key
Right - RIGHT ARROW key
Up - UP ARROW key
A - The A key
These are the enumerated values that we will use to determine if the player is pressing one or more arrow
keys and the A key.
Make the test
The code in Listing 9 (p. 297) tests to determine if the player is currently pressing both the LEFT
ARROW key and the A key. In addition the code tests to conrm that the current position of the ladybug
is not outside the left edge of the game window.
Move the ladybug
If this test returns true, the code in Listing 9 (p. 297) sets the horizontal position for the ladybug to
ve pixels to the left of its current position. This change in position will be reected visually in the game
window the next time the ladybug is drawn.
Three more tests
8 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.input.keyboardstate.aspx
9 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.input.keyboardstate.iskeydown.aspx
10 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.input.keyboardstate.iskeyup.aspx
11 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.input.keys.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 18. XNA0132-A SIMPLE GAME PROGRAM WITH ON-SCREEN
TEXT
300
Three more similar if statements are executed testing for RIGHT ARROW, UP ARROW, and DOWN
ARROW and moving the ladybug appropriately if the tests return true.
Not mutually exclusive
Note that these tests are not mutually exclusive. For example, the player can be pressing both the LEFT
ARROW key and the UP ARROW key, which will result in the ladybug moving diagonally up and to the
left.
Applies to a particular ladybug
The tests performed above and the corresponding moves apply only to the ladybug whose reference is
stored at index [1] in the list of ladybugs.
Four more similar tests
Four more very similar tests are applied by the code in Listing 9 (p. 297) . These tests and the resulting
moves apply only to the ladybug whose reference is stored at index [0] in the list of ladybugs.
Testing for the A key not pressed
Note that in these four tests, I used the logical not operator (!) to test for the arrow keys pressed but
the A key not pressed . (I also could have called the IsKeyUp method instead of using the logical
not operator for these tests.)
18.5.3.2.2.2 Dragging the ladybug objects with the mouse
Listing 10 (p. 300) shows the code required to drag the ladybug objects with the mouse.
Listing 10
. Moving the ladybug objects with the mouse.
//Get the state of the mouse.
MouseState mouseState = Mouse.GetState();
//Press the left mouse button to move one ladybug.
if(mouseState.LeftButton == ButtonState.Pressed) {
ladybugs[0].Position =
new Vector2(mouseState.X,mouseState.Y);
}//end if
//Press the right mouse button to move the other
// ladybug.
if(mouseState.RightButton == ButtonState.Pressed) {
ladybugs[1].Position =
new Vector2(mouseState.X,mouseState.Y);
}//end if
base.Update(gameTime);
}//end Update method
Relatively simple code
The code in Listing 10 (p. 300) is used to drag one or the other of two ladybug objects with the mouse.
As you can see from the amount of code involved, dragging the ladybug objects with the mouse is much
simpler than moving them from the keyboard.
You can drag one of the ladybug objects by pressing the left mouse button and you can drag the other
ladybug object by pressing the right mouse button. You can drag them both, one on top of the other by
pressing both mouse buttons at the same time.
Get the state of the mouse
As is the case with the keyboard, the code in Listing 10 (p. 300) polls the mouse once during each
iteration of the game loop to determine its state.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
301
a
Listing 10 (p. 300) begins by calling the GetState method of the Mouse
MouseState object that describes the current state of the mouse.
A MouseState object
According to the documentation 12 , a MouseState object
class to get a reference to
"Represents the state of a mouse input device, including mouse cursor position and buttons
pressed."
The object contains several properties including the following:
•
•
•
•
LeftButton - Returns the state of the left mouse button as type ButtonState .
RightButton - Returns the state of the right mouse button as type ButtonState .
X - Species the horizontal position of the mouse cursor in pixels relative to the upper left corner of
the game window.
Y - Species the vertical position of the mouse cursor in pixels relative to the upper left corner of
the game window.
The ButtonState enumeration
According to the documentation
13
,
ButtonState
is an Enumeration that
"Identies the state of a mouse button or Xbox 360 Controller button."
It has the following members:
•
•
Pressed - The button is pressed.
Released - The button is released.
Test for a left mouse button pressed
The code in Listing 10 (p. 300) tests to determine if the left mouse button is pressed. If so, it sets the
position of the ladybug object referred to by index [0] in the list of ladybugs to the current position of the
mouse pointer.
A change in ladybug position
This change in position becomes visible the next time the ladybug is drawn. This has the eect of causing
the ladybug object to follow the mouse pointer throughout the game window. It is even possible for the
mouse to drag a ladybug outside the game window and leave it there.
Do the same for the other ladybug object
The code in Listing 10 (p. 300) also does essentially the same thing, but applies the operation to the
ladybug object referred to by index [1] in the list of ladybugs.
Methods of the Mouse class
In addition to the GetState method mentioned earlier, the Mouse class has one other static method
that is not inherited from the Object class. It is named SetPosition 14 , and it
"Sets the position of the mouse cursor relative to the upper-left corner of the window."
You saw this method used to place the mouse cursor in the center of the game window in Listing 7 (p. 296)
.
The end of the overridden Update method
Listing 10 (p. 300) also signals the end of the overridden
Update
method.
12 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.input.mousestate.aspx
13 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.input.buttonstate(XNAGameStudio.20).aspx
14 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.input.mouse.setposition.aspx
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
302
CHAPTER 18. XNA0132-A SIMPLE GAME PROGRAM WITH ON-SCREEN
TEXT
18.5.3.2.3 The overridden Game.Draw method
The overridden Game.Draw method is shown in its entirety in Listing 11 (p.
Listing 11 . The overridden Game.Draw method.
302) .
protected override void Draw(GameTime gameTime) {
spriteBatch.Begin();
spiderWeb.Draw(spriteBatch);//draw background
//Draw all spiders.
for(int cnt = 0;cnt < spiders.Count;cnt++) {
spiders[cnt].Draw(spriteBatch);
}//end for loop
//Draw all ladybugs.
for(int cnt = 0;cnt < ladybugs.Count;cnt++) {
ladybugs[cnt].Draw(spriteBatch);
}//end for loop
//Draw the output text.
string output = "";
if(spiderCount == 0){
output = "Congratulations. No spiders made it to"
+ " the bottom.";
}else{
output = "Oops, " + spiderCount + " or more "
+ "spiders made it to the bottom.";
}//end else
// Find the center of the string
Vector2 FontOrigin =
Font1.MeasureString(output) / 2;
// Draw the string
spriteBatch.DrawString(Font1,
output,
FontPos,
Color.Yellow,
0,//angle
FontOrigin,
1.0f,//scale
SpriteEffects.None,
0.0f);//layer depth
spriteBatch.End();
base.Draw(gameTime);
}//end Draw method
//-------------------------------------------------//
}//end class
}//end namespace
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
303
18.5.3.2.3.1 The MeasureString method
Other than the MeasureString 15 method of the SpriteFont
that I haven't explained before.
The MeasureString method
16
class, there is nothing in Listing 11 (p. 302)
"Returns the height and width of a given string as a Vector2."
18.5.3.2.3.2 Overloaded division operator
The statement that declares and initializes the variable named FontOrigin in Listing 11 (p. 302) is very
interesting. Recall that a Vector2 object has two elds, X and Y. Note that the code in Listing 11 (p.
302) divides the object by 2. I didn't say that the code extracts the elds and divides them by 2. I said
that the code divides the object by 2. This must mean that the division operator (/) is overloaded by the
Vector2 class such that dividing a Vector2 object by a scalar value divides the elds by the scalar value.
If you know what I am talking about when I talk about operator overloading, that is good. If not,
don't worry about it. That topic is far beyond the technical requirements for the introductory XNA game
programming course that I teach.
18.5.3.3 The end of the program
Listing 11 (p. 302) signals the end of the overridden
and the end of the program.
Game.Draw
method, the end of the
Game1
class,
18.6 Run the program
I encourage you to copy the code from Listing 13 (p. 306) and Listing 14 (p. 312) . Use that code to create
an XNA project. You should be able to download suitable images from the Internet. Compile and run the
project. Experiment with the code, making changes, and observing the results of your changes. Make certain
that you can explain why your changes behave as they do.
18.7 Run my text program
Click here 17 to download a zip le containing my version of the program named XNA0132ProjA .
Extract the folder named XNA0132ProjA from the zip le and save it somewhere on your disk. Start
Visual C# 2010 Express and select Open Project... from the File menu. Navigate to the project
folder and select the le with the extension of .sln . This should cause the project to open and be ready
to run or debug as described in the earlier module titled Getting Started 18 .
18.8 Run my game program
Click here 19 to download a zip le containing my version of the program named XNA0132Proj . Extract
the folder named XNA0132Proj from the zip le and save it somewhere on your disk. Start Visual
C# 2010 Express and select Open Project... from the File menu. Navigate to the project folder
and select the le with the extension of .sln . This should cause the project to open and be ready to run
or debug as described in the earlier module titled Getting Started .
15 http://msdn.microsoft.com/en-us/library/bb464128.aspx
16 http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.spritefont.aspx
17 http://cnx.org/content/m49529/latest/XNA0132ProjA.zip
18 http://cnx.org/contents/GY804-eY:g-5oRzQu
19 http://cnx.org/content/m49529/latest/XNA0132Proj.zip
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
304
18.9 Summary
CHAPTER 18. XNA0132-A SIMPLE GAME PROGRAM WITH ON-SCREEN
TEXT
You learned how to write a simple game program involving user input from the keyboard and the mouse.
You also learned how to create on-screen text.
18.10 What's next?
At this point, the main tool that we are missing for creating more interesting 2D arcade style games is the
ability to incorporate sound eects and music into the games. The research and study of that topic will be
left as an exercise for the student.
18.11 Miscellaneous
This section contains a variety of miscellaneous information.
Housekeeping material
• Module name: Xna0132-A Simple Game Program with On-Screen Text
• File: Xna0132.htm
• Published: 02/28/14
Disclaimers: Financial : Although the Connexions site makes it possible for you to download
a PDF le for this module at no charge, and also makes it possible for you to purchase a pre-printed
version of the PDF le, you should be aware that some of the HTML elements in this module may
not translate well into PDF.
I also want you to know that, I receive no nancial compensation from the Connexions website even
if you purchase the PDF version of the module.
In the past, unknown individuals have copied my modules from cnx.org, converted them to Kindle
books, and placed them for sale on Amazon.com showing me as the author. I neither receive
compensation for those sales nor do I know who does receive compensation. If you purchase such
a book, please be aware that it is a copy of a module that is freely available on cnx.org and that it
was made and published without my prior knowledge.
Aliation :: I am a professor of Computer Information Technology at Austin Community College
in Austin, TX.
18.12 Complete program listing
Listing 12
. Game1 class for project XNA0132ProjA.
/*Project XNA0132ProjA
********************************************************/
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace XNA0132ProjA {
public class Game1 : Microsoft.Xna.Framework.Game {
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
305
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
SpriteFont Font1;
Vector2 FontPos;
//-------------------------------------------------//
protected override void LoadContent() {
spriteBatch = new SpriteBatch(GraphicsDevice);
//Create a new SpriteFont object.
Font1 = Content.Load<SpriteFont>("Lindsey");
//Create a new Vector2 object to center the text
// in the game window.
FontPos = new Vector2(
graphics.GraphicsDevice.Viewport.Width / 2,
graphics.GraphicsDevice.Viewport.Height / 2);
}//end LoadContent method
//-------------------------------------------------//
protected override void Draw(GameTime gameTime) {
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
// Draw Lindsey Font
string output = "Lindsey Font";
// Find the center of the string
Vector2 FontOrigin =
Font1.MeasureString(output) / 2;
//Draw the string with the FontOrigin at the
// position specified by FontPos.
spriteBatch.DrawString(
Font1,//font to use
output,//output text
FontPos,//position re upper left corner
Color.LightGreen,//color of text
-45,//rotation of text
FontOrigin,//place this at FontPos
2.5f,//scale
SpriteEffects.None,
0.5f);//z-order layer
spriteBatch.End();
base.Draw(gameTime);
}//end Draw method
//-------------------------------------------------//
protected override void Update(GameTime gameTime) {
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 18. XNA0132-A SIMPLE GAME PROGRAM WITH ON-SCREEN
TEXT
306
// Allows the game to exit
if(GamePad.GetState(PlayerIndex.One).
Buttons.Back == ButtonState.Pressed)
this.Exit();
//No special update code required.
base.Update(gameTime);
}//end Update method
//-------------------------------------------------//
protected override void Initialize() {
//Not needed
base.Initialize();
}//end Initialize
//-------------------------------------------------//
public Game1() {//Typical constructor
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}//end constructor
//-------------------------------------------------//
protected override void UnloadContent() {
//Not needed
}//end UnloadContent
//-------------------------------------------------//
}//end class
}//end namespace
Listing 13
. Sprite class for project XNA0132Proj.
/*Project XNA0132Proj
* This file defines a Sprite class from which a Sprite
* object can be instantiated. This version supports
* collision detection based on intersecting rectangles.
* It also provides an Edge property that records and
* returns the edge number if a sprite collides with an
* edge. However, the edge information is available for
* only one iteration of the game loop. Normally the
* value of Edge is 0. However, it changes to 1,2,3,or4
* if a sprite collides with the top, right, bottom, or
* left edge of the game window.
*******************************************************/
using
using
using
using
using
System;
System.Collections.Generic;
Microsoft.Xna.Framework;
Microsoft.Xna.Framework.Content;
Microsoft.Xna.Framework.Graphics;
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
307
namespace XNA0132Proj {
class Sprite {
private int edge = 0;//new to this version
private Texture2D image;
private Vector2 position = new Vector2(0,0);
private Vector2 direction = new Vector2(0,0);
private Point windowSize;
private Random random;
double elapsedTime;//in milliseconds
//The following value is the inverse of speed in
// moves/msec expressed in msec/move.
double elapsedTimeTarget;
//-------------------------------------------------//
//New to this version.
//Edge property accessor
public int Edge {
get {
return edge;
}//end get
}//end Edge property accessor
//-------------------------------------------------//
//Image property accessor
public Texture2D Image {
get {
return image;
}//end get
}//end Image property accessor
//-------------------------------------------------//
//Position property accessor
public Vector2 Position {
get {
return position;
}
set {
position = value;
}//end set
}//end Position property accessor
//-------------------------------------------------//
//WindowSize property accessor
public Point WindowSize {
set {
windowSize = value;
}//end set
}//end WindowSize property accessor
//-------------------------------------------------//
//Direction property accessor
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
308
CHAPTER 18. XNA0132-A SIMPLE GAME PROGRAM WITH ON-SCREEN
TEXT
public Vector2 Direction {
get {
return direction;
}
set {
direction = value;
}//end set
}//end Direction property accessor
//-------------------------------------------------//
//Speed property accessor. The set side should be
// called with speed in moves/msec. The get side
// returns speed moves/msec.
public double Speed {
get {
//Convert from elapsed time in msec/move to
// speed in moves/msec.
return elapsedTimeTarget / 1000;
}
set {
//Convert from speed in moves/msec to
// elapsed time in msec/move.
elapsedTimeTarget = 1000 / value;
}//end set
}//end Speed property accessor
//-------------------------------------------------//
//This constructor loads an image for the sprite
// when it is instantiated. Therefore, it requires
// an asset name for the image and a reference to a
// ContentManager object.
//Requires a reference to a Random object. Should
// use the same Random object for all sprites to
// avoid getting the same sequence for different
// sprites.
public Sprite(String assetName,
ContentManager contentManager,
Random random) {
image = contentManager.Load<Texture2D>(assetName);
image.Name = assetName;
this.random = random;
}//end constructor
//-------------------------------------------------//
//This method can be called to load a new image
// for the sprite.
public void SetImage(String assetName,
ContentManager contentManager) {
image = contentManager.Load<Texture2D>(assetName);
image.Name = assetName;
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
309
}//end SetImage
//-------------------------------------------------//
//This method causes the sprite to move in the
// direction of the direction vector if the elapsed
// time since the last move exceeds the elapsed
// time target based on the specified speed.
public void Move(GameTime gameTime) {
//New to this version
//Clear the Edge property value. Edge information
// is available for only one iteration of the
// game loop.
edge = 0;
//Accumulate elapsed time since the last move.
elapsedTime +=
gameTime.ElapsedGameTime.Milliseconds;
if(elapsedTime > elapsedTimeTarget) {
//It's time to make a move. Set the elapsed
// time to a value that will attempt to produce
// the specified speed on the average.
elapsedTime -= elapsedTimeTarget;
//Add the direction vector to the position
// vector to get a new position vector.
position = Vector2.Add(position,direction);
//Check for a collision with an edge of the game
// window. If the sprite reaches an edge, cause
// the sprite to wrap around and reappear at the
// other edge, moving at the same speed in a
// different direction within the same quadrant
// as before. Also set the Edge property to
// indicate which edge was involved. 1 is top, 2
// is right, 3 is bottom, and 4 is left.
// Note that the Edge property will be cleared
// to 0 the next time the Move method is called.
if(position.X < -image.Width) {
position.X = windowSize.X;
edge = 4;//collision with the left edge - new
NewDirection();
}//end if
if(position.X > windowSize.X) {
position.X = -image.Width / 2;
edge = 2;//collision with the right edge - new
NewDirection();
}//end if
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
310
CHAPTER 18. XNA0132-A SIMPLE GAME PROGRAM WITH ON-SCREEN
TEXT
if(position.Y < -image.Height) {
position.Y = windowSize.Y;
edge = 1;//collision with the top - new
NewDirection();
}//end if
if(position.Y > windowSize.Y) {
position.Y = -image.Height / 2;
edge = 3;//collision with the bottom - new
NewDirection();
}//end if on position.Y
}//end if on elapsed time
}//end Move
//-------------------------------------------------//
//This method determines the length of the current
// direction vector along with the signs of the X
// and Y components of the current direction vector.
// It computes a new direction vector of the same
// length with the X and Y components having random
// lengths and the same signs.
//Note that random.NextDouble returns a
// pseudo-random value, uniformly distributed
// between 0.0 and 1.0.
private void NewDirection() {
//Get information about the current direction
// vector.
double length = Math.Sqrt(
direction.X * direction.X +
direction.Y * direction.Y);
Boolean xNegative =
(direction.X < 0) ? true : false;
Boolean yNegative =
(direction.Y < 0) ? true : false;
//Compute a new X component as a random portion of
// the vector length.
direction.X =
(float)(length * random.NextDouble());
//Compute a corresponding Y component that will
// keep the same vector length.
direction.Y = (float)Math.Sqrt(length * length direction.X * direction.X);
//Set the signs on the X and Y components to match
// the signs from the original direction vector.
if(xNegative)
direction.X = -direction.X;
if(yNegative)
direction.Y = -direction.Y;
}//end NewDirection
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
311
//-------------------------------------------------//
public void Draw(SpriteBatch spriteBatch) {
//Call the simplest available version of
// SpriteBatch.Draw
spriteBatch.Draw(image,position,Color.White);
}//end Draw method
//-------------------------------------------------//
//Returns the current rectangle occupied by the
// sprite.
public Rectangle GetRectangle() {
return new Rectangle((int)(position.X),
(int)(position.Y),
image.Width,
image.Height);
}//end GetRectangle
//-------------------------------------------------//
//This method receives a list of Sprite objects as
// an incoming parameter. It tests for a collision
// with the sprites in the list beginning with the
// sprite at the head of the list. If it detects a
// collision, it stops testing immediately and
// returns a reference to the Sprite object for
// which it found the collision. If it doesn't find
// a collision with any sprite in the list, it
// returns null.
//A collision is called if the rectangle containing
// this object's image intersects the rectangle
// containing a target sprite's image.
public Sprite IsCollision(List<Sprite> target) {
Rectangle thisRectangle =
new Rectangle((int)(position.X),
(int)(position.Y),
image.Width,
image.Height);
Rectangle targetRectangle;
int cnt = 0;
while(cnt < target.Count) {
targetRectangle = target[cnt].GetRectangle();
if(thisRectangle.Intersects(targetRectangle)) {
return target[cnt];
}//end if
cnt++;
}//end while loop
return null;//no collision detected
}//end IsCollision
//-------------------------------------------------//
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
CHAPTER 18. XNA0132-A SIMPLE GAME PROGRAM WITH ON-SCREEN
TEXT
312
}//end class
}//end namespace
Listing 14
. Game1 class for project XNA0132Proj.
/*Project XNA0132Proj
* This project demonstrates how to write a simple 2D
* arcade style game. Ten spiders try to make it across
* a web from top to bottom in opposition to two
* ladybugs. If a ladybug collides with a spider, the
* spider is eaten by the ladybug.
*
* The ladybugs can be moved either with the keyboard or
* the mouse. Press the arrow keys to move one of the
* ladybugs. Press the A key plus the arrow keys to move
* the other ladybug.
*
* Press the left mouse button to drag one of the
* ladybugs with the mouse. Press the right arrow key
* to drag the other ladybug with the mouse.
*
* If a spider makes it from the top to the bottom of
* the game window, it wraps back to the top and starts
* the trip over.
*
* On-screen text keeps track of the number of spider
* crossings at the bottom of the game window. Note,
* however, that if two spiders cross the bottom in
* very close proximity, they may not both get counted.
*
* This program demonstrates how to display on-screen
* text. Note however that it is necessary to create
* a font resource before you can display onscreen text.
* *****************************************************/
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using XNA0132Proj;
namespace XNA0132Proj {
public class Game1 : Microsoft.Xna.Framework.Game {
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
//Use the following values to set the size of the
// client area of the game window. The actual window
// with its frame is somewhat larger depending on
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
313
// the OS display options. On my machine with its
// current display options, these dimensions
// produce a 1024x768 game window.
int windowWidth = 1017;
int windowHeight = 738;
//This is the length of the greatest distance in
// pixels that any sprite will move in a single
// frame of the game loop.
double maxVectorLength = 5.0;
Sprite spiderWeb;//reference to a background sprite.
//References to the spiders are stored in this
// List object.
List<Sprite> spiders = new List<Sprite>();
int numSpiders = 10;//Number of spiders.
//The following value should never exceed 60 moves
// per second unless the default frame rate is also
// increased to more than 60 frames per second.
double maxSpiderSpeed = 30;//moves per second
//References to the Ladybugs are stored in this List.
List<Sprite> ladybugs = new List<Sprite>();
int numLadybugs = 2;//Max number of ladybugs
//Random number generator. It is best to use a single
// object of the Random class to avoid the
// possibility of using different streams that
// produce the same sequence of values.
//Note that the random.NextDouble() method produces
// a pseudo-random value where the sequence of values
// is uniformly distributed between 0.0 and 1.0.
Random random = new Random();
//The following variable is used to count the number
// of spiders that make it past the ladybugs and
// reach the bottom of the game window.
int spiderCount = 0;
SpriteFont Font1;//font for on-screen text
Vector2 FontPos;//position of on-screen text
//-------------------------------------------------//
public Game1() {//constructor
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
//Set the size of the game window.
graphics.PreferredBackBufferWidth = windowWidth;
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
314
CHAPTER 18. XNA0132-A SIMPLE GAME PROGRAM WITH ON-SCREEN
TEXT
graphics.PreferredBackBufferHeight = windowHeight;
}//end constructor
//-------------------------------------------------//
protected override void Initialize() {
//No initialization required.
base.Initialize();
}//end Initialize
//-------------------------------------------------//
protected override void LoadContent() {
spriteBatch = new SpriteBatch(GraphicsDevice);
Font1 = Content.Load<SpriteFont>("Kootenay");
IsMouseVisible = true;//make mouse visible
//Create a sprite for the background image.
spiderWeb =
new Sprite("spiderwebB",Content,random);
spiderWeb.Position = new Vector2(0f,0f);
//Instantiate all of the spiders and cause them to
// move from left to right, top to
// bottom. Pass a reference to the same Random
// object to all of the sprites.
for(int cnt = 0;cnt < numSpiders;cnt++) {
spiders.Add(
new Sprite("blackWidowSpider",Content,random));
//Set the position of the current spider at a
// random location within the game window but
// near the top of the game window.
spiders[cnt].Position = new Vector2(
(float)(windowWidth * random.NextDouble()),
(float)((windowHeight/10) *
random.NextDouble()));
//Get a direction vector for the current spider.
// Make both components positive to cause the
// vector to point down and to the right.
spiders[cnt].Direction = DirectionVector(
(float)maxVectorLength,
(float)(maxVectorLength * random.NextDouble()),
false,//xNeg
false);//yNeg
//Notify the spider object of the size of the
// game window.
spiders[cnt].WindowSize =
new Point(windowWidth,windowHeight);
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
315
//Set the speed in moves per second for the
// current spider to a random value between
// maxSpiderSpeed/2 and maxSpiderSpeed.
spiders[cnt].Speed = maxSpiderSpeed / 2
+ maxSpiderSpeed * random.NextDouble() / 2;
}//end for loop
//Instantiate all of the ladybugs.They move under
// control of the keyboard or the mouse.
for(int cnt = 0;cnt < numLadybugs;cnt++) {
ladybugs.Add(
new Sprite("ladybug",Content,random));
//Position the ladybugs at a random position
// near the bottom of the game window.
ladybugs[cnt].Position = new Vector2(
(float)(windowWidth * random.NextDouble()),
(float)(windowHeight ladybugs[cnt].Image.Height));
}//end for loop
//Position the on-screen text.
FontPos = new Vector2(windowWidth / 2, 50);
//Position the mouse pointer in the center of the
// game window.
Mouse.SetPosition(windowWidth / 2,windowHeight /2);
}//end LoadContent
//-------------------------------------------------//
//This method returns a direction vector given the
// length of the vector, the length of the
// X component, the sign of the X component, and the
// sign of the Y component. Set negX and/or negY to
// true to cause them to be negative. By adjusting
// the signs on the X and Y components, the vector
// can be caused to point into any of the four
// quadrants.
private Vector2 DirectionVector(float vecLen,
float xLen,
Boolean negX,
Boolean negY) {
Vector2 result = new Vector2(xLen,0);
result.Y = (float)Math.Sqrt(vecLen * vecLen
- xLen * xLen);
if(negX)
result.X = -result.X;
if(negY)
result.Y = -result.Y;
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
316
CHAPTER 18. XNA0132-A SIMPLE GAME PROGRAM WITH ON-SCREEN
TEXT
return result;
}//end DirectionVector
//-------------------------------------------------//
protected override void UnloadContent() {
//No content unload required.
}//end unloadContent
//-------------------------------------------------//
protected override void Update(GameTime gameTime) {
//Tell all the spiders in the list to move.
for(int cnt = 0;cnt < spiders.Count;cnt++) {
spiders[cnt].Move(gameTime);
}//end for loop
//Tell each ladybug to test for a collision with a
// spider and to return a reference to the spider
// if there is a collision. Return null if there is
// no collision.
for(int cnt = 0;cnt < ladybugs.Count;cnt++) {
//Test for a collision between this ladybug and
// all of the spiders in the list of spiders.
Sprite target =
ladybugs[cnt].IsCollision(spiders);
if(target != null) {
//There was a collision. The spider gets eaten.
// Remove it from the list of spiders.
spiders.Remove(target);
}//end if
}//end for loop
//Check to see if any spiders have made it to the
// bottom edge.
for(int cnt = 0;cnt < spiders.Count;cnt++) {
if(spiders[cnt].Edge == 3)
//One or more made it to the bottom edge.
spiderCount += 1;
}//end for loop
//The following code is used to move one or the
// other of two ladybugs using the arrow keys.
// Press only the arrow keys to move one of the
// ladybugs. Press the A or a plus the arrow keys
// to move the other ladybug.
//The ladybugs cannot be moved outside the game
// window.
//When an arrow key is pressed, the ladybug moves
//five pixels per call to the Update method.
//Get the state of the keyboard.
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
317
KeyboardState keyboardState = Keyboard.GetState();
//Execute moves on one ladybug with arrow keys plus
// the A or a key.
if(keyboardState.IsKeyDown(Keys.Left) &&
(keyboardState.IsKeyDown(Keys.A)) &&
(ladybugs[0].Position.X > 0)) {
ladybugs[0].Position = new Vector2(
ladybugs[0].Position.X - 5,
ladybugs[0].Position.Y);
}//end if
if(keyboardState.IsKeyDown(Keys.Right) &&
(keyboardState.IsKeyDown(Keys.A)) &&
(ladybugs[0].Position.X <
(windowWidth - ladybugs[1].Image.Width))) {
ladybugs[0].Position = new Vector2(
ladybugs[0].Position.X + 5,
ladybugs[0].Position.Y);
}//end if
if(keyboardState.IsKeyDown(Keys.Up) &&
(keyboardState.IsKeyDown(Keys.A)) &&
(ladybugs[0].Position.Y > 0)) {
ladybugs[0].Position = new Vector2(
ladybugs[0].Position.X,
ladybugs[0].Position.Y - 5);
}//end if
if(keyboardState.IsKeyDown(Keys.Down) &&
(keyboardState.IsKeyDown(Keys.A)) &&
(ladybugs[0].Position.Y <
(windowHeight - ladybugs[1].Image.Height))) {
ladybugs[0].Position = new Vector2(
ladybugs[0].Position.X,
ladybugs[0].Position.Y + 5);
}//end if
//Execute moves on the other ladybug with arrow
// keys pressed but the A key not pressed.
if(keyboardState.IsKeyDown(Keys.Left) &&
!(keyboardState.IsKeyDown(Keys.A)) &&
(ladybugs[1].Position.X > 0)) {
ladybugs[1].Position = new Vector2(
ladybugs[1].Position.X - 5,
ladybugs[1].Position.Y);
}//end if
if(keyboardState.IsKeyDown(Keys.Right) &&
!(keyboardState.IsKeyDown(Keys.A)) &&
(ladybugs[1].Position.X <
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
318
CHAPTER 18. XNA0132-A SIMPLE GAME PROGRAM WITH ON-SCREEN
TEXT
(windowWidth - ladybugs[1].Image.Width))) {
ladybugs[1].Position = new Vector2(
ladybugs[1].Position.X + 5,
ladybugs[1].Position.Y);
}//end if
if(keyboardState.IsKeyDown(Keys.Up) &&
!(keyboardState.IsKeyDown(Keys.A)) &&
(ladybugs[1].Position.Y > 0)) {
ladybugs[1].Position = new Vector2(
ladybugs[1].Position.X,
ladybugs[1].Position.Y - 5);
}//end if
if(keyboardState.IsKeyDown(Keys.Down) &&
!(keyboardState.IsKeyDown(Keys.A)) &&
(ladybugs[1].Position.Y <
(windowHeight - ladybugs[1].Image.Height))) {
ladybugs[1].Position = new Vector2(
ladybugs[1].Position.X,
ladybugs[1].Position.Y + 5);
}//end if
//The following code is used to drag one or the
// other of two ladybugs using the mouse. Press
// the left mouse button to drag one of the
// ladybugs. Press the right mouse button to drag
// the other ladybug.
//Get the state of the mouse.
MouseState mouseState = Mouse.GetState();
//Press the left mouse button to move one ladybug.
if(mouseState.LeftButton == ButtonState.Pressed) {
ladybugs[0].Position =
new Vector2(mouseState.X,mouseState.Y);
}//end if
//Press the right mouse button to move the other
// ladybug.
if(mouseState.RightButton == ButtonState.Pressed) {
ladybugs[1].Position =
new Vector2(mouseState.X,mouseState.Y);
}//end if
base.Update(gameTime);
}//end Update method
//-------------------------------------------------//
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
319
protected override void Draw(GameTime gameTime) {
spriteBatch.Begin();
spiderWeb.Draw(spriteBatch);//draw background
//Draw all spiders.
for(int cnt = 0;cnt < spiders.Count;cnt++) {
spiders[cnt].Draw(spriteBatch);
}//end for loop
//Draw all ladybugs.
for(int cnt = 0;cnt < ladybugs.Count;cnt++) {
ladybugs[cnt].Draw(spriteBatch);
}//end for loop
//Draw the output text.
string output = "";
if(spiderCount == 0){
output = "Congratulations. No spiders made it to"
+ " the bottom.";
}else{
output = "Oops, " + spiderCount + " or more "
+ "spiders made it to the bottom.";
}//end else
// Find the center of the string
Vector2 FontOrigin =
Font1.MeasureString(output) / 2;
// Draw the string
spriteBatch.DrawString(Font1,
output,
FontPos,
Color.Yellow,
0,//angle
FontOrigin,
1.0f,//scale
SpriteEffects.None,
0.0f);//layer depth
spriteBatch.End();
base.Draw(gameTime);
}//end Draw method
//-------------------------------------------------//
}//end class
}//end namespace
-end-
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
320
INDEX
Index of Keywords and Terms
Keywords are listed by the section with that keyword (page numbers are in parentheses).
Keywords
do not necessarily appear in the text of the page. They are merely associated with that section. Ex.
apples, Ÿ 1.1 (1) Terms are referenced by the page they appear on. Ex. apples, 1
A
C
D
E
F
G
H
I
K
a class named Object, Ÿ 6(51)
abstract data types, Ÿ 5(31)
abstraction, Ÿ 5(31)
animation frame rates, Ÿ 13(157)
assignment compatibility, Ÿ 8(75)
L local variables, Ÿ 5(31)
M method overriding, Ÿ 7(65)
methods, Ÿ 6(51)
mouse, Ÿ 18(283)
multiple inheritance, Ÿ 6(51)
C, Ÿ 3(11), Ÿ 4(17)
casting, Ÿ 8(75)
class hierarchy, Ÿ 8(75)
class inheritance, Ÿ 7(65)
class member access control, Ÿ 5(31)
class variables, Ÿ 5(31)
collision detection, Ÿ 17(257)
color key transparency, Ÿ 14(181)
Console Application, Ÿ 11(111)
constructors, Ÿ 11(111)
O
data validation by property setters and public
access methods, Ÿ 5(31)
R
encapsulation, Ÿ 5(31)
events, Ÿ 6(51)
extend, Ÿ 6(51)
S
P
frame animation, Ÿ 13(157)
GAME 1343 Game and Simulation
Programming, Ÿ 1(1)
game program, Ÿ 18(283)
generic List object, Ÿ 15(209)
generic type, Ÿ 10(99)
HASA, Ÿ 6(51)
how C identies properties, Ÿ 5(31)
how to set and get properties in C, Ÿ 5(31)
information hiding, Ÿ 5(31)
inherit, Ÿ 6(51)
instance variables, Ÿ 5(31)
interface inheritance., Ÿ 7(65)
ISA, Ÿ 6(51)
keyboard, Ÿ 18(283)
T
Object class, Ÿ 10(99)
on-screen text, Ÿ 18(283)
overloaded method, Ÿ 7(65)
polymorphism, Ÿ 7(65)
primitive, Ÿ 8(75)
primitive variables, Ÿ 5(31)
properties, Ÿ 6(51)
public accessor methods, Ÿ 5(31)
public manipulator methods, Ÿ 5(31)
reference types, Ÿ 8(75)
reference variables, Ÿ 5(31)
runtime polymorphism, Ÿ 9(87)
simple Sprite class, Ÿ 15(209)
sprite, Ÿ 12(143)
Sprite class, Ÿ 17(257)
sprite images, Ÿ 13(157)
sprite sheet, Ÿ 13(157)
SpriteBatch.Draw method, Ÿ 14(181)
superclasses, Ÿ 6(51)
the base keyword, Ÿ 11(111)
this keyword, Ÿ 11(111)
type conversion, Ÿ 8(75)
U user input, Ÿ 18(283)
V Visual C 2010 Express Edition, Ÿ 2(3)
W Windows Game application., Ÿ 11(111)
X XMA Game Studio, Ÿ 2(3)
XNA, Ÿ 11(111), Ÿ 12(143), Ÿ 13(157),
Ÿ 15(209), Ÿ 16(229), Ÿ 17(257), Ÿ 18(283)
XNA Game Studio, Ÿ 1(1), Ÿ 4(17)
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
ATTRIBUTIONS
321
Attributions
Collection: XNA Game Studio
Edited by: R.G. (Dick) Baldwin
URL: http://cnx.org/content/col11634/1.6/
License: http://creativecommons.org/licenses/by/4.0/
Module: "Xna0095-Preface"
By: R.G. (Dick) Baldwin
URL: http://cnx.org/content/m49466/1.2/
Page: 1
Copyright: R.G. (Dick) Baldwin
License: http://creativecommons.org/licenses/by/4.0/
Module: "Xna0100-Getting Started"
By: R.G. (Dick) Baldwin
URL: http://cnx.org/content/m49467/1.4/
Pages: 3-9
Copyright: R.G. (Dick) Baldwin
License: http://creativecommons.org/licenses/by/4.0/
Module: "Xna0102-What is C and Why Should You Care"
By: R.G. (Dick) Baldwin
URL: http://cnx.org/content/m49469/1.4/
Pages: 11-15
Copyright: R.G. (Dick) Baldwin
License: http://creativecommons.org/licenses/by/4.0/
Module: "Xna0104-What is OOP and Why Should You Care?"
By: R.G. (Dick) Baldwin
URL: http://cnx.org/content/m49475/1.3/
Pages: 17-29
Copyright: R.G. (Dick) Baldwin
License: http://creativecommons.org/licenses/by/4.0/
Module: "Xna0106-Encapsulation in C"
By: R.G. (Dick) Baldwin
URL: http://cnx.org/content/m49472/1.2/
Pages: 31-50
Copyright: R.G. (Dick) Baldwin
License: http://creativecommons.org/licenses/by/4.0/
Module: "Xna0108-Inheritance in C"
By: R.G. (Dick) Baldwin
URL: http://cnx.org/content/m49498/1.2/
Pages: 51-63
Copyright: R.G. (Dick) Baldwin
License: http://creativecommons.org/licenses/by/4.0/
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
322
ATTRIBUTIONS
Module: "Xna0110-Polymorphism Based on Overloaded Methods"
By: R.G. (Dick) Baldwin
URL: http://cnx.org/content/m49505/1.2/
Pages: 65-74
Copyright: R.G. (Dick) Baldwin
License: http://creativecommons.org/licenses/by/4.0/
Module: "Xna0112-Type Conversion, Casting, and Assignment Compatibility"
By: R.G. (Dick) Baldwin
URL: http://cnx.org/content/m49500/1.2/
Pages: 75-86
Copyright: R.G. (Dick) Baldwin
License: http://creativecommons.org/licenses/by/4.0/
Module: "Xna0114-Runtime Polymorphism through Class Inheritance"
By: R.G. (Dick) Baldwin
URL: http://cnx.org/content/m49508/1.2/
Pages: 87-98
Copyright: R.G. (Dick) Baldwin
License: http://creativecommons.org/licenses/by/4.0/
Module: "Xna0116-Runtime Polymorphism and the Object Class"
By: R.G. (Dick) Baldwin
URL: http://cnx.org/content/m49506/1.2/
Pages: 99-109
Copyright: R.G. (Dick) Baldwin
License: http://creativecommons.org/licenses/by/4.0/
Module: "Xna0118-The XNA Framework and the Game Class"
By: R.G. (Dick) Baldwin
URL: http://cnx.org/content/m49509/1.2/
Pages: 111-142
Copyright: R.G. (Dick) Baldwin
License: http://creativecommons.org/licenses/by/4.0/
Module: "Xna0120-Moving Your Sprite and using the Debug Class"
By: R.G. (Dick) Baldwin
URL: http://cnx.org/content/m49511/1.2/
Pages: 143-155
Copyright: R.G. (Dick) Baldwin
License: http://creativecommons.org/licenses/by/4.0/
Module: "Xna0122-Frame Animation using a Sprite Sheet"
By: R.G. (Dick) Baldwin
URL: http://cnx.org/content/m49518/1.2/
Pages: 157-180
Copyright: R.G. (Dick) Baldwin
License: http://creativecommons.org/licenses/by/4.0/
Module: "Xna0124-Using Background Images and Color Key Transparency"
By: R.G. (Dick) Baldwin
URL: http://cnx.org/content/m49515/1.2/
Pages: 181-207
Copyright: R.G. (Dick) Baldwin
License: http://creativecommons.org/licenses/by/4.0/
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
ATTRIBUTIONS
323
Module: "Xna0126-Using OOP - A Simple Sprite Class"
By: R.G. (Dick) Baldwin
URL: http://cnx.org/content/m49520/1.2/
Pages: 209-228
Copyright: R.G. (Dick) Baldwin
License: http://creativecommons.org/licenses/by/4.0/
Module: "Xna0128-Improving the Sprite Class"
By: R.G. (Dick) Baldwin
URL: http://cnx.org/content/m49527/1.2/
Pages: 229-256
Copyright: R.G. (Dick) Baldwin
License: http://creativecommons.org/licenses/by/4.0/
Module: "Xna0130-Collision Detection"
By: R.G. (Dick) Baldwin
URL: http://cnx.org/content/m49524/1.2/
Pages: 257-281
Copyright: R.G. (Dick) Baldwin
License: http://creativecommons.org/licenses/by/4.0/
Module: "Xna0132-A Simple Game Program with On-Screen Text"
By: R.G. (Dick) Baldwin
URL: http://cnx.org/content/m49529/1.3/
Pages: 283-319
Copyright: R.G. (Dick) Baldwin
License: http://creativecommons.org/licenses/by/4.0/
Available for free at Connexions <http://cnx.org/content/col11634/1.6>
XNA Game Studio
This is a collection of modules designed primarily for teaching GAME 1343 Game and Simulation Programming I at Austin Community College in Austin, TX.
About OpenStax-CNX
Rhaptos is a web-based collaborative publishing system for educational material.
Was this manual useful for you? yes no
Thank you for your participation!

* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project

Download PDF

advertisement