|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionWelcome to the second part of my tutorial on Managed DirectX, included with DirectX 9 SDK. Most C# developers were waiting for this release. Before Managed DirectX, C# developers were using DirectX via COM Interop of DirectX 7 or 8 VB component. The new Managed DirectX components offer best performance and easier programming over DirectX COM Interop. This tutorial is for newcomers in DirectX development, like me, or for people who were using COM Interop for DirectX development. In this tutorial, we will make a clone of Super Metroid game called Managed Metroid. We will try to use all the components of Managed DirectX ( In part 2, we will continue with sprite animation and audio playback with RequirementsThese articles require some knowledge of C#. Also a basic game development background would be useful. In this article, I assume that you have read the first part of the tutorial found at http://www.codeproject.com/cs/media/mdx_tutorial1.asp[^]. Software requirements:
Music playbackWith Managed DirectX, if you want a quick way to playback music in your application then you're served. The We implant Audio Playback in a code like this (It's just a sample one, this is not in the demo project) : private void PlayAudio(string audiofile)
{
// Create the audio class with specified audio file
Audio audio = new Audio(audiofile)
// Play Audio
audio.Play();
}
Adding namespaceHere are the references you need to add to the project for this tutorial (in the code and in References folder in solution) using System.IO;
using Microsoft.DirectX;
using Microsoft.DirectX.AudioVideoPlayback;
using Microsoft.DirectX.DirectDraw;
Adding DirectX variablesFirst of all, we declare the DirectDraw device. This time, we also add a constant that hold the number of frames, an array of After that, we declare the Audio playback variables. First of all, the The // Direct Draw variables
//DirectDraw Device used in all the application
private Device display;
//Front Surface
private Surface front = null;
//Back Buffer Surface
private Surface back = null;
//Surface that hold the Samus Sprites
private Surface samus = null;
//Constant of number of frames
private const int nFrames = 8;
//Array of Rectangle to hold the position of the frames
private Rectangle[] frames = new Rectangle[nFrames];
//Sprite Height
private int samus_height = 49;
//Sprite Height
private int samus_width = 35;
//Current frame displayed
private int currentframe = 0;
//Path to the samus sprite
private string samus_sprite = Application.StartupPath + "\\samussprite.bmp";
// Audio Playback variables
//Audio Playback class
private Audio music;
//String of the musicpath
private string musicpath = Application.StartupPath + "\\music\\";
//The playlist initialized later
private string[] m_playlist = null;
//The maxindex, store the upper bounds of the playlist
private int maxindex = 0;
//Current index of the music played
private int musicindex = 0;
// Misc variables
//Timer used to cadence the sprite animation
private System.Windows.Forms.Timer TimerSprite;
//Help Text
private string helptext =
"Managed DirectX Tutorial 2 - Press Enter or Escape to exit";
Initialize audio playbackInitializing audio playback is very simple. Create the private void InitAudioPlayback()
{
// Create the Audio class with the first element of the playlist
music = new Audio(m_playlist[0]);
// Event to handle the ending of the music
music.Ending += new EventHandler(this.OnMusicEnding);
// Play the music
music.Play();
}
FillPlaylist methodThe private string[] FillPlaylist()
{
// Get the number of song
maxindex = Directory.GetFiles(musicpath).Length;
// Create the playlist
string[] playlist = new string[maxindex];
// Fill the playlist
playlist = Directory.GetFiles(musicpath);
// Return
return playlist;
}
OnMusicEnding eventThis function opens the next song in the play list. Instead of creating a new instance of the private void OnMusicEnding(object sender, System.EventArgs e)
{
// Check if the Audio class is created
if(music != null)
{
// Increment the music idnex
musicindex++;
// Stop the current song played
music.Stop();
// If this is the song, return to the first song
if(musicindex >= maxindex - 1)
{
musicindex = 0;
}
try
{
// Open the next song
music.Open(m_playlist[musicindex]);
// Reset the Ending event
music.Ending -= new EventHandler(this.OnMusicEnding);
music.Ending += new EventHandler(this.OnMusicEnding);
// Play it
music.Play();
}
catch(Exception)
{
// If they are any problems, exit the application
this.Close();
}
}
}
Sprite animationMy method to animate a sprite is very simple. Here is the image I will animate:
With Jasc Animation Shop and Paint Shop Pro, I pasted all the frames of the animation into a single image:
So how do I select each frame in my application? We pre-compute the area of each frame and we store it in an array of Each for(int i=0; i< nFrames; i++)
{
Point pt = new Point(samus_width * i, 0);
frames[i] = new Rectangle(pt, new Size(samus_width, samus_height));
}
But now, how do I draw each frame in my // Draw the title to the back buffer using source copy blit
back.DrawFast(320, 240, samus, frames[currentframe],
DrawFastFlags.DoNotWait);
Finally, we add a Windows private void TimerSprite_Tick(object sender, System.EventArgs e)
{
if(currentframe >= nFrames - 1)
{
currentframe = 0;
}
else
{
currentframe++;
}
}
DirectDraw initializationNow too much code has changed here. We added the pre-compute of the animation frames. private void InitDirectDraw()
{
// Used to describe a Surface
SurfaceDescription description = new SurfaceDescription();
// Init the Device
display = new Device();
// Set the Cooperative Level and parent,
// Setted to Full Screen Exclusive to the form
display.SetCooperativeLevel(this,
CooperativeLevelFlags.FullscreenExclusive);
// Set the resolution and color depth used in full screen
//(640x480, 16 bit color)
display.SetDisplayMode(640, 480, 16, 0, false);
// Define the attributes for the front Surface
description.SurfaceCaps.PrimarySurface = true;
description.SurfaceCaps.Flip = true;
description.SurfaceCaps.Complex = true;
// Set the Back Buffer count
description.BackBufferCount = 1;
// Create the Surface with specifed description and device)
front = new Surface(description, display);
description.Clear();
// A Caps is a set of attributes used by most of DirectX components
SurfaceCaps caps = new SurfaceCaps();
// Yes, we are using a back buffer
caps.BackBuffer = true;
// Associate the front buffer to back buffer with specified caps
back = front.GetAttachedSurface(caps);
description.Clear();
samus = new Surface(samus_sprite, description, display);
for(int i=0; i< nFrames; i++)
{
Point pt = new Point(samus_width * i, 0);
frames[i] = new Rectangle(pt, new Size(samus_width, samus_height));
}
// Set the fore color of the text
back.ForeColor = Color.White;
}
DrawNextFrame methodThe private void DrawNextFrame()
{
// If the front isn't create, ignore this function
if (front == null)
{
return;
}
// If the form is minimized, ignore this function
if(this.WindowState == FormWindowState.Minimized)
{
return;
}
try
{
back.ColorFill(Color.Black);
// Draw the title to the back buffer using source copy blit
back.DrawFast(320, 240, samus,
frames[currentframe], DrawFastFlags.DoNotWait);
// Draw the text also to the back buffer using source copy blit
back.DrawText(10, 30, helptext, false);
back.DrawText(10, 50, "Playing : "+m_playlist[musicindex], false);
// Doing a flip to transfer back buffer to the front, faster
front.Flip(back, FlipFlags.DoNotWait);
}
catch(WasStillDrawingException)
{
return;
}
catch(SurfaceLostException)
{
// If we lost the surfaces, restore the surfaces
RestoreSurfaces();
}
}
RestoreSurfaces methodNo changes for this method private void RestoreSurfaces()
{
// Used to describe a Surface
SurfaceDescription description = new SurfaceDescription();
// Restore al the surface associed with the device
display.RestoreAllSurfaces();
// For the samus sprite, we need to dispose it first
// and then re-create it
samus.Dispose();
samus = null;
samus = new Surface(samus_sprite, description, display);
return;
}
ConstructorIn the constructor, we add the fill of the play list and we add the start of the timer. public Tutorial2()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
m_playlist = FillPlaylist();
InitDirectDraw();
InitAudioPlayback();
// Remove the cursor
this.Cursor.Dispose();
// Show the form if isn't already do
this.Show();
TimerSprite.Start();
TimerSprite.Enabled = true;
// The main loop
while(Created)
{
DrawNextFrame();
// Make sure that the application process the messages
Application.DoEvents();
}
}
The Closing eventThe private void Tutorial2_Closing(object sender,
System.ComponentModel.CancelEventArgs e)
{
if(display != null)
{
display.RestoreDisplayMode();
display.SetCooperativeLevel(this, CooperativeLevelFlags.Normal);
display.Dispose();
}
if(music != null)
{
if(music.Playing)
{
music.Stop();
}
music.Dispose();
}
}
The KeyUp eventThe private void Tutorial2_KeyUp(object sender,
System.Windows.Forms.KeyEventArgs e)
{
// If the user press Escape or Enter, the tutorial exits
if(e.KeyCode == Keys.Escape || e.KeyCode == Keys.Enter)
this.Close();
}
Main methodYou need to make the same change as the first part. [STAThread]
static void Main()
{
Tutorial2 app = new Tutorial2();
Application.Exit();
}
ConclusionIn this tutorial, we learned how to animate a sprite using Known issues
HistoryUpdate 1.0
| ||||||||||||||||||||