
Introduction
Good news everybody! I have invented an asteroids game with a slight twist;
one that doesn't rely on Direct X coding.
The zip now contains all the necessary files, my apologies to those that
downloaded the incomplete version.
This is my first article, so please excuse any untidiness. First off, a
little background about why I wrote this code and article, and my coding style.
I have always loved games, and awhile ago, decided that writing a version of the
classic space invaders game would be a good way of teaching my fellow Visual
Foxpro programmers about object oriented design and coding. I also came to the
conclusion some time ago that perhaps the best way of learning a new coding
language was to separate the learning process out. A typcial path into learning
a new language consists of about 3 parts:
- learning the IDE
- learning the syntax
- coding the solution to some problem.
If you can remember which of these three elements you are performing when you
use a package to write code, learning becomes easier. To make things easier
still, if you don't have to figure out the solution to a problem, you can
maximize on points 1 and 2. Therefore, I decided that to learn C# and its IDE,
it would be best to reinvent the wheel that is space invaders, since I already
knew at least one solution.
Well, I had already cracked a rudimentary space invaders, so half way
through, I broke my own rule and converted it to be asteroids instead.
Sometimes, you just can't resist changing the scope ;-)
The purpose of the project was to get a good introduction to C# - the aim was
not to write perfect code or OO design. That is my disclaimer. My
production code (for money) is always better formatted and structured that this,
honestly. No, really. Heh heh.
The purpose of my article is thus: to show the world my child. I am very
pleased with it, especially since this time around I have cracked keyboard
handling for games (thanks to Lutz
Roeder and his port of the old game Digger) and that with the exception of
Hello World, this is my first real C# code experience. Secondly, sharing is
good. Thirdly, people may learn from it and finally, I would like other people
to suggest / code alterations for the dodgy aspects of this code. I would very
much appreciate constructive feedback - preferably with code examples. Don't
pull any punches guys, I want to learn.
For those observant enough, the slight twist mentioned above is this: the
game has a 1 in 10 chance of the game play changing each time all the asteroids
are destroyed - asteroids may bounce of the edges of the screen instead of
wrapping to the other side. I call this pinball or dodgem mode. Next, there is
also a 1 in 10 chance of asteroids being able to destroy each other (suicide
mode), so a player needs to be quick to accumulate points.
The following is a list of issues outstanding and suggestions for moving the
game forward in a learning context.
- Use of regions for collision detection.
- Class hierarchy is a bit of a kludge (or shonky as our colonial brethren
would say). In need of some refactoring, moving bits and bobs up or down the
hierarchy.
- Re-jig the class members from mainly public declarations to mostly private
scope.
- Refactor playingField to be three new classes:
canvas,
observer (to include collision detection), sound system
- Text message subclass of Player to cope with score and other messages.
- XML High score table
- Database high score table
- Serialisable high score class
- Better understanding of ActiveX wrapper class along with Multimedia control.
- Implement better movement physics of the form y=mx+c, in order to have
better control of the movement speed of game elements.
- Recode the existing missile physics to work using real numbers instead of
just whole numbers (thus increasing accuracy)
- Rename the player class to be something like gameCharacter to avoid
confusion with human player.
- I may be using the concept of static methods / fields incorrectly. This is
the first time I have encountered them (Visual Foxpro doesn't have them).
- Random UFO character that can destroy player / asteroids.
- Network two player version (coop or one at a time).
- Configurable keyboard controls
- Game over code is very gadgy.
- Get rid of the clich�d triangular space ship (heh heh)
Running the code
Before building and running the code, you will need to
provide the following .wav files:
- bigasteroid.wav // for death of big asteroid and missile explosion
- mediumasteroid.wav // for death medium asteroid
- smallasteroid.wav // guess
- player_missile.wav // for launch of missile
- acid bass drum.wav // for start of each new level (make_asteroids)
and alter the following field in the player class:
protected string soundPath = "d:\\my documents\\Visual Studio
Projects\\asteroids\\Asteroids\\"; // root path to the sound files
I have found that borrowing wav files from the OS version of Pinball can be
quite satisfactory for sound effects.
Controls for play
- z - rotate left
- x - rotate right
- j - thrust
- k - fire
- L - dead stop
- p - pause game
Overview
A form contains a playing field and a timer. The playing field
renders and observes all the game characters The timer triggers movement and
animation
Description of Classes
PlayingField Class
This class achieves at least 2 objectives.
- To fulfill the observer role (Observer Pattern, from Design Patters, Gamma,
Helm, Johnson & Vlissides) and handle the collision detection
- To render the characters to the screen
In retrospect, it would be a good idea to separate this functionality as they
are pretty much unrelated.
Collision Detection
Overall, this looks more complicated that it should. There are a lot of null
checks in the code due to my use of arrays instead of arraylists, which I did
not know about to start with. An array list will resize and reindex itself when
elements are added / removed, so this would have help reduce the need for null
checking, but would probably have added complexity to working out which controls
have been checked for collision and rendering, as elements indexes would be
altered transparently by the arraylist class.
The collision detection is reasonably optimized, and there is a flag that can
be set to reduce this optimization for experimental purposes. At the top level
of detection, each character's bounding rectangle is checked for intersection
(and this is the layer that may be turned off, it makes a significant difference
when there are lots of asteroids). The next layer of detection uses point arrays
and graphics paths (graphicsPath.isvisible) to work out whether a
point in a targetA intercepts the graphicsPath of
targetB. Traditionally, I would guess this is usually achieved
using Regions, however, due to ignorance and lack of help, I could not get the
beta c# version to work properly with Region specific code. For any collidable
element, there must therefore be a graphics path member and an array of points
that delimits the outline of the object.
Drawing the characters
My initial concept was for each character in the game to draw itself. When I
tried this, I found that could not get rid of the characters' background area,
and thus any time a character overlapped with another, part of one would be
obscured. This looked far to primitive, so I tried various means of removing the
background, including setting back colours to transparent etc etc. I failed, so
decided that a possible solution was to have a single control responsible for
drawing all characters, and thus, all characters would be drawn on a large,
single background.
In the code, I have called the class PlayingField, but
Canvas would have been more appropriate. PlayingField
has an array called PlayerArray. This is used to store all
the elements of the gameplay (except the score) that require drawing /
animation. For each tick of a timer, this array is iterated and its elements
drawn to the screen. Each element for drawing must have an animation array -
this array is used to store graphics paths and the pens or brushes used to
render them.
A simple character such as an asteroid typically only has 2 elements in its
array, a graphics path constructed from a polygon, and a brush or pen to fill
it. A complex character such as a missile explosion may have more than 2
elements, in this case, an explosion consists of a circle to be drawn with a pen
(outline only), and a smaller circle to be drawn with a brush (filled).
Further coding could produce a text message class, and this could work along
the same lines. It could then be used for displaying the score or any other
messages to be written / animated on the screen, for example, when in suicide
mode or pinball / dodgem mode.
The Player Class
The parent class for any game element that
requires movement or animation or collision detection. As stated earlier, this
needs alteration so that some of the methods are moved down the hierarchy tree
(for example, there is no point an asteroid having a launch_missile
method, and only a human player needs to care about lastKeyPressed).
Most of the fields are self-explanatory and / or heavily commented in the
code, so I won't bother with them on the whole. Those of more a complex nature
are as follows:
public object[,] animationArray; This will store a graphics path
and pen or brush for rendering with. Each instance may have more than one
graphicsPath and Pen/Brush pair. This way it is possible to layer up various
colours and shapes for your animations. For example, the missile explosion
consists of an expanding filled circle outlined with an expanding circle outline
in a different color, thus having a [2,2] array (ie two pairs, compared with an
asteroid's [1,2] array).
public Point[] shapeArray; An array of points that serves 2
purposes: to define the shape used to populate the graphics path, to take part
in collision detection. When somebody comes up with region intersection code for
the collision, this array may become redundant.
make_move() and animate(): There is a fine line
between these definitions, but have separated them out to two methods, since it
seemed worthwhile. make_move() deals with the physics of movement (whether items
bounce, screen wrap, speed and direction). animate() deals with colour, size and
shape changes. This is where other systems would perhaps use and array of
preloaded bitmaps.
The sound system
There are two versions of this, one that accommodates
the beta2 version of c#, and the other accommodates the final released code for
c#.
The beta version is commented out in the code. Unfortunately, I could find no
documentation for the final version of the Microsoft multimedia control that I
use, so again, a bit of an experiment, but at least it works. Thanks to Jerzy
Peter and Peter Stephens for helping me with the initial sound system which
used the winmm.dll for playing sounds. I have left some of that code in the
source files for reference. Unfortunately, I could not make this play more than
one sound at a time (but it did force me to dabble with threads to try and make
it work), so came up with the idea of using an array of ActiveX multimedia
players to achieve this.
To make the sound player class, I just dumped a multimedia player on a form
and looked at the code generated by the IDE, I don't fully understand what is
happening with the ActiveX wrappers used by C#, but again, it works, so good
enough for now.
Playing a sound effect
You can set up the sound player array to have more players if you want more
sounds at one time. The code for that is self explanatory. I have left it at 6.
When a multimedia player plays a sound, it does not allow sharing of the wav
file in use. To get around this, I create a copy of the passed in file and use
the copy instead. The file is then deleted when the player sends its
done_event. I initially thought that copying a file would be a
little inefficient, so I wrote some code to copy the file using
FileStreams instead of the static File.Copy methods.
My tests show that there was little difference, and so long as the files are
small enough, the system works.
In order to delete the temporary sounds files, I had to force the players to
release them, I achieve this by throwing the player into error by asking it to
load an empty filename. I know this is gadgy coding, but send your kludge medals
to the above email address anyway. Unfortunately, this error seems to make the
sound effects slip out of synch with the game slightly.
Conclusion
Finally, I hope you have enjoyed my code and article, I wanted it to be
beginner level (step by step) but that is such a huge job, it would have
taken too long given my current circumstances. Kudos to those that do write 'em,
they are time consuming.
My next article will be about a tree simulator I have written that produces a
simple tree like image with sweeping colours. Its very pretty.
Many thanks to all at Codeproject, especially those that suggested methods
for removal of background and the sound system.
|
|
 |
 | Top? gajatko | 2:18 1 Jul '07 |
|
 |
Nice 
What's your highest score?
|
|
|
|
 |
 | projects in play sound in c# Anonymous | 2:07 24 Feb '05 |
|
|
 |
 | Problems Christian Graus | 13:13 28 Mar '02 |
|
 |
I can open the project but I cannot open the form because 'form1.resx' does not exist.
I see AxMediaPlayer in the list of references - does that mean you're using DirectSound ? I'd love some more info on this if you are.
Christian
The tragedy of cyberspace - that so much can travel so far, and yet mean so little.
"I'm somewhat suspicious of STL though. My (test,experimental) program worked first time. Whats that all about??!?! - Jon Hulatt, 22/3/2002
|
|
|
|
 |
|
 |
Doh, what a chump. Sorry Christian. Here is the solution, I will try and edit the article so others don't have the same problem.
Make a new solution / project. Whe the IDE creates the standard windows form, view the code and completely replace all of it with the code in the Form1.cs file that you will have originally downloaded.
At this point, all that is left to do is drop a multimedia control onto the form, and this will update the references and the assemblyinfo file. Once you have done this, just delete the multimedia control from the form view window.
The multimedia control can be added to the toolbox in the following way:
Right click your toolbox and choose customise toolbox from the shortcut menu. Locate "Windows Media Player" on the com components tab of the resulting dialogue box. On my setup, it is the one but last entry. Its file is listed as C:\WINNT\System32\msdxm.ocx
The bad news is that I am not using Direct Sound directly. However, the active x control that is the media player probably is, but I have no knowledge of direct sound myself. If you learn anything, please do let me know. As far as I know (read it somewhere), Microsoft will be releasing Direct X 9 this summer (UK summer) and I suspect that the .net framework will be extended to include it, at which point I will be disappointed if MS do not include copious volumes of help files and documentation.;)
Jason King
jason.king@profox.co.uk Feel the love at www.profox.co.uk
|
|
|
|
 |
|
 |
Profox Jase wrote: The bad news is that I am not using Direct Sound directly.
I guess that after I posted. I dunno if it uses Direct Sound, but it won't do what I want - lots of sounds playing at once. Grausteroids currently uses Direct Sound to do that. 
The Petzold C# book seems to indicate he's expecting some sound support with the next release of C#. I hope he's right.
Christian
The tragedy of cyberspace - that so much can travel so far, and yet mean so little.
"I'm somewhat suspicious of STL though. My (test,experimental) program worked first time. Whats that all about??!?! - Jon Hulatt, 22/3/2002
|
|
|
|
 |
|
 |
It would be nice to do what Grausteroids does without using the ActiveX control. I have approximated this functionality with my array of media controls though, you could consider using that type of thing I suppose in the meantime.
What is your opinion of the Petzold book? I have never read any of his stuff, though have read reviews (mixed). From what I read about it, it seems to concentrate on GDI stuff...is this true?
Jason King
jason.king@profox.co.uk Feel the love at www.profox.co.uk
|
|
|
|
 |
|
 |
Profox Jase wrote: It would be nice to do what Grausteroids does without using the ActiveX control. I have approximated this functionality with my array of media controls though, you could consider using that type of thing I suppose in the meantime.
The thing is, I know of no other way to play more than one sound at once.
Profox Jase wrote: What is your opinion of the Petzold book? I have never read any of his stuff, though have read reviews (mixed). From what I read about it, it seems to concentrate on GDI stuff...is this true?
Like his Win32 book, it's about 1/2 UI and 1/2 graphics. I think it makes it a great GDI+ reference and a great book on client software in C#. Which happens to be what it claims to be. It's not my only C# book, but it's a valuable one to have.
Christian
The tragedy of cyberspace - that so much can travel so far, and yet mean so little.
"I'm somewhat suspicious of STL though. My (test,experimental) program worked first time. Whats that all about??!?! - Jon Hulatt, 22/3/2002
|
|
|
|
 |
|
 |
I will write my next article this weekend (starts tomorrow) and this will concentrate on playing more than one sounnd at once through an array of Windows Media Players. This is currently implemented in my asteroids code, but I will tidy it up and give a more detailed explanation as to how it works and how I developed it.
In the meantime, here is a basic rundown:
SoundPlayer Class
1. By dumping one instance of a player on a form, I was able to determine the code required to create an instance at runtime. I didn't need to understand all the aspects (and still don't) of the activeX class.
2. Once I had determined how to load and play a file (set autoplay to true, and call open() with a filename) and capture the done_playing event (or whatever it is called), I had enough knowledge to write a new class as an interface to the media player.
3. My new class, called SoundPlayer represents a single invisible instance of the Windows Media Player. When the constructor is called, the SoundPlayer adds an instance of the WMP to itself and sets up a few properties. The bulk of this code I lifted from the code written by the IDE.
4. Two crucial elements of the initialisation are as follows:
this.Player.SendPlayStateChangeEvents = true; this.Player.PlayStateChange += new AxMediaPlayer._MediaPlayerEvents_PlayStateChangeEventHandler(Player_Done);
This means that when the player starts or finishes playing a file, it raises the PlayStateChange event which in turn my wrapper class subscibes to. When this event fires, I call the badly named Player_Done method.
5. The SoundPlayer class has a property (or rather, a field) called isReady. When a file is playing, isReady is false. When a file finishes playing, isReady is reset to true.
Array of Players The next element of my solution creates an array of my new soundPlayer class thus:
public SoundPlayer[] soundPlayers = new SoundPlayer[6];
I then iterate through the array and set up however many sound players I decide I want, as you can see above, I have chosen to start with 6.
// create an array of sound channels for (int i = 0; i<=soundPlayers.GetLength(0)-1; i++) soundPlayers[i] = new SoundPlayer();
Due to poor design choices, this code currently resides in the playingField class, which is inappropriate. Now, when I wish to play a sound, a call the playSound method and pass in the filename of the wav file I wish to play. The code will create a copy of the passed in file (so that it can be used by more than one player) and will search through its array of soundplayers to find one which is marked as isReady = true. It will then pass the copy of the file (well, actually, the filename of the temporary copy) to the ready player and off it goes.
My new design and article will refactor the existing code into two loosely coupled classes, the array class for handling filenames and choosing which player to use, and the player class containing the active x control.
Hope this helps. Its 6 in the morning, and I think I have exorcised my code demons enough to get back to sleep. Goodnight world.
Jason King
jason.king@profox.co.uk Feel the love at www.profox.co.uk
|
|
|
|
 |
