Introduction
DirectX has changed quite a bit over the years and the new DirectX 9 offers
some new things that previous versions never did. DX9 provides
new functionalities for enhanced 3D manipulations. But one
big change is the addition of a managed DirectX so that you can create
applications using a .Net language and still tie into DirectX. This is
without having to work around a COM Interoperability issue. DirectX9
directly supports managed code and makes life for the DirectX/C# developer much
simpler in the long run. The Managed DirectX classes include a DirectDraw
set of classes to use for 2D manipulation. This 2D interface is not a new
version of DirectDraw - it is a wrapper to get to the DirectDraw7
interface. This is good for a lot of developers that still want to do 2D
games and also want to use a .NET language.
The DirectX SDK can be obtained from
Microsoft at http://msdn.microsoft.com/directx/
Ok, enough of an introduction, let's get to the code...
The Graphics
One of the first things that I did was to convert the BMP files into PNG.
I did this for a couple of reasons. The first is that PNG files are
smaller, so that having them embedded into the executable directly will not
increase the file size as much as bitmaps would. The second reason is to
demonstrate that other file formats can be used to create surfaces from.
This is not directly in DX9, but there are C# classes to read the data in
correctly. I had converted to JPG at first, but the quality setting was
too low and the images did not look as good.
Handling error codes from the calls to methods like Draw
are different
from DX8 too. There is not a returned error code, just a try/catch to get
the errors.
try
{
back.Draw(new Rectangle(150, 275+(40 * iOption),32,20),
Selected, rcRect, DrawFlags.DoNotWait | DrawFlags.KeySource);
}
catch(SurfaceLostException)
{
RestoreSurfaces();
}
The Sounds
Sounds ended up being a very simple thing overall, but I did have a few
problems with it. At first, the samples for using DirectSound seemed
simple enough until you notice that the Device
class that
DirectSound uses is the same class name that DirectDraw uses. This
meant that I had to fully qualify the class names when I created those
variables.
Microsoft.DirectX.DirectDraw.Device displayDevice;
Microsoft.DirectX.DirectSound.Device soundDevice;
To create the SecondaryBuffer
objects to play the sounds, I decided
to use a stream from the executable to get WAV files into the buffers. To
do this a GetManifestResourceStream
was used. The problems
that I had here were that the resource had to be fully qualified with the
assembly name too.
try
{
SkidBuffer = new SecondaryBuffer(
loadedAssembly.GetManifestResourceStream("Invasion.skid.wav"),
soundDevice);
}
catch(SoundException)
{
Console.WriteLine("load sounds error 1");
}
Rest of the Conversion
Overall, the remaining parts that needed to be converted went smoothly. I
removed the linked lists in favor of the ArrayList
class. This really cleaned up the code when it came time to add/remove
UFOs, bullets, and extras. You could argue that using a vector from STL
would clean up the handling of the linked list too or the use of some other
type of container class. I agree that is the reason for the conversion.
One of the tricky parts of converting an application from C++ to C# is
remembering all of the little differences between the languages. For
example, in C++ you can use rand()
to generate a random
number. For C#, random numbers are generated with an instance of the Random
class. This works just great, except for one thing. I
originally setup each UFO with its own random variable.
This worked just fine until I realized that each UFO was getting
the same random numbers. This is because the instance of Random
,
seeds the variable with the current time. This meant that the separate
UFO variables would get a random number generator all seeded with the same
time. Each of the random generators created the exact same numbers.
Once I moved the random number generator to the Invasion
class,
then each UFO had its own values and things worked as expected.
Debugging
Debugging a fullscreen DirectX application is always a tricky thing to
do. For this conversion,
Console.Writeline
was used to print
out data to the debug window. Unhandled exceptions were the root of most of the
crashes that were experienced; this includes
InvalidRectangleException
as the biggest problem. This seems very important with Managed DirectX to
handle all exceptions with a
try/catch
. A second
display could have been used to make things simpler, but having the output in
the debug window after the crash was good enough to fix the issues.
History
The original code for this game came from
Invasion - A computer game using DirectDraw by Mauricio Ritter.
It is a great little game that had all of the right graphics and unlimited game
play. Mauricio did a great job, so I made sure to leave his name in the credits.