Sammy The Snake: An XNA game for the Zune

Author: | Clint Rutkas: twitter, blog |
Download: | Download |
Software: | Visual Studio 2008 Standard or better (Trial Download)
Windows Mobile 6.0 SDK Windows Mobile 6.1 Emulator Images (Optional) |
Hardware: | Windows Mobile Pro (Optional) |
Time Required: | 2 hours |
Cost: | Free-ish |
After playing way too much guitar hero, bought a guitar only to quickly realize I'm horrible. Asking my friends, I decided to adopt the world's hardest instrument to play, that's right, the cow bell. It takes years to master and I decide to turn my cell phone, an HTC Touch Diamond into this magical instrument too.
This is a fairly straight forward application. We'll touch on altering the volume of a cell phone along with interacting with a touch screen and how to do a deploy for Windows Mobile so you can have a cowbell band of your own. The application will also properly resize an image to match the screen proportions, however you'll have to download the source code for that.
You'll see while getting your system up and working with the emulator may take a few additional steps, it will code mostly like a typical windows application for this instance.
The source code shown isn't everything you need to get the application running, please download the source code to get the rest. The link is at the top of the article.
After you get Visual
Studio 2008 up and running, if you don't have the Windows Mobile 6.0 SDK, please download it and install it. The link is at the top of this article. Visual Studio 2008 only comes with the Windows mobile 5.0 SDK.
On the right is a picture of the emulator in one of styling forms. On top of just being able to test on an emulator, you can do a direct deploy to your cell phone. Now a Windows Mobile Professional phone can do this directly, however a Windows Mobile Standard (non-touch screen) does require a few extra steps. I blogged about these steps over on my blog. After your phone is properly configured, all you need to do is change your target deployment.
I highlighted the device deploy instead of the emulator.
To create a new solution we go to File –> New –> Project. We'll select the language of our choice (c# for me) then go to “Smart Device”
Easy so far, right? Next comes selecting the framework along with your target platform. Select “Windows Mobile 6 Professional SDK” then click OK and you're good to start.
To alter your volume currently in the compact framework, you have to do some pinvokes to call native API calls. This is scary for some but don't fret. There is a site called pinvoke.net that makes doing these calls simple as pie. We'll create a new class called Audio to encapsulate all audio functions. This includes playing a file and altering the volume. First we'll do the simple ability to play an audio file. Since we'll be grabbing our data from the resources, we'll use a MemoryStream object convert the bytes to a stream so we can read it in. We'll need to include the System.Media namespace to be able to play audio. To do the pinvokes, we'll include the System.Runtime.InteropServices.
C#
private readonly SoundPlayer sp; public Audio(byte[] AudioResource) { sp = new SoundPlayer(new MemoryStream(AudioResource)); sp.Load(); } public void Play() { sp.Play(); } public void PlayLoop() { sp.PlayLooping(); } public void Stop() { sp.Stop(); }
VB
Private ReadOnly sp As SoundPlayer Public Sub New(ByVal AudioResource As Byte()) sp = New SoundPlayer(New MemoryStream(AudioResource)) sp.Load() End Sub Public Sub Play() sp.Play() End Sub Public Sub PlayLoop() sp.PlayLooping() End Sub Public Sub [Stop]() sp.[Stop]() End Sub
And in the application, we'll call the audio file like the following:
c#
private readonly Audio audio = new Audio(Properties.Resources.CowBellAudio);
VB
Private ReadOnly audio As New Audio(My.Resources.CowBellAudio)
Next we'll add in the ability to alter the volume with the pinvokes. Sadly, I'm not sure why the set volume numbers act as they do. After searching the internet, these numbers kept appearing. In a perfect world, I would have liked to see 0 to 100 or something like that but it isn't so. We'll create get / sets in the wrapper to make a more seamless wrapper object to hide the pinvoke calls.
C#
[DllImport("coredll.dll")] private static extern int waveOutGetVolume(IntPtr hwo, out uint dwVolume); [DllImport("coredll.dll")] private static extern int waveOutSetVolume(IntPtr hwo, uint dwVolume); public static uint Volume { get { uint _volume; waveOutGetVolume(IntPtr.Zero, out _volume); return _volume; } set { waveOutSetVolume(IntPtr.Zero, value); } } public enum Volumes { OFF = 0, LOW = 858993459, NORMAL = 1717986918, MEDIUM = -1717986919, HIGH = -858993460, VERY_HIGH = -1 }
VB
Declare Function waveOutGetVolume Lib "coredll.dll" (ByVal uDeviceID As Integer, ByRef lpdwVolume As Integer) As Integer Declare Function waveOutSetVolume Lib "coredll.dll" (ByVal device As IntPtr, ByVal volume As Integer) As Integer Public Shared Property AudioVolume() As Integer Get Dim _volume As Integer waveOutGetVolume(IntPtr.Zero, _volume) Return _volume End Get Set(ByVal value As Integer) waveOutSetVolume(IntPtr.Zero, value) End Set End Property Public Enum Volumes OFF = 0 LOW = 858993459 NORMAL = 1717986918 MEDIUM = -1717986919 HIGH = -858993460 VERY_HIGH = -1 End Enum
This is a cowbell application so we need an actual cowbell on it. After a quick image search, I found the cowbell image on the internet and the X is from the Famfamfam Silk icon collection.
We'll want to embed these images to make deployment easier and have everything self-contained so we'll use the resource in the project's property tab. To reach this, you go to Project->Properties or right click on your project and at the bottom, there is the Properties menu option.
In here, we'll want to use the Images section along with the audio section.
When you select the section you want, click the Add Resource button and track down the file you need. Referencing these resources is pretty straight forward now but each language has a slightly different way to do it.
C#
Properties.Resources.cowBellImage;
VB
My.Resources.cowBellImage
Easy no?
We'll want to turn off some elements on the form to maximize the cowbell. We'll set the WindowState to Maximum, TopMost to True, MinimizeBox to False and FormBorder to None.
We'll drag on two picture boxes on the form naming them picCowbell and picClose. We'll set the background to both elements to be transparent. We'll also want to double click on each picture boxes to create a Click event. For the picClose element, we'll add in the Close() method to shut down the application.
We'll also create a MouseDown event on the picCowbell. Here we'll determine how loud to play then play the sound. We'll create the Audio object during the form creation so it is already loaded into memory and will only be created once.
C#
private readonly Audio audio = new Audio(Properties.Resources.CowBellAudio); private void picCowbell_MouseDown(object sender, MouseEventArgs e) { Audio.Volumes value; switch ((int)Math.Round((e.Y / (Height * 1.0)) * 5)) { //case 0: // implied with default; //case 1: default: value = Audio.Volumes.LOW; break; case 2: value = Audio.Volumes.NORMAL; break; case 3: value = Audio.Volumes.MEDIUM; break; case 4: value = Audio.Volumes.HIGH; break; case 5: value = Audio.Volumes.VERY_HIGH; break; } // no need to take the performance hit of changing the volume // if the values aren't different if( oldValue != value) { Audio.Volume = (uint) value; oldValue = value; } audio.Play(); }
VB
Private ReadOnly audio As New Audio(My.Resources.CowBellAudio) Private Sub picCowbell_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Dim value As Audio.Volumes Select Case Convert.ToInt32(Math.Round((e.Y / (Height * 1)) * 5)) Case 2 value = audio.Volumes.NORMAL Exit Select Case 3 value = audio.Volumes.MEDIUM Exit Select Case 4 value = audio.Volumes.HIGH Exit Select Case 5 value = audio.Volumes.VERY_HIGH Exit Select Case Else 'case 0: // implied with default; 'case 1: value = audio.Volumes.LOW Exit Select End Select ' no need to take the performance hit of changing the volume ' if the values aren't different If oldValue <> value Then audio.AudioVolume = Convert.ToInt32(value) oldValue = value End If audio.Play() End Sub
We'll right click on the solution in the solution explorer or we can go to File->New->Project. If you do the file menu route, you'll have a screen that is slightly different than mine. At the bottom you'll have a drop down menu where you'll want to change it from “Create a new solution” to “Add to Solution”.
Since I want a CAB install, I'll go to the “Other Project Types” then to “Setup and Deployment” and select a Smart Device CAB Project”.
From there, right click on “Application Folder” and go to “Add”. Select “Primary Output” and hit OK.
So with the output added in now, we'll need to do some more magic. In the “File System” window, we'll right-click on “File System on Target Machine” and go “Add Special Folder” then to “Start Menu Folder”.
Now that we have our start menu, we'll go back to the “Application Menu” and right click on the “Primary output from Cowbell”. Select “Create Shortcut” and drag that shortcut to your start menu folder. I suggest you rename it too.
Then you can build it, drag the CAB over and install on your device.
To download the application's full source code, please download it from https://sec.ch9.ms/ecn/c4fcontent/migration/9059259/cowbell.zip
Clint Rutkas works for Microsoft and has created a few cool projects in the past like a Disco Dance Floor, an automated bartender system and a self-balancing skateboard. His blog is http://BetterThanEveryone.com where he posts about what crazy application idea he is working on next.
MORE COWBELL, we need more windows mobile cowbell!
"Sadly, I’m not sure why the set volume numbers act as they do. After searching the internet, these numbers kept appearing. In a perfect world, I would have liked to see 0 to 100 or something like that but it isn’t so."
Convert the values to hex and you'll see why:
OFF = 00000000
LOW = 33333333
NORMAL = 66666666
MEDIUM = 99999999
HIGH = CCCCCCCC
VERY_HIGH = FFFFFFFF
So it ranges from 0 to FFFFFFFF - these must just be "common" values that fall in that range.