|
 |
Man.. that seems to be a lot of work to get the program running..
cant you alter the source for us so we dont need to do this everytime
Within the twentieth century, an ultraintelligent machine will be built and that it will be the last invention that man need make.
I.J. Good
|
|
|
|
 |
|
 |
Hiya Lawrence
Yes, indeed, I suspect the entire thing will a fair bit of work to resurrect it - as I recall I first wrote this in the beta version of .net, and it was pretty much my first ever .net program. I will try to find time to rework this - its a two stage process really, firstly, I should get it working with .net 2.0, followed by a complete re-write, as its horrible code as I recall.
I will try to put some time into this as and when I have a moment, but this will be some time in the making I suspect.
Profox Jase Co-Author, "Cross-Platform .NET Development: Using Mono, Portable.NET, and Microsoft .NET" (Apress, 2004, ISBN: 1-59059-330-8) The Book
|
|
|
|
 |
 | Curses. Christian Graus | 10:34 28 Mar '02 |
|
 |
I was going to start my Asteroids game in C# without DX this weekend....
Actually I probably still will. I have all the graphics and the code in C++ with DX, and I'm interested in the comparison, even if I end up not posting it.
Christian
The tragedy of cyberspace - that so much can travel so far, and yet mean so little.
"I'm somewhat suspicious of STL though. My (test,experimental) program worked first time. Whats that all about??!?! - Jon Hulatt, 22/3/2002
|
|
|
|
 |
