Click here to Skip to main content
Email Password   helpLost your password?

Sample Image - asteroids.gif

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:

  1. learning the IDE
  2. learning the syntax
  3. 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.

Running the code

Before building and running the code, you will need to provide the following .wav files:

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

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.

  1. To fulfill the observer role (Observer Pattern, from Design Patters, Gamma, Helm, Johnson & Vlissides) and handle the collision detection
  2. 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.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
JokeTop?
gajatko
2:18 1 Jul '07  
Nice Big Grin

What's your highest score? Big Grin
Generalprojects in play sound in c#
Anonymous
2:07 24 Feb '05  
i want that project

GeneralProblems
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

GeneralRe: Problems
Profox Jase
18:29 28 Mar '02  
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
GeneralRe: Problems
Christian Graus
18:42 28 Mar '02  
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. Frown

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

GeneralRe: Problems
Profox Jase
18:57 28 Mar '02  
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
GeneralRe: Problems
Christian Graus
19:19 28 Mar '02  
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

GeneralRe: Problems
Profox Jase
20:02 28 Mar '02  
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
GeneralRe: Problems
Lawrence Botley
0:48 9 Mar '07  
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

GeneralRe: Problems
Profox Jase
0:54 16 Mar '07  
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

GeneralCurses.
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

GeneralRe: Curses.
Profox Jase
18:33 28 Mar '02  
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
GeneralRe: Curses.
Christian Graus
19:20 28 Mar '02  
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

GeneralRe: Curses.
Mike Nordell
21:48 30 Mar '02  
Curses is a fun idea. Port e.g. PDCurses to C# and then the game to curses! Smile
GeneralRe: Curses.
Profox Jase
9:35 31 Mar '02  
Heh heh

Heavens to Murgatroyd, I am a SnagglePuss!





Jason King

jason.king@profox.co.uk
Feel the love at www.profox.co.uk
GeneralShonky?
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
GeneralRe: Shonky?
Profox Jase
5:55 28 Mar '02  
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
GeneralRe: Shonky?
Christian Graus
14:27 28 Mar '02  
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

GeneralRe: Shonky?
Profox Jase
18:11 28 Mar '02  
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.

Rose Rose Rose

Jason King

jason.king@profox.co.uk
Feel the love at www.profox.co.uk
GeneralRe: Shonky?
Chris Losinger
5:50 28 Mar '02  
half the world was a british colony at one time. Smile
-c



Ah, but a programmer's reach should exceed his grasp, or what are late nights for?

Smaller Animals Software, Inc.

GeneralRe: Shonky?
Anonymous
7:49 28 Mar '02  
much less then a half !
check out the history books.
GeneralRe: Shonky?
Gary McHale
5:06 1 Apr '02  
Not if you count the sea.
GeneralRe: Shonky?
Christian Graus
10:32 28 Mar '02  
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


Last Updated 28 Mar 2002 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010