UPDATE 10/14/2008: The source code download and demo have been updated for Silverlight 2 RTW.
As I mentioned before, I have been playing around with Silverlight 1.1 Alpha (and now Silveright 2 Beta 1) to both learn and see how well it could handle a casual game such as an asteroids type clone.
Well, check out SILVERLIGHT ROCKS! I have also made the source code available.
As far as the Silverlight Alpha, it felt very stable for me and it felt quite productive for an alpha! In addition to some issues I mentioned before, I ran into some other things:
- The MediaElement did not want to play my MP3 sound effects created with Audacity (using either ID3v1 or ID3v2 format). So I ended up converting to .WMA and they were happy.
- I could not get the Downloader component to work with my .WMA files (so you will likely notice an uncomfortable "pause" when playing back the first laser fire sfx). Even worse I found that IE would lock up when refreshing or navigating off the page after attempting to use the Downloader component.
THE GAME LOOP
Most games contain a central loop that gets executed for each timer tick. Inside this loop, sprites are moved, AI is performed, and collisions calculated. You might be wondering, why not use the key-based animations available in XAML? Well, it would be pretty difficult to calculate dynamic, angle-based movement for sprites using strict Storyboards. Using a main game loop gives ultimate control over what is happening per frame.
But note that there is no customary Timer Class available in Silverlight 1.1 Alpha. Instead, we can use an animation timer as described in this previous post.
HANDLING XAML ASSETS
I used Expression Design to draw out the asteroids and ship and ultimately create their XAML. You will notice that there are separate XAML files (SpaceShip.xaml, Asteroid.xaml, AsteroidBig.xaml, etc.) for each "sprite" in the game. These are added to the main Page.xaml document at run time through the several "Create" methods in Page.xaml.cs.
If you examine the Properties Window for any of these "Sprite" XAML files, you will notice that their Build Option is set to "Embedded Resource." This will cause the XAML file to be built into the Assembly (.dll), which is downloaded to the web client when the user loads the Silverlight application.
So how do these XAML assets get used at run time? If we peek at Utils.cs, we see a short helper routine called GetResource which will return the XAML string from the embedded resource by name. Note that the resource name will be in the format ProjectName.File.xaml, where ProjectName is your project name, and File.xaml is the XAML file name.
public string GetResource(string ResourceName)
using (System.IO.StreamReader reader = new System.IO.StreamReader(this.GetType().Assembly.GetManifestResourceStream(ResourceName)))
resource = reader.ReadToEnd();
For example, to load the embedded XAML resource "SpaceShip.xaml" we would call:
string xaml = utils.GetResource("SilverlightRocks.SpaceShip.xaml");
After we have the xaml string, we can create an instance of the class and insert the object into our main document using XamlReader.Load. For example,
_asteroids[i].PathObject = XamlReader.Load(thisXaml, false) as Path;
One important note: if you name any object in your XAML file using the x:Name attribute (which Expression will often do), and you plan on using XamlReader.Load, you must also include the xaml xml namespace (xmlns) like so:
http://schemas.microsoft.com/winfx/2006/xaml" x:Name="Ship" Width="50" Height="44" Canvas.Left="350" Canvas.Top="175">
Keeping the Sprites' XAML files separate from the main Page.xaml has several advantages. It makes it easier to create multiple instances of a sprite through code; it keeps the XAML files shorter and easier to maintain; it allows editing of the separate assets through Expression Blend.
MIXING IN STORYBOARD ANIMATIONS
Although I mentioned earlier how a main Game Loop makes it easier to have ultimate control of movement and collisions, there are some animations that are better left to Storyboard animations. For example, when our ship hits an asteroid and explodes, there are three chunks of debris that float off and fade out. This kind of mundane animation - without collisions or user input - is perfect for a storyboard.
I created the explosion animation using Expression Blend, which offers easy to use Key based animation. The resulting XAML file is ExplosionDebris.xaml, and contains a storyboard animation named ShipExplode:
... and when the ship collides with an asteroid, we can first position the Canvas containing the explosion ("ExplosionDebris") over the Ship's position, and then start the Storyboard animation using its Begin() method.
Silverlight's MediaElement class supports sound mixing - all we need to do is create enough copies of each sound to mix in. You can imagine that if we only created one copy of the Laser Fire sound in memory, the sound would restart every time the user pressed "fire" and the effect would be pretty poor! So for the laser fire sound effect, we create an array of copies of the sound and then iterate through them in sequence.
As I mentioned before, I am having issues with Silverlight 1.1 Alpha when trying to place my sounds in a ZIP and use the Downloader class. While I get an HTTP 200 and can assign the Source property without error, the sounds do not play back and IE locks up when trying to refresh or navigate using history.
Silverlight is a promising technology for creating casual games with easy web distribution. While I have not done any testing on the Mac, the frame rate and performance on the PC seems quite adequate for many applications --- and this is at Alpha!