|
 |
Heh heh, funnily enough, I have waited about 2 weeks before writing the article and posting it, and my fear was that someone would beat me to it.
I would love to see your C# code Christian, even if you don't post it to the general public.
Bring it on!
Jason King
jason.king@profox.co.uk Feel the love at www.profox.co.uk
|
|
|
|
 |
|
 |
I will probably still do it, because it will require me to learn all the C# stuff I need to at this point.
Christian
The tragedy of cyberspace - that so much can travel so far, and yet mean so little.
"I'm somewhat suspicious of STL though. My (test,experimental) program worked first time. Whats that all about??!?! - Jon Hulatt, 22/3/2002
|
|
|
|
 |
|
 |
Curses is a fun idea. Port e.g. PDCurses to C# and then the game to curses!
|
|
|
|
 |
|
 |
Heh heh
Heavens to Murgatroyd, I am a SnagglePuss!
Jason King
jason.king@profox.co.uk Feel the love at www.profox.co.uk
|
|
|
|
 |
 | Shonky? Jason Gerard | 5:44 28 Mar '02 |
|
 |
Class hierarchy is a bit of a cludge (or shonky as our colonial brethren would say). In need of some refactoring, moving bits and bobs up or down the hierarchy.
I live in the "colonies" as you say, and I've never heard of a shonky.
Jason Gerard
|
|
|
|
 |
|
 |
Heh heh, I picked up shonky from that excellent colonial soap opera that is the mind bending "Neighbours". Additionally, I have heard uttered by Kiwis. AFAIK, it means slightly crooked or otherwise not quite right in some way, for example, "this car is a bit shonky"
Hope this helps.
Jason King
jason.king@profox.co.uk Feel the love at www.profox.co.uk
|
|
|
|
 |
|
 |
Profox Jase wrote: I picked up shonky from that excellent colonial soap opera that is the mind bending "Neighbours".
I refuse to be in the same room if my wife puts Neighbours or Home and Away on. I refer to these shows as a 'mental enema'.
Christian
The tragedy of cyberspace - that so much can travel so far, and yet mean so little.
"I'm somewhat suspicious of STL though. My (test,experimental) program worked first time. Whats that all about??!?! - Jon Hulatt, 22/3/2002
|
|
|
|
 |
|
 |
Heh heh, nice work mate, I bet she still watches it though, right? What is is with soap operas that they are so addictive to the female of the species (watch the flames rise!)?
The one good thing to come from Neighbours is Mark Little (who played Joe Mangle - excellent name) who I have twice seen live in his stand up act in the UK. He really is quality. I believe he started out as a circuit comedian doing working men's clubs. I reckon that must have been pretty gruelling.
Jason King
jason.king@profox.co.uk Feel the love at www.profox.co.uk
|
|
|
|
 |
|
 |
half the world was a british colony at one time.  -c
Ah, but a programmer's reach should exceed his grasp, or what are late nights for?
Smaller Animals Software, Inc.
|
|
|
|
 |
|
 |
much less then a half ! check out the history books.
|
|
|
|
 |
|
 |
Not if you count the sea.
|
|
|
|
 |
|
 |
If something is shonky it means it's dodgy, or not well put together. It's Aussie talk, mate.
Christian
The tragedy of cyberspace - that so much can travel so far, and yet mean so little.
"I'm somewhat suspicious of STL though. My (test,experimental) program worked first time. Whats that all about??!?! - Jon Hulatt, 22/3/2002
|
|
|
|
 |
|
|