SilverlightShow: Silverlight Community http://www.silverlightshow.net/ Silverlight articles, Silverlight tutorials, Silverlight videos, Silverlight samples SilverlightShow.net http://www.rssboard.org/rss-specification Argotic Syndication Framework 2008.0.2.0, http://www.codeplex.com/Argotic en-US estoychev@completit.com (Emil Stoychev) Simulating rain in Silverlight Part 4 - Adding sound effects <p><a href="http://twitter.com/home?status=New+article+by+%40silverlightshow%3A+Simulating+rain+in+Silverlight+Part+4+-+Adding+sound+effects+http%3A%2F%2Fbit.ly%2FoHN4mX+%23silverlight" target="_blank"><img style="border:0px solid; margin-bottom: 10px; float: right; margin-left: 10px;" alt="Tweet This!" src="http://www.silverlightshow.net/Storage/tt-big4.png" /></a><strong><em>This article is compatible with the latest version of Silverlight.</em></strong></p> <h3>Introduction</h3> <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; width: 200px; float: right; margin-left: 10px; padding-top: 5px;"> <h3>More resources...</h3> <ul style="list-style-type: circle; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-left: 20px; font-size: 12px;"> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/Webinars.aspx">Free SilverlightShow Webinars</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/GetStarted.aspx">Get Started with Silverlight 4</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/ebooks/silverlight_exam.aspx">'Getting Ready for Microsoft Silverlight Exam 70-506' Ebook </a></li> </ul> <p style="padding-bottom: 5px; text-align: center;"><a href="http://www.silverlightshow.net/ebooks/silverlight_exam.aspx"><img style="border:0px solid;" alt="Getting Ready for Microsoft Silverlight Exam 70-506: Ebook" src="http://www.silverlightshow.net/Storage/sl_exam_thumb_small.png" usemap="#rade_img_map_1291385581316" /></a><br /> <strong><span style="font-size: 13px;">($9.99)</span></strong></p> <p style="font-size: 12px;">     <a href="http://www.silverlightshow.net/ebooks.aspx">All SilverlightShow Ebooks</a> <img alt="" src="http://www.silverlightshow.net/Storage/arrow-content.jpg" /></p> </div> <p>This article is part 4 of my article series, <em>Simulating rain in Silverlight</em>. After I added wind effect in the previous article, now I add some “color” in the whole scene. Besides the sound effect, I also put in some visuals to control the wind speed. You can choose between three different rates that will alter the angle of the rain drops. To better react when changing the speed I had to do some minor changes in the simulation algorithm. You are free to download and play with the code from the link below.</p> <p>It may seem very unfamiliar to you to have sound in your Silverlight application, but it, actually, is not that hard. Silverlight provides you with tools for handling sounds and videos in code and XAML. How? Just in a few seconds...</p> <p>First, here is a demo:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/lnikolov/RainEffectPart4.html">View live demo</a></p> <p>As you might see, you can choose between three different speeds that affect respectively the angle of the rain. Obviously, the stronger the wind is, the bigger the angle is. In nature, having summer rain with 40 kilometers per hour, results in approximately 10 degrees of inclination of the rain. Respectively, 60 km/h yields 30 degrees and 80 km/h – 60 degrees. Again, it depends on the intensity and rain density, but we can use this rule for orientation marks in our simulation.</p> <p>You can download the source from the following link:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/lnikolov/RainEffectPart4SoundEffects.zip">Download source code</a></p> <h3>How to add sound in your application?</h3> <p>Rain and wind simulation is not complete without any sound effects. </p> <p>The<strong> MediaElement</strong> class is the key. It allows you to play a number of audio and video formats. For a complete list of supported files, read <a href="http://msdn.microsoft.com/en-us/library/cc189080(v=vs.95).aspx">here</a>.</p> <p>The first thing you need to do is to find (or create the sounds) you are to use. There are a bundle of websites on the net offering free sound effects in any audio format. For example, check these out: <a href="http://www.partnersinrhyme.com/soundfx/Weather.shtml">http://www.partnersinrhyme.com/soundfx/Weather.shtml</a> and <a href="http://www.shockwave-sound.com/">http://www.shockwave-sound.com/</a>.</p> <p>My rain simulation uses two audio files – one for the rain and one for the wind. You need to add the files in the project and change the build action to Resource to ensure the media file gets copied in the <em>ClientBin</em> folder.</p> <p>I declare my media elements in the XAML that control the both effects:</p> <div class="reCodeBlock" style="border:1px solid #7f9db9;overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #000000;"><</code><code style="color: #006699; font-weight: bold;">MediaElement</code> <code style="color: #808080;">x:Name</code><code style="color: #000000;">=</code><code style="color: blue;">"WindSound"</code></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>                </code><span style="margin-left: 48px !important; font-size: 11px;"><code style="color: #808080;">Source</code><code style="color: #000000;">=</code><code style="color: blue;">"wind.mp3"</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>                </code><span style="margin-left: 48px !important; font-size: 11px;"><code style="color: #808080;">AutoPlay</code><code style="color: #000000;">=</code><code style="color: blue;">"False"</code><code style="color: #000000;">></</code><code style="color: #006699; font-weight: bold;">MediaElement</code><code style="color: #000000;">></code></span></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important; font-size: 11px;"> </span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #000000;"><</code><code style="color: #006699; font-weight: bold;">MediaElement</code> <code style="color: #808080;">x:Name</code><code style="color: #000000;">=</code><code style="color: blue;">"RainSound"</code></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>                </code><span style="margin-left: 48px !important; font-size: 11px;"><code style="color: #808080;">Source</code><code style="color: #000000;">=</code><code style="color: blue;">"rain.mp3"</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>                </code><span style="margin-left: 48px !important; font-size: 11px;"><code style="color: #808080;">AutoPlay</code><code style="color: #000000;">=</code><code style="color: blue;">"False"</code><code style="color: #000000;">></</code><code style="color: #006699; font-weight: bold;">MediaElement</code><code style="color: #000000;">></code></span></span></div> </div> <p>I set <em>AutoPlay=”False”</em> to prevent the media element from playing as soon as the app starts. I control this on my own:</p> <div class="reCodeBlock" style="border:1px solid #7f9db9;overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">private</code> <code style="color: #006699; font-weight: bold;">void</code> <code style="color: #000000;">WindSound_MediaOpened(</code><code style="color: #006699; font-weight: bold;">object</code> <code style="color: #000000;">sender, System.Windows.RoutedEventArgs e)</code></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #000000;">{</code></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.windMarker.Time = TimeSpan.FromMilliseconds(</code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.WindSound.NaturalDuration.TimeSpan.TotalMilliseconds - 1000);</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.WindSound.Markers.Add(</code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.windMarker);</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.WindSound.Volume = 0.3;</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.WindSound.Play();</code></span></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #000000;">}</code></span></div> </div> <p>To loop the audio, I use markers. Markers are marks in the audio/video that you can jump to when needed. I create a marker one second before the end of the media file and I use it to mark the end of the file and loop the media. The alternative, to handle the media ended event, is not the best choice here since the sound is getting silent and silent in the very end:</p> <div class="reCodeBlock" style="border:1px solid #7f9db9;overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">private</code> <code style="color: #006699; font-weight: bold;">void</code> <code style="color: #000000;">WindSound_MarkerReached(</code><code style="color: #006699; font-weight: bold;">object</code> <code style="color: #000000;">sender, TimelineMarkerRoutedEventArgs e)</code></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #000000;">{</code></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.WindSound.Stop();</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.WindSound.Play();</code></span></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #000000;">}</code></span></div> </div> <p>To change the rain angle on the fly, I must abstract the rain in maximum rate. The improvement that I make here is in the way I initialize the rain and all it dependencies. The drop settings are constructed in the rain constructor, so is the slice and the fall distance.</p> <p>Having all this in mind, we have this method:</p> <div class="reCodeBlock" style="border:1px solid #7f9db9;overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">public</code> <code style="color: #006699; font-weight: bold;">void</code> <code style="color: #000000;">ChangeAngle(</code><code style="color: #006699; font-weight: bold;">int</code> <code style="color: #000000;">angle)</code></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #000000;">{</code></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">int</code> <code style="color: #000000;">fallDistance = (</code><code style="color: #006699; font-weight: bold;">int</code><code style="color: #000000;">)((</code><code style="color: #006699; font-weight: bold;">double</code><code style="color: #000000;">)</code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.Height / Math.Cos(Math.PI * angle / 180));</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.FallDistance = fallDistance;</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.Angle = angle;</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.Slice = (</code><code style="color: #006699; font-weight: bold;">int</code><code style="color: #000000;">)(</code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.Height * Math.Tan(Math.PI * angle / 180));</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.sliceCoef = (</code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.FallDistance * Math.Sin(Math.PI * angle / 180)) / </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.Width;</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.DropSettings = </code><code style="color: #006699; font-weight: bold;">new</code> <code style="color: #000000;">DropSettings(0, fallDistance, Constants.ANIMATION_DURATION);</code></span></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #000000;">}</code></span></div> </div> <p>It changes not only the angle, but every aspect of the rain that depends on the angle.</p> <p>Now, it’s easy to change the wind speed:</p> <div class="reCodeBlock" style="border:1px solid #7f9db9;overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #008200;">// Speed changed handler</code></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">private</code> <code style="color: #006699; font-weight: bold;">void</code> <code style="color: #000000;">SpeedSelector_SelectionChanged(</code><code style="color: #006699; font-weight: bold;">object</code> <code style="color: #000000;">sender, SelectionChangedEventArgs e)</code></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #000000;">{</code></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.HideArrows();</code></span></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"> </span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">switch</code> <code style="color: #000000;">(</code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.SpeedSelector.SelectedIndex)</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important; font-size: 11px;"><code style="color: #000000;">{</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>        </code><span style="margin-left: 24px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">case</code> <code style="color: #000000;">0:</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>            </code><span style="margin-left: 36px !important; font-size: 11px;"><code style="color: #000000;">{   </code><code style="color: #008200;">// Light rain</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>                </code><span style="margin-left: 48px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.light1.Visibility = </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.light2.Visibility = </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.light3.Visibility = System.Windows.Visibility.Visible;</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>                </code><span style="margin-left: 48px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.rain.ChangeAngle(</code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.lightAngle);</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>                </code><span style="margin-left: 48px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.WindSound.Volume = 0.3;</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>                </code><span style="margin-left: 48px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">break</code><code style="color: #000000;">;</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>            </code><span style="margin-left: 36px !important; font-size: 11px;"><code style="color: #000000;">}</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>        </code><span style="margin-left: 24px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">case</code> <code style="color: #000000;">1:</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>            </code><span style="margin-left: 36px !important; font-size: 11px;"><code style="color: #000000;">{   </code><code style="color: #008200;">// Medium rain</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>                </code><span style="margin-left: 48px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.medium1.Visibility = </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.medium2.Visibility = </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.medium3.Visibility = System.Windows.Visibility.Visible;</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>                </code><span style="margin-left: 48px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.rain.ChangeAngle(</code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.mediumAngle);</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>                </code><span style="margin-left: 48px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.WindSound.Volume = 0.6;</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>                </code><span style="margin-left: 48px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">break</code><code style="color: #000000;">;</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>            </code><span style="margin-left: 36px !important; font-size: 11px;"><code style="color: #000000;">}</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>        </code><span style="margin-left: 24px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">case</code> <code style="color: #000000;">2:</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>            </code><span style="margin-left: 36px !important; font-size: 11px;"><code style="color: #000000;">{   </code><code style="color: #008200;">// Rough rain</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>                </code><span style="margin-left: 48px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.rough1.Visibility = </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.rough2.Visibility = </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.rough3.Visibility = System.Windows.Visibility.Visible;</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>                </code><span style="margin-left: 48px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.rain.ChangeAngle(</code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.roughAngle);</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>                </code><span style="margin-left: 48px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.WindSound.Volume = 0.9;</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>                </code><span style="margin-left: 48px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">break</code><code style="color: #000000;">;</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>            </code><span style="margin-left: 36px !important; font-size: 11px;"><code style="color: #000000;">}</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important; font-size: 11px;"><code style="color: #000000;">}</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #000000;">}</code></span></div> </div> <p>Of course, changing the wind speed affects the Volume property of the media file. That’s how you volume up or down an audio.</p> <h3>Conclusion</h3> <p>In this article I add sound effects to my rain simulation. Moreover, I build some visuals to change the wind speed. I strongly encourage you to use <strong>MediaElement</strong> when dealing with media files. It provides you with all you could need. Or solid base for all you could ever need!</p> Feel free to ask or comment! http://www.silverlightshow.net/items/Simulating-rain-in-Silverlight-Part-4-Adding-sound-effects.aspx editorial@silverlightshow.net (Lazar Nikolov ) http://www.silverlightshow.net/items/Simulating-rain-in-Silverlight-Part-4-Adding-sound-effects.aspx#comments http://www.silverlightshow.net/items/Simulating-rain-in-Silverlight-Part-4-Adding-sound-effects.aspx Fri, 30 Sep 2011 19:21:00 GMT Simulating rain in Silverlight Part 3 - Adding wind effect <a href="http://twitter.com/home?status=New+article+by+%40silverlightshow%3A+Simulating+rain+in+Silverlight+part+3%3A+Adding+wind+effect+http%3A%2F%2Fbit.ly%2FoHN4mX" target="_blank"><img style="border:0px solid; margin-bottom: 10px; float: right; margin-left: 10px;" alt="Tweet This!" src="http://www.silverlightshow.net/Storage/tt-big4.png" /></a> <p><strong><em>This article is compatible with the latest version of Silverlight.</em></strong></p> <h3>Introduction</h3> <p>This is the logical continuation of my article <a href="http://www.silverlightshow.net/items/Simulating-rain-in-Silverlight-Part-2-Optimization.aspx" target="_blank" re_target="null">"Simulating rain in Silverlight Part 2 - Optimization"</a>. </p> <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; width: 200px; float: right; margin-left: 10px; padding-top: 5px;"> <h3>More resources...</h3> <ul style="list-style-type: circle; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-left: 20px; font-size: 12px;"> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/Webinars.aspx">Free SilverlightShow Webinars</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/GetStarted.aspx">Get Started with Silverlight 4</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/ebooks/silverlight_exam.aspx">'Getting Ready for Microsoft Silverlight Exam 70-506' Ebook </a></li> </ul> <p style="padding-bottom: 5px; text-align: center;"><a href="http://www.silverlightshow.net/ebooks/silverlight_exam.aspx"><img style="border:0px solid;" alt="Getting Ready for Microsoft Silverlight Exam 70-506: Ebook" src="http://www.silverlightshow.net/Storage/sl_exam_thumb_small.png" usemap="#rade_img_map_1291385581316" /></a><br /> <strong><span style="font-size: 13px;">($9.99)</span></strong></p> <p style="font-size: 12px;">     <a href="http://www.silverlightshow.net/ebooks.aspx">All SilverlightShow Ebooks</a> <img alt="" src="http://www.silverlightshow.net/Storage/arrow-content.jpg" /></p> </div> <p> In this publication, I will add wind effect to the rain. And why is this wind effect the logical extension of the rain? Obviously, the nature rarely brings us the rain without a wind. Furthermore, it is not that trivial to add the wind effect... </p> <p> </p> Ок, let's take a good look at the phenomenon, just to make sure we have a solid understanding of what we're trying to simulate. Our simulation will assume the wind is constant. This way we avoid the random fluctuations of the rain drops. Why is this? Since Silverlight does not limit you, just experiment with it by yourself. Clear, we have rain drops, constant wind blowing the scene, and that is...just deal with the angle the drops are falling. <br /> <p>From this point on, I will expect you to have read my previous articles, <a href="http://www.silverlightshow.net/items/Simulating-rain-in-Silverlight.aspx" target="_blank" re_target="null">"Simulating rain in Silverlight"</a> and <a href="http://www.silverlightshow.net/items/Simulating-rain-in-Silverlight-Part-2-Optimization.aspx" target="_blank" re_target="null">"Simulating rain in Silverlight Part 2 - Optimization"</a> since I am going to build over them.</p> <p>Before I begin with the problem, here is a demo:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/lnikolov/RainEffectPart3Wind.html" target="_blank" re_target="null">View live demo</a> - 30 degrees angle</p> <p><a href="http://www.silverlightshow.net/Storage/Users/lnikolov/RainEffectPart3Wind.zip" target="_blank" re_target="null">Download source code</a></p> <h3>What is the problem?</h3> <p>What are we up to now? In the previous articles we have the rain with the falling straight down drops. Now we will alter the angle of the drops to simulate the wind effect. You would say “Okay, that’s easy, just change the angle in the <em>RotateTransform</em> in the algorithm and it’s over”. I wish it was that easy. </p> <p>Changing the angle in the transformation does actually resemble the wind effect, but having the scene in the Canvas, we have to slice two portions to keep the picture consistent. First, we have drops from the left of the scene that need to be hidden after reaching the scene border. And then, we have the drops from the right that need to be hidden until reached the scene border.</p> <p>And,</p> <h3>What is the solution?</h3> <p>Not that hard, actually, I promise. Just simple math...trigonometry. </p> <p>We have three types of drops in this simulation. One that falls all its way in the scene. Other that starts in the scene and falls until hits the left border of the scene. And last, the drops that start their way outside the scene and, at some moment, enter the scene until they fall.</p> <p>We don’t need to pay any special attention to the first type of drops since they just need to have their angle set. It is rather different with the rest of the drops. First, we need to calculate what the point in the scene that distinguishes the first from the second type is. I call this property <em>Slice</em>. Having the height of the scene and the angle you simply use the tangent of the angle. Based on <em>Slice</em>, I need the slice coefficient:<br />  </p> <div class="reCodeBlock" style="border:1px solid #7f9db9;overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.Slice = ( </code><code style="color: #006699; font-weight: bold;">int</code> <code style="color: #000000;">)( height * Math.Tan( Math.PI * </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.Angle / 180 ) );</code></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.sliceCoef = ( </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.FallDistance * Math.Sin( Math.PI * </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.Angle / 180 ) ) / </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.Width;</code></span></div> </div> <p>Having this information, it is easy to determine what type is a drop. Of course, the third type is easily distinguishable using the right border of the scene.</p> <p><span style="line-height: 115%; font-size: 11pt;"></span></p> <div class="reCodeBlock" style="border:1px solid #7f9db9;overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">int</code> <code style="color: #000000;">leftMargin = Convert.ToInt32( rand.NextDouble() * ( </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.Width + </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.Slice ) );</code></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.sceneDrops[ currentDrop ].Margin = </code><code style="color: #006699; font-weight: bold;">new</code> <code style="color: #000000;">Thickness( leftMargin, 0, 0, 0 );</code></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">double</code> <code style="color: #000000;">coef = ( </code><code style="color: #006699; font-weight: bold;">double</code> <code style="color: #000000;">)leftMargin / ( </code><code style="color: #006699; font-weight: bold;">double</code> <code style="color: #000000;">)</code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.Width;</code></span></div> </div> <p>The drop is Type 1 if the coefficient is less than the slice coefficient:</p> <p><span style="line-height: 115%; font-size: 11pt;"></span></p> <div class="reCodeBlock" style="border:1px solid #7f9db9;overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">if</code> <code style="color: #000000;">( coef <= </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.sliceCoef )</code></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important;"><code style="color: #000000;"><span style="font-size: 11px;">{</span></code></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #006699; font-weight: bold;">int</code> <code style="color: #000000;">newFinalHeight = ( </code><code style="color: #006699; font-weight: bold;">int</code> <code style="color: #000000;">)( ( leftMargin ) / ( Math.Sin( Math.PI * </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.Angle / 180 ) ) );</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.sceneDrops[ currentDrop ].FallDistance = newFinalHeight;</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.sceneDrops[ currentDrop ].FallTime =</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>        </code><span style="margin-left: 24px !important;"><code style="color: #000000;">TimeSpan.FromMilliseconds( Constants.ANIMATION_DURATION.TotalMilliseconds *</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>        </code><span style="margin-left: 24px !important;"><code style="color: #000000;">( ( </code><code style="color: #006699; font-weight: bold;">double</code> <code style="color: #000000;">)newFinalHeight / ( </code><code style="color: #006699; font-weight: bold;">double</code> <code style="color: #000000;">)</code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.DropSettings.FallDistance ) );</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important;"><code style="color: #000000;"><span style="font-size: 11px;">}</span></code></span></div> </div> <p>To calculate the new length of the fall, that is the fall height, we use trigonometry. Of course, we also must alter the duration of the fall.</p> <p>Similarly, to calculate the way of Type 3 drops, we use this code:</p> <p><span style="line-height: 115%; font-size: 11pt;"></span></p> <div class="reCodeBlock" style="border:1px solid #7f9db9;overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">if</code> <code style="color: #000000;">( coef >= 1 )</code></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important;"><code style="color: #000000;"><span style="font-size: 11px;">{</span></code></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #006699; font-weight: bold;">int</code> <code style="color: #000000;">newInitialHeight = ( </code><code style="color: #006699; font-weight: bold;">int</code> <code style="color: #000000;">)( ( </code><code style="color: #006699; font-weight: bold;">double</code> <code style="color: #000000;">)( leftMargin - </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.Width ) / Math.Sin( Math.PI * </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.Angle / 180 ) );</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.sceneDrops[ currentDrop ].FallInitialHeight = newInitialHeight;</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.sceneDrops[ currentDrop ].FallTime =</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>        </code><span style="margin-left: 24px !important;"><code style="color: #000000;">TimeSpan.FromMilliseconds( Constants.ANIMATION_DURATION.TotalMilliseconds *</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>        </code><span style="margin-left: 24px !important;"><code style="color: #000000;">( ( </code><code style="color: #006699; font-weight: bold;">double</code> <code style="color: #000000;">)( </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.FallDistance - newInitialHeight ) / ( </code><code style="color: #006699; font-weight: bold;">double</code> <code style="color: #000000;">)</code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.FallDistance ) );</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important;"><code style="color: #000000;"><span style="font-size: 11px;">}</span></code></span></div> </div> <p>And voila! That was the main part of it all. Wasn’t that hard, right?</p> <h3>Conclusion</h3> <p>To summarize, in this article we’ve simulated wind effect applied to the rain effect from my previous article - <a href="http://www.silverlightshow.net/items/Simulating-rain-in-Silverlight-Part-2-Optimization.aspx" target="_blank" re_target="null">"Simulating rain in Silverlight Part 2 - Optimization"</a>. In my next article, I will add visuals to harness and change the speed of the wind through UI. I will also create sound effects for both wind and rain to “color” the scene. <span style="line-height: 115%; font-family: wingdings; font-size: 11pt;">J</span></p> <div id="-chrome-auto-translate-plugin-dialog" style="opacity: 1 !important; background-image: initial !important; background-attachment: initial !important; background-origin: initial !important; background-clip: initial !important; background-color: transparent !important; padding-top: 0px !important; padding-right: 0px !important; padding-bottom: 0px !important; padding-left: 0px !important; margin-top: 0px !important; margin-right: 0px !important; margin-bottom: 0px !important; margin-left: 0px !important; position: absolute !important; top: 0px; left: 0px; overflow-x: visible !important; overflow-y: visible !important; z-index: 999999 !important; display: none; text-align: left;"> <div style="max-width: 300px !important; color: #fafafa !important; opacity: 0.8 !important; border-top-left-radius: 10px 10px !important; border-top-right-radius: 10px 10px !important; border-bottom-right-radius: 10px 10px !important; border-bottom-left-radius: 10px 10px !important; background-color: #363636 !important; font-size: 16px !important; padding-top: 8px !important; padding-right: 8px !important; padding-bottom: 8px !important; padding-left: 8px !important; overflow-x: visible !important; overflow-y: visible !important; background-image: -webkit-gradient(linear, 0% 0%, 100% 100%, from(#000000), color-stop(0.5, #363636), to(#000000)); z-index: 999999 !important; text-align: left;border-width: 0px !important;border-color: #000000 !important;"> <div class="translate"></div> <div class="additional"></div> </div> <img alt="" src="http://www.google.com/uds/css/small-logo.png" onclick="" style="position: absolute !important; z-index: -1 !important; right: 1px !important; top: -20px !important; cursor: pointer !important; border-top-left-radius: 20px 20px; border-top-right-radius: 20px 20px; border-bottom-right-radius: 20px 20px; border-bottom-left-radius: 20px 20px; background-color: rgba(200, 200, 200, 0.292969) !important; padding-top: 3px !important; padding-right: 5px !important; padding-bottom: 0px !important; padding-left: 5px !important; margin-top: 0px !important; margin-right: 0px !important; margin-bottom: 0px !important; margin-left: 0px !important;" /></div> http://www.silverlightshow.net/items/Simulating-rain-in-Silverlight-Part-3-Adding-wind-effect.aspx editorial@silverlightshow.net (Lazar Nikolov ) http://www.silverlightshow.net/items/Simulating-rain-in-Silverlight-Part-3-Adding-wind-effect.aspx#comments http://www.silverlightshow.net/items/Simulating-rain-in-Silverlight-Part-3-Adding-wind-effect.aspx Wed, 28 Sep 2011 11:47:00 GMT Book review: XNA Game Studio 4.0 Programming: Developing for Windows Phone 7 and Xbox 360 <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; width: 200px; float: right; margin-left: 10px; padding-top: 5px;"> <h3>Don't miss...</h3> <ul style="list-style-type: circle; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-left: 20px; font-size: 12px;"> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/video/XNA-for-WP7-Webinar.aspx">Webinar recording 'XNA for Windows Phone 7'</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Windows-Phone-7-Part-1-Getting-Started.aspx">WP7 series by Andrea Boschin</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx">XNA for Silverlight Developers Ebook:</a> </li> </ul> <p style="padding-bottom: 5px; text-align: center;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx"><img style="border:0px solid; width: 71px; height: 110px;" alt="Windows Phone 7 for Silverlight Developers Ebook" src="http://www.silverlightshow.net/storage/thumb_xna_ebook_cover_150_0.png" usemap="#rade_img_map_1291385581316" /></a><br /> <strong><span style="font-size: 13px;">($2.99)</span></strong></p> <p style="font-size: 12px;">     <a href="http://www.silverlightshow.net/ebooks.aspx">All SilverlightShow Ebooks</a> <img alt="" src="http://www.silverlightshow.net/Storage/arrow-content.jpg" /></p> </div> <p><em>This review is for the book </em><em><a href="http://www.silverlightshow.net/book/XNA-Game-Studio-4.0-Programming-Developing-for-Windows-Phone-7-and-Xbox-360.aspx">'XNA Game Studio 4.0 Programming – Developing for Windows Phone 7 and Xbox 360'</a></em><em>, and has been submitted by a member of the </em><a href="http://wpug.net/" target="_blank"><em>Windows Phone 7 User Group</em></a><em> - a user group supported by SilverlightShow.<br /> We </em><a href="http://www.silverlightshow.net/About/Partners.aspx" target="_self"><em>support user groups</em></a><em> with books, swag, events promotion, free event passes and others. </em><a href="http://www.silverlightshow.net/About/contacts.aspx" target="_self"><em>Contact us to get support for your user group</em></a><em>. <br />  </em> </p> <h2>Introduction</h2> <p>The full title of the book is “XNA Game Studio 4.0 Programming – Developing for Windows Phone 7 and Xbox 360 (Developer’s Library)”. It’s quite a descriptive title in itself, pretty much summing up what to expect. The book is not designed specifically for Windows Phone 7 developers, since XNA can run on the Xbox and PC, but it does include Windows Phone 7 specifics (more on that later).</p> <p>When it comes to technical books, much of its integrity can be obtained by looking at the authors’ achievements and relation to the subject at hand. With that in mind, XNA Game Studio 4.0 Programming has a solid foundation since the two authors, Tom Miller and Dean Johnson, are actually part of the XNA development team, which is a major advantage the book can boast.<br />  </p> <h2>Content</h2> <p><img alt="" style="margin-bottom: 10px; float: right; margin-left: 10px;" src="http://www.silverlightshow.net/Storage/Ebooks/xna_game_book.png" />Being a “Developer’s Library”, this book is packed with content. Each topic is covered in enough depth to provide developers with enough information to find out how to implement features using good programming techniques. The structure of the book is discussed in the next section, but each chapter contains a decent step by step guide on implementing the topic at hand. It also provides background information on what’s happening as well as detailed code so that the reader can understand what the code does and adapt it to their projects. Being a reference book, the code isn’t overly verbose and so the reader can jump in to any chapter, find out how to code that specific feature and get back to their project without having to rely on any previous work. The quality of code is great and the custom Avatar animation chapter is certainly something that shines. </p> <h2>Structure</h2> <p>The subtitle of the book, “Developer’s Library”, should not be underestimated as this book really does serve a purpose and does it very well. Many XNA books I’ve encountered go down the path of working with the user to build a game from scratch. Starting from a new project, the author would explain to readers how to display sprites, and then move on to input, effects, audio and so forth. These types of books certainly have their place, but XNA Game Studio 4.0 Programming takes a different approach. Instead of guiding the user step by step through a game, most chapters are treated in isolation. That is, you end up creating new projects as you progress through the book, instead of continuing with one project and expanding on it. This is a great approach to take, especially for a book that’s designed to be a reference, since it means that the reader can jump to any topic and immediately be able to implement the relevant feature without having to code previous projects. It also means the user can jump from topic to topic if they are already experienced with XNA and want to simply learn more about specific features. </p> <p>The downside to such an approach is that newcomers to XNA, or game programming in general, may not necessarily find it easy to create a complete game from scratch. Whilst they will learn a lot, certainly enough to create the components of great games, they may find it difficult connecting everything together into one game. However, this book does not claim to be for beginners and, as such, I do not hold this point against it. XNA Games Studio 4.0 Programming aims to be a reference book and it sticks to that philosophy very well.</p> <p>I’d like to also give kudos to the authors for adding a chapter on 3D math basics. It’s a topic often left out of game development books despite math being fundamental to many games. If you’re proficient with vectors and matrices, it’s a chapter to skip, but it’s a great addition for those unfamiliar with it or need a quick refresher course.<br />  </p> <h2>Windows Phone 7</h2> <p>Although this book covers XNA in general, it does give specific information about Windows Phone 7. For example, it explains how to implement the various Windows Phone 7 specific features such as the accelerometer and location services. It also discusses how to implement the radio, Launchers and Choosers and tombstoning. The sections for each of these are succinct, but like the other topics in the book, they come with code to show you how to implement the features in XNA. As a side note, this book doesn’t discuss the Mango SDK features, presumably because the SDK was publicly released after the book has been completed.<br />  </p> <h2>Conclusion</h2> <p>XNA Game Studio 4.0 Programming is a very good reference book and provides a lot of code, as well as solid explanations, on pretty much all aspects of the XNA framework. With each chapter having a new project, it makes it very easy to just skip to your chapter of interest and be shown how to implement the feature from scratch. If you’re new to XNA, or game programming in general, I recommend you buy an introductory book to complement this one. Although this book has small projects that demonstrate the topic at hand, it may be a bit too disjointed for newcomers who want to create a game from scratch straight away. As a side note, this book assumes you’re familiar with C# as do many other XNA books. </p> <p>However, as mentioned previously, this book is a “Developer’s Library” and not a beginner’s guide. I highly recommend it to anyone using XNA. Those who already have experience with XNA will find it a nice desktop reference to have for those moments where you don’t know, or have forgotten, how to do certain things. The book tries to cover all topics in reasonable depth and there are many small details that often don’t get mentioned in other books. If you’re new to XNA, you’ll find it a nice book to be able to jump to when you want to find out more about a specific topic. </p> <p>There are many XNA tutorials on the web and, of course, the MSDN documentations. However, XNA Game Studio 4.0 Programming – Developing for Windows Phone 7 and Xbox 360 (Developer’s Library), centralizes all the key aspects of the XNA framework in<a name="_GoBack"></a>to one tidy book, which should be on the desk of any developer creating XNA projects. </p> http://www.silverlightshow.net/items/Book-review-XNA-Game-Studio-4.0-Programming-Developing-for-Windows-Phone-7-and-Xbox-360.aspx editorial@silverlightshow.net (keyboardP ) http://www.silverlightshow.net/items/Book-review-XNA-Game-Studio-4.0-Programming-Developing-for-Windows-Phone-7-and-Xbox-360.aspx#comments http://www.silverlightshow.net/items/Book-review-XNA-Game-Studio-4.0-Programming-Developing-for-Windows-Phone-7-and-Xbox-360.aspx Tue, 27 Sep 2011 02:32:00 GMT Book review: Professional Windows Phone 7 Game Development <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; width: 200px; float: right; margin-left: 10px; padding-top: 5px;"> <h3>Don't miss...</h3> <ul style="list-style-type: circle; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-left: 20px; font-size: 12px;"> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/video/XNA-for-WP7-Webinar.aspx">Webinar recording 'XNA for Windows Phone 7'</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Windows-Phone-7-Part-1-Getting-Started.aspx">WP7 series by Andrea Boschin</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx">XNA for Silverlight Developers Ebook:</a> </li> </ul> <p style="padding-bottom: 5px; text-align: center;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx"><img style="border:0px solid;width: 71px; height: 110px;" alt="Windows Phone 7 for Silverlight Developers Ebook" src="http://www.silverlightshow.net/storage/thumb_xna_ebook_cover_150_0.png" usemap="#rade_img_map_1291385581316" /></a><br /> <strong><span style="font-size: 13px;">($2.99)</span></strong></p> <p style="font-size: 12px;">     <a href="http://www.silverlightshow.net/ebooks.aspx">All SilverlightShow Ebooks</a> <img alt="" src="http://www.silverlightshow.net/Storage/arrow-content.jpg" /></p> </div> <p><em>This review is on the book </em><a href="http://www.silverlightshow.net/book/Professional-Windows-Phone-7-Game-Development-Creating-Games-using-XNA-Game-Studio-4.aspx" target="_self"><em>'Professional Windows Phone 7 Game Development: Creating Games using XNA Game Studio 4'</em></a><em>, and has been submitted by a member of the </em><a href="http://wpug.net/" target="_blank"><em>Windows Phone 7 User Group</em></a><em> - a user group supported by SilverlightShow.<br /> We </em><a href="http://www.silverlightshow.net/About/Partners.aspx" target="_self"><em>support user groups</em></a><em> with books, swag, events promotion, free event passes and others. </em><a href="http://www.silverlightshow.net/About/contacts.aspx" target="_self"><em>Contact us to get support for your user group</em></a><em>. </em></p> <p> </p> <h3>Introduction</h3> <p>Writing a book for XNA on Windows Phone 7 (WP7) is challenging proposition, not because it is a difficult subject but because of the varied audiences. The opening chapter of ‘Professional Windows Phone 7 Game Development – Creating Games Using XNA Game Studio-4 (Wrox – Chris G.Williams, George W.Clingerman)’  explains who the book is pitched at; XNA developers who want to take advantage of a WP7 mobile platform, iOS/Android developers who want to add WP7 to their channels, and the .net developer who is a mild mannered business pro by day but wants to flex those gamer muscles by night. So is it possible to write a book to satisfy such a range of skills?</p> <p> </p> <h3>Topics by example</h3> <p>The opening chapters give a quick but good account of the WP7 platform and the developer tools you will be using. Once past the ‘Getting Started’ chapters you start getting into the WP7 technologies. When the book introduces a new technology/topic, such as Orientation or the Accelerometer, it does so by taking the reader through a small XNA project. The style works well; you end up with a number of projects each concentrating on a specific subject. This avoids some of the pitfalls of having too many new features and not knowing which one is wrong when the application crashes (perhaps that’s just me). It is also clear that the authors have tried to write the code samples to provide reusable utilities, not only for other WP7 applications but also with one eye on porting to other XNA platforms such as the Xbox. The obvious example of this is for input. You’ll learn about touch specific gestures (touch is not only on the phone) and that WP7 only has one XNA controller. However, rather than show samples dealing with just the one controller it includes code to handle multiple controllers (players). You could argue that’s confusing but I like that it reminds you to design for different channels and for potential future enhancements to WP7.</p> <p> </p> <h3>XNA or Windows Phone?</h3> <p>As you would expect from such a book the subjects covered vary from those focusing on XNA (such as game loops, controllers, handling screen/state transitions, etc) to those more aligned with WP7 (accelerometer, vibrate, handling calls, state management, etc). Some topics, such as background music or the back button, have their own very specific XNA & Phone flavours to them and the book does particularly well in making sure you are aware of the certification issues and pitfalls around those subjects. </p> <p> </p> <h3>3D World</h3> <p>Previously when reading “learn gaming” books they tend to start with 2d as a basic course and then keep 3d for the advanced. The authors have decided that they can unleash 3d onto you. I think that given XNA is more 3d oriented this makes sense. However, it does present a problem; the book has to introduce 3d modelling tools. It does a good job at helping you dip your toe in the water with ‘Blender’ but do not expect in depth tutorials, it’s left up to you to grapple with the complex 3d modeller of your choice. </p> <p>Once you have your models you are shown how to animate and transform them. I found this section to be a little light on content but since it is a big subject the book does direct you to the samples from Microsoft rather than simply regurgitate what you can get for free. </p> <p> </p> <h3>A Connected World</h3> <p>Today’s gaming world often involves the internet. Although WP7 does not yet have the peer-to-peer connections for what most people would consider a true network game, the book takes a couple of chapters to introduce how you might exploit its networking capabilities. This is split into two main camps; notifications you can provide/receive on the phone and consuming web services via WCF/REST. The book uses the examples of a high-score and a game match-making service for the tutorials. This area represents a real problem for the authors. Pitch this too low and risk really boring the .net developer, too high and you’ll lose new-comers to web services. I feel it was a little too low and I confess that I became bored quite quickly, but it is a good introduction to web services so it just depends on the reader’s needs.</p> <p> </p> <h3>More Phone</h3> <p>The remainder of the book focuses on WP7 features that really do not have a great deal of interest to the gamer. I feel a bit harsh writing that since the chapters culminate on a jigsaw picture puzzle utilising the picture chooser/camera. So sure you can use all the features to create some creative games but for me this was an interesting aside rather than “real” game development.</p> <p> </p> <h3>Can I write a game?</h3> <p>Having a book that talks about phone and XNA technologies is all well and good, but the real question is, “does it help me write a game?” It seems to me that the authors were stuck with a dilemma. Do they show small samples to help you understand a specific technology or do they introduce a sample game and take you along for the ride by slowly implementing it? As already mentioned they opted for the small samples but eventually, about half-way through the book, you are rewarded by implementing a full game, ‘Drive & Dodge’. It’s a simple game and uses all the techniques and technologies prior to the introduction of 3d. The second sample, ‘Poker Dice with Friends’ utilises the web services and 3d techniques. I don’t think anyone would expect to immediately be writing the next Halo but at what can you expect? The criteria I use to judge the level of a gaming book includes; collision detection, animated sprites and how to increase the speed/complexity of a game. These areas are covered but only at a basic level. You will certainly have enough skills to move assets around a screen, run a 3d animation and detect collisions by simple bounding areas. You are shown how to adjust movement based on elapsed time. However, I want to write more compelling games where I need much better collision detection than a bounding square. I want to animate Sonic as he jumps and spins. I want the on-coming obstacles on the road to hit my car rather than leap-frog past them because of the displacement due to speed and elapsed time.</p> <h4>Recommendation</h4> <p>I believe that the book should really be entitled, ‘Introducing XNA Windows Phone Development’. As an introduction it is good and provides you with a set of useful classes. However there is room for improvement. I would like to see it explain how to add a texture to a project. The downloadable solutions should be updated to the current version of XNA – can you tell I wasted time on that? Also much of the code is repeated and it does start to resemble a code typing marathon from a late 80s computer magazine. But these are not big problems and I would happily recommend this book for those gaming newbies – so those .net business pro’s should read this, although speed read the web services. Those with previous game writing experience will learn about WP7 touch and other subjects specific to the phone, so there is certainly something for you. </p> <p>Overall it’s a good book; if you’re new to game development then consider it an introduction, if you do not know WP7 then it will plug a lot of gaps for you. </p> http://www.silverlightshow.net/items/Review-of-Professional-Windows-7-Game-Development.aspx editorial@silverlightshow.net (pauliom ) http://www.silverlightshow.net/items/Review-of-Professional-Windows-7-Game-Development.aspx#comments http://www.silverlightshow.net/items/Review-of-Professional-Windows-7-Game-Development.aspx Fri, 19 Aug 2011 08:23:00 GMT Book Review: Windows Phone 7 Game Development <div style="padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; width: 200px; float: right; margin-left: 10px; padding-top: 5px;border: #dddddd 1px solid;"> <h3>Don't miss...</h3> <ul style="list-style-type: circle; margin: 0px; padding-left: 20px; font-size: 12px;"> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/video/XNA-for-WP7-Webinar.aspx">Webinar recording 'XNA for Windows Phone 7'</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Windows-Phone-7-Part-1-Getting-Started.aspx">WP7 series by Andrea Boschin</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx">XNA for Silverlight Developers Ebook:</a> </li> </ul> <p style="text-align: center; padding-bottom: 5px;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx"><img style="width: 71px; height: 110px;border: 0px solid;" alt="Windows Phone 7 for Silverlight Developers Ebook" src="http://www.silverlightshow.net/storage/thumb_xna_ebook_cover_150_0.png" usemap="#rade_img_map_1291385581316" /></a><br /> <strong><span style="font-size: 13px;">($2.99)</span></strong></p> <p style="font-size: 12px;">     <a href="http://www.silverlightshow.net/ebooks.aspx">All SilverlightShow Ebooks</a> <img alt="" src="http://www.silverlightshow.net/Storage/arrow-content.jpg" /></p> </div> <p><em>This review is for the book </em><a href="http://www.silverlightshow.net/book/Windows-Phone-7-Game-Development.aspx" target="_self"><em>'Windows Phone 7 Game Development'</em></a><em>, and has been submitted by a member of the </em><a href="http://wpug.net/" target="_blank"><em>Windows Phone 7 User Group</em></a><em> - a user group supported by SilverlightShow.<br /> We </em><a href="http://www.silverlightshow.net/About/Partners.aspx" target="_self"><em>support user groups</em></a><em> with books, swag, events promotion, free event passes and others. </em><a href="http://www.silverlightshow.net/About/contacts.aspx" target="_self"><em>Contact us to get support for your user group</em></a><em>. <br />  </em></p> <p>Gaming has exploded in popularity in recent years, more so on mobile devices. With these devices getting more powerful and more accessible, a wealth of possibilities is now open for developers.</p> <p>There is no denying that game development is still a difficult task, but there are a wealth of resources, including this Windows Phone 7 Game Development book, to help you get well on your way to defeating goblins, winning a drag race or concurring a new world.</p> <p> </p> <h4>The Book</h4> <p> </p> <p>Windows Phone 7 Game development is, overall, a really great book and resource. Developers with a background in .NET/C# development will gain the most from this book, and even seasoned game developers who are new to the windows phone 7 platform will pick up a thing or two. However, this book is not aimed at developers who are new to programming or have little to no experience with .NET/C#.</p> <p> </p> <h4>XNA</h4> <p> </p> <p>The general order of the book is great and flows nicely. Proceedings start with an introduction the Windows Phone 7 landscape with getting started tutorials centred on XNA. These lessons focus on 2D gaming and demonstrate how to draw sprites and text to the phone screen.</p> <p>After this, the author moves on to creating a reusable game framework. To me, this is quite a risky thing to be talking about from Chapter 2 but I feel it works really well. </p> <p>Right now, there are no adopted patterns / frameworks for XNA, so getting developers to think about this early on is great. This framework is then used to build a small space ship game and is developed in subsequent chapters.</p> <p>The next few chapters are then used to introduce some more key concepts and then implement them into a working game that was started in the previous chapter. User Input discusses how the user can interact with a game – Keyboard, Touch etc. and Sounding Out with Game Audio discusses how to add sound effects and music to the game.</p> <p>By this point, the reader is now in a great place to understand 2D games in XNA, but this is only part of the story. The next few chapters after this are where the real take-away content begins – a world of 3D!</p> <p>What I really like here is that the author has brought it back and explains the core mathematics needed to create 3D worlds. these are then implemented within the context of XNA. This really adds value as understanding vertices, matrices, rendering, depth buffers etc. are essential and the knowledge gained here can be applied to other frameworks and platforms too. </p> <p>The sample games are also really great, and include flying a paper aeroplane around a 3D skybox. Very cool!</p> <p>After this, there is some more value-added knowledge to be gained from the “enhancing your game” and “application life cycle” chapters. Here, features such as a settings class and a high score table are added to the game framework and other content covers phone specific events and persisting game data.</p> <p> </p> <h4>Silverlight</h4> <p> </p> <p>This is a relatively short chapter that introduces Silverlight as a game platform.</p> <p>The section starts with an introduction to Silverlight and takes a walkthrough the tooling and languages before delving into the controls that come out of the box with the framework. </p> <p>Once this has been explained, the game specific Silverlight content begins with a look at sprites, animations & storyboards, events, input and a discussion on performance.</p> <p>The final Silverlight specific chapter demonstrates Navigation, Game State, adding Music & Sound effects and using the XNA audio library within a Silverlight project. </p> <p>In my opinion, this content is great at what it explains, but in comparison to how in depth the XNA content goes, it feels a little out of place. If you are looking at using Silverlight to create a game, you might be better placed finding another book.</p> <p> </p> <h4>Distribution</h4> <p> </p> <p>The final part of the book is framework agnostic and covers how to distribute your game. This covers testing, trial mode, promotional upgrades and phone specific features such as theming.</p> <p>Finally, there is content on the submission requirements and a walkthrough on how to prepare your game for submission to the marketplace.</p> <p>The book ends with some notes on how to port your code to other platforms, and this includes making your XNA game run on Windows and your Silverlight application run in the browser.</p> <p> </p> <h4>Conclusion</h4> <p> </p> <p>Overall, this book is fantastic if you are a developer who has some C#/.NET experience and you are looking to create games on Windows Phone 7. If you are looking to create games in Silverlight or have little to no experience with programming, you will be better placed seeking a different book to get started.</p> http://www.silverlightshow.net/items/Book-Review-Windows-Phone-7-Game-Development.aspx editorial@silverlightshow.net (lookitskris ) http://www.silverlightshow.net/items/Book-Review-Windows-Phone-7-Game-Development.aspx#comments http://www.silverlightshow.net/items/Book-Review-Windows-Phone-7-Game-Development.aspx Thu, 18 Aug 2011 15:34:00 GMT XNA for Silverlight developers: Part 13 - Mango (2) <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; padding-top: 5px; text-align: center;">Are you interested in <strong>creating games for Windows Phone 7</strong> but don't have XNA experience? <br /> <a href="http://www.silverlightshow.net/video/XNA-for-WP7-Webinar.aspx">Watch the recording</a> of the recent intro webinar delivered by Peter Kuhn '<strong>XNA for Windows Phone 7</strong>'.<a href="https://www1.gotomeeting.com/register/256344145" target="_blank"></a><br /> </div> <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; width: 196px; float: right; height: 401px; margin-left: 10px; margin-right: 5px; padding-top: 5px;"> <h3>Don't miss...</h3> <ul style="list-style-type: circle; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-left: 20px; font-size: 12px;"> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/WP7-What-is-Windows-Phone-7.aspx">What is Windows Phone series</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Exploring-Silverlight-XNA-integration-on-Windows-Phone-7.aspx">XNA article by Levente Mihály</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Windows-Phone-7-Part-1-Getting-Started.aspx">WP7 series by Andrea Boschin</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx">The XNA series is now available as a fully offline resource</a> </li> </ul> <p style="padding-bottom: 5px; text-align: center;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx"><img style="border:0px solid;" alt="XNA for Silverlight Developers Ebook" src="http://www.silverlightshow.net/storage/thumb_xna_ebook_cover_150_0.png" usemap="#rade_img_map_1291385581316" /></a></p> <p style="font-size: 12px;">     <a href="http://www.silverlightshow.net/ebooks.aspx">All SilverlightShow Ebooks</a> <img alt="" src="http://www.silverlightshow.net/Storage/arrow-content.jpg" /></p> </div> <p style="text-align: left;"><strong>This article is compatible with Windows Phone "Mango". <em><br /> Latest update: Aug 1, 2011.</em></strong></p> <p>In the last part of this series we've taken a look at one of the major new features in the upcoming Mango update for Windows Phone: integrating Silverlight and XNA in the same application, which is a thrilling opportunity for both normal application developers to enrich their applications with 2D/3D graphics content as well as game developers to simplify development of menus and other screens that require a rich user interface. In this part we'll take a look at the other new features Mango will bring that are particularly interesting to game developers. Remember that you need <a href="http://www.microsoft.com/downloads/en/details.aspx?FamilyID=77586864-ab15-40e1-bc38-713a95a56a05&displaylang=en">the beta version of the developer tools</a> if you want to try any of these features yourself.</p> <h3>Fast Application Switching</h3> <p>With the update, the execution model of Windows Phone will be improved. In particular, a new "Dormant" state will be added to the application life cycle. The way this works is that when the user navigates away from your application, instead of tombstoning it right away, it is suspended in a way that preserves the whole application state intact in memory. The advantage of this is that you do not need to restore this state when the user returns to your application. Only if the user continues to work with the phone and the operating system eventually gets into the situation that it needs to free resources (memory) to guarantee a great user experience for a newly started or the currently running application, your application will be tombstoned.</p> <p>To give the developer the possibility to distinguish between the dormant and tombstoned state, the Activated event of the <a href="http://msdn.microsoft.com/en-us/library/ff708002(v=VS.92).aspx">PhoneApplicationService</a> now provides an <a href="http://msdn.microsoft.com/en-us/library/hh239155(v=VS.92).aspx">IsApplicationInstancePreserved property</a> in its event arguments. If you want to benefit from the new fast application switching, you should not do any of your normal tombstoning restore procedures when this property is true. Everything is just as the user left it, even running timers and similar things are restored automatically. What you do need to do is check and restore external resources, for example when you had open network connections. The new <a href="http://msdn.microsoft.com/en-us/library/ff431744(v=VS.92).aspx">code samples</a> for the Mango update include an Execution Model Sample that demonstrates the feature in all details.</p> <h4>Debugging Tombstoning/the Dormant State</h4> <p>The Windows Phone emulator you use to develop your applications fully supports the new feature. In some cases this can be undesired, for example when you want to test your application's behavior with tombstoning and activation after it had been terminated. One nice feature of the new Windows Phone development tools is that you can influence this behavior in the project properties in Visual Studio:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/________image_2.png"><img width="460" height="204" style="border:0px; background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_________image_thumb.png" /></a></p> <p>When you check the above option, your application will be tombstoned when you debug in the emulator – nice!</p> <p><em>Note: for XNA projects, the option is located on the "XNA Game Studio" tab in the project properties.</em></p> <h3>Support for Sockets</h3> <p>The topic of multiplayer gaming has been treated a bit as an orphan for hobby and indie game programmers in the first release of Windows Phone 7. The platform of course already has networking options, but they're based on web services and hardly allow more than implementing turn-based multiplayer games. The upcoming platform update adds support for sockets to the phone, which is much more suitable to implement a sophisticated, real-time multiplayer gaming experience. Both UDP and TCP clients are supported.</p> <p>I have written a separate article on socket support in Mango that implements a small sample using a Windows console application as server. You can read this post <a href="http://www.pitorque.de/MisterGoodcat/post/Windows-Phone-7-Mango-Sockets.aspx">on my blog</a>; it also provides the full source for download. The <a href="http://msdn.microsoft.com/en-us/library/ff431744(v=VS.92).aspx">code samples for Windows Phone</a> also have a complete sample for this new feature: "Tic-Tac-Toe Over Sockets" is a complete Tic-Tac-Toe game that connects to a remote server an lets you play against a (not so brilliant :]) computer AI.</p> <p>Sockets surely are a complex topic with a lot of subtle issues you can trip up on, and implementing your own solution on top of it can be troublesome. However, I expect that frameworks and helpers will emerge in the future and open the way for a broader audience to benefit from this feature easier.</p> <h3>Profiling</h3> <p>Games are probably the category of consumer software that has the longest tradition in performance optimization. Even today games on the desktop are optimized at a really low level, for example by implementing the most crucial routines in assembly. On the phone there is nothing like this, but still optimization is an important topic, especially given the limited resources available on the platform. A lot of developers not only make use of tools like profilers once they run into performance <em>problems</em>, but they also are interested in getting that information to optimize for power consumption or response times to user input. Luckily, with the upcoming Mango update, there will be a built-in option for that.</p> <p>To start a new profiling session, you have to choose the new "Start Windows Phone Performance Analysis" option from the Debug menu in Visual Studio:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_______image_6.png"><img width="399" height="418" style="border:0px; background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/________image_thumb_2.png" /></a></p> <p>The next window enables changing some of the parameters of the session. I don't know if the "Memory" option is disabled because of the beta status or if it is simply not supported in the emulator.</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/______image_8.png"><img width="603" height="424" style="border:0px; background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/______image_thumb_3.png" /></a></p> <p>You should take that warning seriously – it usually does not make any sense to profile the emulator as its performance has nothing in common with real devices. You cannot even relate your observations to real world numbers by "scaling the numbers" or similar; often the bottlenecks and limitations are completely different on the real hardware (<a href="http://www.pitorque.de/MisterGoodcat/post/What-difference-does-a-sprite-sheet-make.aspx">here</a> is an example of such an analysis I've done on that topic).</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/____image_10.png"><img width="186" height="311" style="border:0px; background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_____image_thumb_4.png" /></a></p> <p>After finishing the profiling session, Visual Studio will present the collected data in the form of several graphs that show the frame rate, CPU and memory usage as well as other data like storyboards and garbage collection events. You can then select time spans and take a look at the details:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/__image_16.png"><img width="439" height="618" style="border:0px; background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/__image_thumb_7.png" /></a></p> <p>You can use the menu marked in the screenshot above to get comfortable access to detailed data about the individual frames, the storyboards and visual tree structure, the loaded modules and the executed functions in the selected time frame. The latter one also allows jumping directly to the related source code if it is part of your own application. A full discussion of how to interpret these results to optimize your application is way beyond the scope of this article. However, the MSDN documentation already has a few How-to articles about optimizing the <a href="http://msdn.microsoft.com/en-us/library/hh202932(v=VS.92).aspx">responsiveness</a> and <a href="http://msdn.microsoft.com/en-us/library/hh202935(v=VS.92).aspx">animation smoothness</a> of your application using the profiler. The general information about the profiling options with detailed explanations of the reports can be found <a href="http://msdn.microsoft.com/en-us/library/hh202934(v=VS.92).aspx">here</a>.</p> <h3>Sensor Input</h3> <p>One of the aspects of sensor input that will be improved is the tooling side. In the new emulator, you will be able to simulate accelerometer input with a nice visual interface, making it a lot easier to test this kind of input for your games in the emulator (without relying on third party solutions for this).</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/__image_18.png"><img width="500" height="500" style="border:0px; background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/__image_thumb_8.png" /></a></p> <p>In addition to the improved tooling, the sensor APIs are extended by support for gyroscopes and the compass. If you are familiar with the topic then you know that all three of those sensors are needed to accurately determine the orientation of the device. To spare you from implementing the needed math to combine the low-level data from these sensors, the Mango update also introduces the <a href="http://msdn.microsoft.com/en-us/library/hh239189(v=VS.92).aspx">Motion class</a>. This class can be used similarly like the sensors (using an event-based approach and and the possibility to start/stop the reading of values) and provides <a href="http://msdn.microsoft.com/en-us/library/hh203182(v=VS.92).aspx">MotionReading</a> objects as result. The data included by that class contains the attitude (yaw, pitch, roll), acceleration and gravity information and the timestamp of the calculation. These values allow the implementation of interesting applications (for example in augmented reality scenarios) and games.</p> <p><em>Note: Gyroscopes are not a hardware requirement for phone vendors. You have to take that into consideration and take proper action if a gyroscope is not available on the device your application is running on.</em></p> <h3>Summary</h3> <p>I hope <a href="http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-12-Mango-1.aspx">part 12</a> and this article were able to shed some light on some of the interesting new features for game developers that the upcoming Windows Phone update will bring for you. I think it is obvious that this is not only a small update, but a tremendous step forward towards a more feature-complete platform and an even greater experience not only for the end-user, but also for us developers. Have fun building your dream game!</p> http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-13-Mango-2.aspx editorial@silverlightshow.net (Peter Kuhn ) http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-13-Mango-2.aspx#comments http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-13-Mango-2.aspx Mon, 20 Jun 2011 11:42:00 GMT XNA for Silverlight developers: Part 12 - Mango (1) <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; padding-top: 5px; text-align: center;">Are you interested in <strong>creating games for Windows Phone 7</strong> but don't have XNA experience? <br /> <a href="http://www.silverlightshow.net/video/XNA-for-WP7-Webinar.aspx">Watch the recording</a> of the recent intro webinar delivered by Peter Kuhn 'XNA for Windows Phone 7'.<a href="https://www1.gotomeeting.com/register/256344145" target="_blank"></a><br /> </div> <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; width: 196px; float: right; height: 401px; margin-left: 10px; margin-right: 5px; padding-top: 5px;"> <h3>Don't miss...</h3> <ul style="list-style-type: circle; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-left: 20px; font-size: 12px;"> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/WP7-What-is-Windows-Phone-7.aspx">What is Windows Phone series</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Exploring-Silverlight-XNA-integration-on-Windows-Phone-7.aspx">XNA article by Levente Mihály</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Windows-Phone-7-Part-1-Getting-Started.aspx">WP7 series by Andrea Boschin</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx">The XNA series is now available as a fully offline resource</a> </li> </ul> <p style="padding-bottom: 5px; text-align: center;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx"><img style="border:0px solid;" alt="XNA for Silverlight Developers Ebook" src="http://www.silverlightshow.net/storage/thumb_xna_ebook_cover_150_0.png" usemap="#rade_img_map_1291385581316" /></a></p> <p style="font-size: 12px;">     <a href="http://www.silverlightshow.net/ebooks.aspx">All SilverlightShow Ebooks</a> <img alt="" src="http://www.silverlightshow.net/Storage/arrow-content.jpg" /></p> </div> <p style="text-align: left;"><strong>This article is compatible with Windows Phone "Mango". <em><br /> Latest update: Aug 1, 2011.</em></strong></p> <p>Although it'll take some months until the final release, the upcoming update for the Windows Phone 7 platform has already drawn a lot of attention, both in the developer community as well as for customers. We were able to see demonstrations of some of the new features early, for example at Microsoft's conference MIX, but the real excitement started when <a href="http://www.microsoft.com/downloads/en/details.aspx?FamilyID=77586864-ab15-40e1-bc38-713a95a56a05&displaylang=en">a beta version of the developer tools</a> was released to the public a few weeks ago. In this article we'll take a look at what will change regarding game development; and if you haven't had time to get your teeth into the upcoming features and improvements yet, you'll be surprise just how much is in the box for us with this update.</p> <h3>Integration of Silverlight and XNA</h3> <p>This is one of the biggest improvements of the Mango update, and something that developers <a href="http://forums.silverlight.net/forums/p/168531/474934.aspx">have been crying for</a> since the very first moments of Windows Phone 7. Some – like the creative minds in that linked thread – have invented workarounds to enable this feature in some way, but of course all these efforts were futile, since the certification requirements explicitly forbid mixing both technologies. This will change with Mango, in a controlled way that allows integration of both technologies within the same application. Let's see how that works.</p> <h4>Creating the Application</h4> <p>To set up a project that combines Silverlight and XNA, you can use one of the two new templates that come with the beta developer tools for this purpose. The first one can be found in the "Silverlight for Windows Phone" node of the "New Project" dialog of Visual Studio and is named "Windows Phone 3D Graphics Application":</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_______image_2.png"><img width="640" height="455" style="border:0px; background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/________image_thumb.png" /></a></p> <p>The other template is located in the "XNA Game Studio 4.0" node and named "Windows Phone Rich Graphics Application":</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_______image_4.png"><img width="640" height="448" style="border:0px; background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/________image_thumb_1.png" /></a></p> <p>When you compare both templates, you can see that they create the same project structure. The difference is that the template that can be found in the Silverlight node adds some minimal game content (a moving rectangle) whereas the template in the XNA node only has the usual blank cornflower blue screen. If you want to learn about the new feature I therefore recommend using the first template, as the pre-built content helps you understand the involved concepts a bit better.</p> <h3></h3> <h4>The Project Structure</h4> <p>The first interesting thing to note when you look at the project is the structure of the solution itself:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/______image_6.png"><img width="347" height="365" style="border:0px; background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_______image_thumb_2.png" /></a></p> <p>As you can see, three projects are created:</p> <ul> <li>A Silverlight project that is set as the startup project in the solution. </li> <li>An XNA content project. </li> <li>An XNA class library project. </li> </ul> <p>The reason for this setup is that on the one hand Silverlight always takes the leading role in an integrated XNA/Silverlight application (which might actually be a big advantage for those of you coming from Silverlight development). On the other hand, Silverlight doesn't know anything about XNA content and how to handle it. To solve this problem, the XNA class library is used as "the glue" between the two, meaning the content project is processed by the class library, and Silverlight in turn uses the class library to get access to the processed content.</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_____image_8.png"><img width="347" height="656" style="border:0px; background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_____image_thumb_3.png" /></a></p> <p>Apart from that there's nothing special to the XNA parts in this structure. You can use the content project just like we have learned in <a href="http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-1-Fundamentals.aspx">part 1</a> of this series. And although you can add code to the XNA class library, you don't need to and simply can leave it completely empty if you like.</p> <h4>The SharedGraphicsDeviceManager</h4> <p>Like I just wrote, Silverlight takes the lead in the concept of integrated XNA/Silverlight apps, which means that the navigation features and concept of pages of normal Silverlight applications fully apply, and none of the central infrastructure elements of XNA, like the Game or GameComponent classes, are used. The idea instead is to explicitly tell the runtime when you want to use XNA rendering, on a per-page basis.</p> <p>To this end, a new assembly Microsoft.Xna.Framework.Interop is introduced which contains the <a href="http://msdn.microsoft.com/en-us/library/hh221558(v=XNAGameStudio.41).aspx">SharedGraphicsDeviceManager class</a>. That class enables you to use XNA's immediate rendering mode in your Silverlight application and provides access to a graphics device through a property named the same. In the templates, the manager is declared in the application lifetime objects in the App Xaml, and from there on can be accessed using a static "Current" property on the manager class throughout your application, whenever you need to access it.</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="kwrd"><</span><span class="html">Application.ApplicationLifetimeObjects</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> <span class="rem"><!--Required object that handles lifetime events for the application--></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> <span class="kwrd"><</span><span class="html">shell:PhoneApplicationService</span> </pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> <span class="attr">Launching</span><span class="kwrd">="Application_Launching"</span> <span class="attr">Closing</span><span class="kwrd">="Application_Closing"</span> </pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> <span class="attr">Activated</span><span class="kwrd">="Application_Activated"</span> <span class="attr">Deactivated</span><span class="kwrd">="Application_Deactivated"</span><span class="kwrd">/></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> </pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> <span class="rem"><!--The SharedGraphicsDeviceManager is used to render with the XNA Graphics APIs--></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> <span class="kwrd"><</span><span class="html">xna:SharedGraphicsDeviceManager</span> <span class="kwrd">/></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span> <span class="kwrd"></</span><span class="html">Application.ApplicationLifetimeObjects</span><span class="kwrd">></span></pre> <!--CRLF--></div> </div> <p>The pattern to activate XNA rendering is to turn on the so-called sharing mode when the user navigates to a page that contains XNA content, and to turn it off again once the user navigates away. For this, an <a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.graphicsdeviceextensions.setsharingmode(v=XNAGameStudio.41).aspx">extension method "SetSharingMode"</a> was added to the GraphicsDevice class.</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="kwrd">protected</span> <span class="kwrd">override</span> <span class="kwrd">void</span> OnNavigatedTo(NavigationEventArgs e)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> <span class="rem">// Set the sharing mode of the graphics device to turn on XNA rendering</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(<span class="kwrd">true</span>);</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> <span class="kwrd">base</span>.OnNavigatedTo(e);</pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> }</pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span> <span class="kwrd">protected</span> <span class="kwrd">override</span> <span class="kwrd">void</span> OnNavigatedFrom(NavigationEventArgs e)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span> <span class="rem">// Set the sharing mode of the graphics device to turn off XNA rendering</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> 12:</span> SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(<span class="kwrd">false</span>);</pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> 13:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> 14:</span> <span class="kwrd">base</span>.OnNavigatedFrom(e);</pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> 15:</span> }</pre> <!--CRLF--></div> </div> <p>It's important to activate sharing mode before you do anything XNA related in your application (like loading content or drawing things), or you will end up with exceptions. </p> <h4>The "Game Loop"</h4> <p>Speaking of drawing things, you might wonder where you put all your update and drawing logic now that there's no Game class anymore? The solution to this is another core component : <a href="http://msdn.microsoft.com/en-us/library/hh221527(v=XNAGameStudio.41).aspx">the GameTimer class</a>. When you look at the members of that class you should see some familiar faces. It has <a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.gametimer.draw(v=XNAGameStudio.41).aspx">Draw</a> and <a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.gametimer.update(v=XNAGameStudio.41).aspx">Update</a> events, and <a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.gametimereventargs_members(v=XNAGameStudio.41).aspx">the GameTimerEventArgs</a> passed into the event handlers has <a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.gametimereventargs.elapsedtime(v=XNAGameStudio.41).aspx">ElapsedTime</a> and <a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.gametimereventargs.totaltime(v=XNAGameStudio.41).aspx">TotalTime</a> properties. This is all very similar to the update and draw mechanism of normal XNA games and the information the <a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.gametime_members(v=XNAGameStudio.41).aspx">GameTime class</a> provides. The game timer has additional features though, for example it is also responsible for setting the desired frame rate (corresponds to the TargetElapsedTime property of the Game class). A typical setup of the GameTimer hence looks like:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="kwrd">public</span> GamePage()</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> InitializeComponent();</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> <span class="rem">// Create a timer for this page</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> timer = <span class="kwrd">new</span> GameTimer();</pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> timer.UpdateInterval = TimeSpan.FromTicks(333333);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> timer.Update += OnUpdate;</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span> timer.Draw += OnDraw;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span> }</pre> <!--CRLF--></div> </div> <p>Like other timers you know from Silverlight and .NET, this one has to be started to actually work too, and it can and also should be stopped in certain situations. Once again the pattern is to activate the timer when the user navigates to an XNA-enabled page, and to stop it when the user navigates away. Combined with the shared graphics devices manager, the code then looks like:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="kwrd">protected</span> <span class="kwrd">override</span> <span class="kwrd">void</span> OnNavigatedTo(NavigationEventArgs e)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> <span class="rem">// Set the sharing mode of the graphics device to turn on XNA rendering</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(<span class="kwrd">true</span>);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> <span class="rem">// Start the timer</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> timer.Start();</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span> <span class="kwrd">base</span>.OnNavigatedTo(e);</pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span> }</pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> 12:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> 13:</span> <span class="kwrd">protected</span> <span class="kwrd">override</span> <span class="kwrd">void</span> OnNavigatedFrom(NavigationEventArgs e)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> 14:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> 15:</span> <span class="rem">// Stop the timer</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> 16:</span> timer.Stop();</pre> <!--CRLF--> <pre class="alt"><span id="lnum17" class="lnum"> 17:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum18" class="lnum"> 18:</span> <span class="rem">// Set the sharing mode of the graphics device to turn off XNA rendering</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum19" class="lnum"> 19:</span> SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(<span class="kwrd">false</span>);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum20" class="lnum"> 20:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum21" class="lnum"> 21:</span> <span class="kwrd">base</span>.OnNavigatedFrom(e);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum22" class="lnum"> 22:</span> }</pre> <!--CRLF--></div> </div> <p>In the Update event handler you can place all your update logic like you would in the Update override of the XNA Game class, for example to move your sprites, compute AI moves and similar things. The same is true for the Draw event handler. Here you use the shared graphics device manager to get access to the required GraphicsDevice instance that you need to e.g. clear the screen and draw your primitives. The rest of the method again works like in normal XNA games.</p> <h4>… and the Rest</h4> <p>When you look at the template that we're using, you can find a few additional elements you know from your XNA games here. The App class for example has a <a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.content.contentmanager.aspx">ContentManager</a> instance. This central place of definition allows you to access it from anywhere to load your game content. In fact, you are free to load your content at any time you like, as long as you have activated sharing mode before. The recommendation though is to also load the content in the OnNavigatedTo overload of a page where it is safe to do and doesn't interfere with the update and draw events of the timer.</p> <p>Another thing to note is the "InitializeXnaApplication" method in the App class which creates another GameTimer instance to call the Update method of the <a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.frameworkdispatcher.aspx">FrameworkDispatcher</a>. This is not particularly new to the Mango update. If you wanted to use certain XNA features like the advanced sound playback in your Silverlight application before, you already <a href="http://msdn.microsoft.com/library/ff842408.aspx">had to use the FrameworkDispatcher manually</a> to make this work. The template only uses the new GameTimer class to set this up. It's nice to know though that everything's already prepared to also use sound and other XNA features in your mixed XNA/Silverlight app.</p> <h4>Rendering Silverlight and XNA on the Same Page</h4> <p>Without writing any code, the new templates already show how you can mix Silverlight and XNA pages in the same application. But wait, there's more! You can also mix Silverlight content with XNA <em>on the same page</em>. This brings us to another important new class in the Mango update, the <a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.uielementrenderer(v=XNAGameStudio.41).aspx">UIElementRenderer</a>. This class has only one purpose: to render a Silverlight UIElement into a texture. The texture in turn can then be used in your draw method to be used and rendered in any way you like. To demonstrate this feature, I add the following code to the "GamePage.xaml", which didn't contain any elements until now:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="kwrd"><</span><span class="html">Grid</span> <span class="attr">x:Name</span><span class="kwrd">="LayoutRoot"</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> <span class="kwrd"><</span><span class="html">TextBlock</span> <span class="attr">HorizontalAlignment</span><span class="kwrd">="Center"</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> <span class="attr">VerticalAlignment</span><span class="kwrd">="Center"</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> <span class="attr">Text</span><span class="kwrd">="Silverlight and XNA"</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> <span class="attr">FontSize</span><span class="kwrd">="36"</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> <span class="attr">RenderTransformOrigin</span><span class="kwrd">="0.5, 0.5"</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> <span class="kwrd"><</span><span class="html">TextBlock.RenderTransform</span><span class="kwrd">></span> </pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> <span class="kwrd"><</span><span class="html">RotateTransform</span> <span class="attr">x:Name</span><span class="kwrd">="Rotate"</span> <span class="kwrd">/></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span> <span class="kwrd"></</span><span class="html">TextBlock.RenderTransform</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span> <span class="kwrd"><</span><span class="html">TextBlock.Triggers</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span> <span class="kwrd"><</span><span class="html">EventTrigger</span> <span class="attr">RoutedEvent</span><span class="kwrd">="FrameworkElement.Loaded"</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> 12:</span> <span class="kwrd"><</span><span class="html">BeginStoryboard</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> 13:</span> <span class="kwrd"><</span><span class="html">Storyboard</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> 14:</span> <span class="kwrd"><</span><span class="html">DoubleAnimation</span> <span class="attr">Storyboard</span>.<span class="attr">TargetName</span><span class="kwrd">="Rotate"</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> 15:</span> <span class="attr">Storyboard</span>.<span class="attr">TargetProperty</span><span class="kwrd">="Angle"</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> 16:</span> <span class="attr">From</span><span class="kwrd">="0"</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum17" class="lnum"> 17:</span> <span class="attr">To</span><span class="kwrd">="360"</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum18" class="lnum"> 18:</span> <span class="attr">Duration</span><span class="kwrd">="0:0:2.0"</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum19" class="lnum"> 19:</span> <span class="attr">RepeatBehavior</span><span class="kwrd">="Forever"</span> <span class="kwrd">/></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum20" class="lnum"> 20:</span> <span class="kwrd"></</span><span class="html">Storyboard</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum21" class="lnum"> 21:</span> <span class="kwrd"></</span><span class="html">BeginStoryboard</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum22" class="lnum"> 22:</span> <span class="kwrd"></</span><span class="html">EventTrigger</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum23" class="lnum"> 23:</span> <span class="kwrd"></</span><span class="html">TextBlock.Triggers</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum24" class="lnum"> 24:</span> <span class="kwrd"></</span><span class="html">TextBlock</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum25" class="lnum"> 25:</span> <span class="kwrd"></</span><span class="html">Grid</span><span class="kwrd">></span></pre> <!--CRLF--></div> </div> <p>As you can see this looks like normal Silverlight content. I added a grid and a text block. The latter one has a rotate transform and a storyboard that changes the rotation angle so the text does a whole 360 degrees spin in two seconds. Would you run the application at this point, you could not see anything; although the content is defined in the Xaml, rendering of this page is handled by XNA, so the Silverlight bits don't affect what's visible on the screen. To make this work, I have to use a UIElementRenderer:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> UIElementRenderer uiElementRenderer;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> ...</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> <span class="kwrd">protected</span> <span class="kwrd">override</span> <span class="kwrd">void</span> OnNavigatedTo(NavigationEventArgs e)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> ...</pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span> <span class="rem">// create the UI element renderer that covers the whole page</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span> uiElementRenderer = <span class="kwrd">new</span> UIElementRenderer(LayoutRoot,</pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span> SharedGraphicsDeviceManager.Current.GraphicsDevice.Viewport.Width,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> 12:</span> SharedGraphicsDeviceManager.Current.GraphicsDevice.Viewport.Height);</pre> <!--CRLF--></div> </div> <p>The constructor of the UIElementRenderer expects the element to render, and the dimensions of the texture to use as a target. In my case I'm simply using the root element and a texture the size of the screen. Of course you can optimize this and only render the actual required parts of the visual tree to the screen, but that's not necessary in this example.</p> <p>The fact that the UIElementRenderer is fixed to a certain width and height should give you a clue that you need to recreate it e.g. once the layout changes (screen orientation or similar) – you cannot change its texture dimensions after you created it. The important part is that any existing renderer needs to be cleaned up using the <a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.uielementrenderer.dispose(v=XNAGameStudio.41).aspx">Dispose method</a> before you throw it away and recreate it. The same is true if e.g. the user navigates away from the page. Just make sure you be nice and free the resources used by the renderer when necessary.</p> <p>The last thing to do is to actually render the Silverlight content, first into the UIElementRenderer's internal textures, and then (using that texture) onto the screen. For the first part the renderer has a <a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.uielementrenderer.render(v=XNAGameStudio.41).aspx">Render method</a>, and for the second part you can use a normal sprite batch and access the renderers <a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.uielementrenderer.texture(v=XNAGameStudio.41).aspx">Texture property</a>.</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="rem">// let the UI element renderer render the current state</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> <span class="rem">// of the Silverlight content to its texture</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> uiElementRenderer.Render();</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> spriteBatch.Draw(uiElementRenderer.Texture,</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> Vector2.Zero,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> Color.White);</pre> <!--CRLF--></div> </div> <p>To draw 2D Silverlight elements on top of your other game content, you would make drawing this texture the very last thing in your Draw event handler (so it is rendered on top of everything else). The result is something like this:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/___image_10.png"><img width="288" height="480" style="border:0px; background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/____image_thumb_4.png" /></a></p> <p>The red square is completely handled by XNA (it's actually a red texture from the XNA content project that is moved around in the Update event handler), whereas the text is the Silverlight content rendered on top. The animation of that text is taken care of by Silverlight's animation system. Once again there's potential for optimization – e.g. if you know the Silverlight content's visual appearance has not changed, you do not re-render the texture in each frame.</p> <p>Even though it looks simple, for other scenarios handling the Silverlight rendering can become more complex. Nick Gravelyn has made a nice <a href="http://blogs.msdn.com/b/nicgrave/archive/2011/05/24/simplifying-uielementrenderer-usage-for-full-page-scenarios.aspx">helper for full-screen scenarios</a> that deals with some of these difficulties.</p> <h4>Advantages</h4> <p>This feature of integrating XNA with Silverlight has some obvious and some not so obvious advantages and creates new possibilities. One of the obvious uses is to create user interfaces. As we have learned in <a href="http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-10-UI-Elements-and-Menus.aspx">part 10</a> of this series creating even simple controls in XNA is tedious and requires a lot of work compared with Silverlight. Some more advanced features like text input can even require a tremendous amount of code on your side if you're trying to integrate it nicely into your game. The possibility to use existing Silverlight UI features to create menu pages for XNA games, or even to create HUD overlays or similar on top of your game content is a great improvement and will speed up those parts of game development a lot. </p> <p>Not so obvious improvements are that this feature now enables scenarios that previously simply were not possible with XNA, like using the WebBrowser control or embed video directly in your game. To read more about the revised decision process of choosing between XNA and Silverlight, click <a href="http://create.msdn.com/en-US/education/catalog/article/which_product_for_windows_phone">here</a>.</p> <h4>Drawbacks</h4> <p>There are also some potential drawbacks of this new feature. One is that if you are a developer who targets all "three screens" (desktop, console, phone) and try to share as much code as possible between those platforms, mixing Silverlight and XNA can even become a handicap, because it is a feature unique to the phone and doesn't port very well.</p> <p>Another issue is that if you have already invested some time in developing your game in XNA, you may now have to spend some more time to migrate to the new concept. For example, as I mentioned some elements like game components cannot be used in hybrid applications, so you have to at least partly rewrite those and/or change your game code structure. More information and tips on moving from the Game class to XNA/Silverlight can be found in <a href="http://create.msdn.com/en-US/education/catalog/article/migration_guide_moving_to_silverlight_xna">this migration guide</a>.</p> <h4>Conclusion</h4> <p>Even though it has some drawbacks and it's not perfect in every detail (yet), the possibility to mix XNA and Silverlight content has been received extremely well by most developers. I hope that this article has provided some insights into the basic ideas and concepts, and also sparked some ideas how you can use that feature to speed up your development or create exciting new things. Let me if you have any questions or feedback in the comments below, or by contacting me directly. You can download the sample project here:</p> <p><a href="http://www.silverlightshow.net/Storage/Sources/XNA_for_Silverlight_developers_Part12.zip">Download source code</a></p> http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-12-Mango-1.aspx editorial@silverlightshow.net (Peter Kuhn ) http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-12-Mango-1.aspx#comments http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-12-Mango-1.aspx Wed, 08 Jun 2011 11:10:10 GMT XNA for Silverlight developers: Part 11 - Tombstoning <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; padding-top: 5px; text-align: center;">Are you interested in <strong>creating games for Windows Phone 7</strong> but don't have XNA experience? <br /> <a href="http://www.silverlightshow.net/video/XNA-for-WP7-Webinar.aspx">Watch the recording</a> of the recent intro webinar delivered by Peter Kuhn 'XNA for Windows Phone 7'.<a href="https://www1.gotomeeting.com/register/256344145" target="_blank"></a><br /> </div> <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; width: 196px; float: right; height: 401px; margin-left: 10px; margin-right: 5px; padding-top: 5px;"> <h3>Don't miss...</h3> <ul style="list-style-type: circle; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-left: 20px; font-size: 12px;"> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/WP7-What-is-Windows-Phone-7.aspx">What is Windows Phone series</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Exploring-Silverlight-XNA-integration-on-Windows-Phone-7.aspx">XNA article by Levente Mihály</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Windows-Phone-7-Part-1-Getting-Started.aspx">WP7 series by Andrea Boschin</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx">The XNA series is now available as a fully offline resource</a> </li> </ul> <p style="padding-bottom: 5px; text-align: center;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx"><img style="border:0px solid;" alt="XNA for Silverlight Developers Ebook" src="http://www.silverlightshow.net/storage/thumb_xna_ebook_cover_150_0.png" usemap="#rade_img_map_1291385581316" /></a></p> <p style="font-size: 12px;">     <a href="http://www.silverlightshow.net/ebooks.aspx">All SilverlightShow Ebooks</a> <img alt="" src="http://www.silverlightshow.net/Storage/arrow-content.jpg" /></p> </div> <p style="text-align: left;"><strong>This article is compatible with Windows Phone "Mango". <em><br /> Latest update: Aug 1, 2011.</em></strong></p> <p>An often discussed topic on Windows Phone 7 is that of “tombstoning”, which describes the process of your application being suspended and removed from the running processes (think hibernate) to free resources on the phone. The application can be “revived” later when the user navigates back to it. It’s the developer’s responsibility to create a user experience that gives the appearance of a seamless transition between applications even when tombstoning happens. For games, the basic concept is the same as for other applications; however, the available possibilities seem slightly different. In this article, we learn about how to handle the application life cycle in conventional Silverlight applications, and how we can properly do the same in games developed with XNA.</p> <h3>Tombstoning in Silverlight</h3> <p>In a Silverlight application, what you do to handle tombstoning is use the <a href="http://msdn.microsoft.com/en-us/library/microsoft.phone.shell.phoneapplicationservice(v=vs.92).aspx">PhoneApplicationService class</a>. This class has a number of events that are raised at different times of the application lifecycle: Launching, Deactivated, Activated and Closing. The logic behind these events is pretty straight forward:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/______image_2.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px;border-width: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_______image_thumb.png" width="267" height="513" /></a></p> <p>One of the interesting details is that your application might not get reactivated at all after deactivation (see the red arrow). If the user never navigates back to your application and instead e.g. launches a new instance using the start menu, or if your application is completely removed by the memory management at a later point, the Activated event will never occur. This is why you should make sure that all vital data is persisted in the Deactivated event handler, as your application may silently be fully terminated later, without being given another chance to store unsaved data.</p> <p>Another important thing to know is that deactivation actually is <em>not</em> the same as tombstoning. When an application gets deactivated, it <em>may</em> also get tombstoned, but in certain situations it may not. The documentation mentions that e.g. if the user hits the start and back buttons in quick succession, tombstoning might not happen. It is also known that tombstoning either does not occur or is less likely to occur for certain choosers and launchers. And of course, if your application runs on a device that has the Windows Phone "Mango" update applied, not getting tombstoned right away even is the default behavior. Instead, on this updated platform your application is first put in a new state named "dormant", where it stays fully intact in memory (but without being granted any processing cycles). Only when the operating system needs to free memory for other tasks and applications later is your application eventually tombstoned. If the user returns to your dormant application before actual tombstoning happened, your application will be (almost) as if they never left, without the need to restore it manually for a persisted state.</p> <h3>Tombstoning in XNA</h3> <p>At first look, the <a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.game_members.aspx">Game class</a> in XNA <em>seems</em> to have similar events for the application life cycle: Activated, Deactivated and Exiting. Since you create a game by inheriting from that class, it’s likely that instead of using the events you will override the corresponding methods instead: OnActivated, OnDeactivated and OnExiting. In the following paragraphs, I only talk about the events, but of course everything applies to the use of these overrides too. In addition, the game has an Initialize method that is called at the beginning and hence can be considered as a counterpart to the Launching event.</p> <p>The problem with this is that the game class events work differently from the events of the PhoneApplicationService, making it much more difficult to handle tombstoning by using them. The sequence and combination the game class events are used also seems a bit confusing on Windows Phone at first. For example, the Activated and Deactivated events are not limited to situations of activation and deactivation; they’re also used when the game launches (Activated) and is shut down (Deactivated), in addition to the Initialize method and Exiting event. Even more confusing, the order these events happen in seems partly illogical. In my tests on both the emulator as well as on real devices, the Deactivated event happened <em>after</em> the Exiting event when the game was actually exited. For "normal" deactivation though the order of events was as expected.</p> <p>Things become even more problematic with Windows Phone "Mango". When the user navigates away from the game, it is not tombstoned right away anymore, but put into the new dormant state I mentioned above. For the game class events this results in a different behavior with regards to the Deactivated and Exiting events. In RTM, both the Deactivated and Exiting events happen when the user navigates away (tombstoning situation), in Mango only the Deactivated event is used, but not the Exiting event (dormant state situation). Even if the application is tombstoned later, the Exiting event will never occur on Mango.</p> <table border="1" cellspacing="0" cellpadding="2" width="636"> <tbody> <tr> <td style="width: 238px;" valign="top"> </td> <td style="width: 82px;" valign="top"> <p style="text-align: center;">Initialize</p> </td> <td style="width: 95px;" valign="top"> <p style="text-align: center;">Activated</p> </td> <td style="width: 126px;" valign="top"> <p style="text-align: center;">Deactivated</p> </td> <td style="width: 95px;" valign="top"> <p style="text-align: center;">Exiting</p> </td> </tr> <tr> <td style="width: 238px;" valign="top">Game is started</td> <td style="width: 82px;" valign="top"> <p style="text-align: center;"><strong>x</strong></p> </td> <td style="width: 95px;" valign="top"> <p style="text-align: center;"><strong>x</strong></p> </td> <td style="width: 126px;" valign="top"> <p style="text-align: center;"><strong> </strong></p> </td> <td style="width: 95px;" valign="top"> </td> </tr> <tr> <td style="width: 238px;" valign="top">Game is deactivated/tombstoned (RTM)</td> <td style="width: 82px;" valign="top"> </td> <td style="width: 95px;" valign="top"> <p style="text-align: center;"><strong> </strong></p> </td> <td style="width: 126px;" valign="top"> <p style="text-align: center;"><strong>x</strong></p> </td> <td style="width: 95px;" valign="top"> <p style="text-align: center;"><strong>x</strong></p> </td> </tr> <tr> <td style="width: 238px;" valign="top">Game is deactivated/dormant (Mango)</td> <td style="width: 82px;" valign="top"> </td> <td style="width: 95px;" valign="top"> </td> <td style="width: 126px;" valign="top"> <p style="text-align: center;"><strong>x</strong></p> </td> <td style="width: 95px;" valign="top"> </td> </tr> <tr> <td style="width: 238px;" valign="top">Game is reactivated</td> <td style="width: 82px;" valign="top"> <p style="text-align: center;"><strong>x</strong></p> </td> <td style="width: 95px;" valign="top"> <p style="text-align: center;"><strong>x</strong></p> </td> <td style="width: 126px;" valign="top"> <p style="text-align: center;"><strong> </strong></p> </td> <td style="width: 95px;" valign="top"> </td> </tr> <tr> <td style="width: 238px;" valign="top">Game is exited</td> <td style="width: 82px;" valign="top"> </td> <td style="width: 95px;" valign="top"> <p style="text-align: center;"><strong> </strong></p> </td> <td style="width: 126px;" valign="top"> <p style="text-align: center;"><strong>x</strong></p> </td> <td style="width: 95px;" valign="top"> <p style="text-align: center;"><strong>x</strong></p> </td> </tr> </tbody> </table> <p>The reason for this seemingly messy behavior is that these events are not a specific feature of XNA on Windows Phone. In particular, these events were not made to handle the application life cycle on the phone. The original intention of these features on other platforms like the PC and XBox was to provide hooks for when the game gains and loses focus. Also, these other platforms don't have concepts like tombstoning or dormant states, which is why events like Exiting also cannot do justice to the Windows Phone platform down to every detail. The consequence of all this is that these events are not suitable to detect tombstoning or handle deactivation and activation reliably, as the behavior does not allow to distinguish between the different possible causes for the events. </p> <p>Now, instead of trying to work around these limitations and somehow arrange with what we are offered by the game class, my recommendation is simple: <em>make use of the PhoneApplicationService class in your games too, and do not rely on the respective events of the game class at all</em>. This is a perfectly legitimate thing to do (it's actually even required for detecting the dormant state of Mango, see below), and we’ll look at that in more detail in a moment.</p> <h4></h4> <h4>A note on the Guide class</h4> <p>As we’ve seen <a href="http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-10-UI-Elements-and-Menus.aspx">in previous articles</a>, the <a href="http://msdn.microsoft.com/en-us/library/bb975653.aspx">Guide class</a> can be used in XNA for simple dialogs, like message boxes and situations where the user has to provide user input. When you use this class and its dialogs, it’s important to know that they also trigger the Activated and Deactivated event of the game class, even though no tombstoning happens when the game is obscured by the dialogs. Once again the reason for this is that these events originally were meant to detect a focus loss, which of course indeed is what happens here. The corresponding events of the phone application service class are not raised in this case.</p> <h3>Some sample code</h3> <p>Let’s finally dive into a sample to see how all of the above translates into code. To be able to use the phone application service in your XNA game, you first need to add references to the following assemblies to your project:</p> <blockquote> <p><span style="font-family: 'courier new'; font-size: 13px;">Microsoft.Phone <br /> System.Windows</span></p> </blockquote> <p>From there, everything is pretty straight-forward. In your game's constructor, you can hook the events of the PhoneApplicationService class as you would in a conventional Silverlight application:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="rem">// hook the events of the phone application service</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> <span class="rem">// to reliably detect launching, closing, and </span></pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> <span class="rem">// activation/deactivation/tombstoning</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> PhoneApplicationService.Current.Launching += PhoneApplicationService_Launching;</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> PhoneApplicationService.Current.Activated += PhoneApplicationService_Activated;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> PhoneApplicationService.Current.Deactivated += PhoneApplicationService_Deactivated;</pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> PhoneApplicationService.Current.Closing += PhoneApplicationService_Closing;</pre> <!--CRLF--></div> </div> <p>The actual event handlers then can be used to execute your logic. Just like for other applications, you would make sure that you store all vital data persistently (e.g. to isolated storage) in the Deactivated and Closing event handlers. Temporary data that is only relevant for the current session can be stored to the available transient storage places like the PhoneApplicationService's state property in the Deactivated event handler too. In the Activated event handler, you can restore your game state and also retrieve any temporary data from the transient storage. Finally, the Launching event handler is what replaces the Initialize method of the game and where you execute all the logic required to provide a "new instance launched" experience to your users.</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="kwrd">private</span> <span class="kwrd">void</span> PhoneApplicationService_Launching(<span class="kwrd">object</span> sender, LaunchingEventArgs e)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> <span class="rem">// here you would execute any logic required when the game launches</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> Debug.WriteLine(<span class="str">"Launching."</span>);</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> }</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> <span class="kwrd">private</span> <span class="kwrd">void</span> PhoneApplicationService_Activated(<span class="kwrd">object</span> sender, ActivatedEventArgs e)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span> <span class="rem">// execute any logic required after activation</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span> <span class="rem">// => for example, restore from isolated storage, </span></pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span> <span class="rem">// and retrieve temporary data from the transient state</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> 12:</span> Debug.WriteLine(<span class="str">"Activated."</span>);</pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> 13:</span> }</pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> 14:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> 15:</span> <span class="kwrd">private</span> <span class="kwrd">void</span> PhoneApplicationService_Deactivated(<span class="kwrd">object</span> sender, DeactivatedEventArgs e)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> 16:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum17" class="lnum"> 17:</span> <span class="rem">// deactivation detected (may or may not result in tombstoning)</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum18" class="lnum"> 18:</span> <span class="rem">// => make sure all vital data is persisted, and all temporary </span></pre> <!--CRLF--> <pre class="alt"><span id="lnum19" class="lnum"> 19:</span> <span class="rem">// data is stored to the transient state</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum20" class="lnum"> 20:</span> Debug.WriteLine(<span class="str">"Deactivated."</span>);</pre> <!--CRLF--> <pre class="alt"><span id="lnum21" class="lnum"> 21:</span> }</pre> <!--CRLF--> <pre class="alteven"><span id="lnum22" class="lnum"> 22:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum23" class="lnum"> 23:</span> <span class="kwrd">private</span> <span class="kwrd">void</span> PhoneApplicationService_Closing(<span class="kwrd">object</span> sender, ClosingEventArgs e)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum24" class="lnum"> 24:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum25" class="lnum"> 25:</span> <span class="rem">// exit detected</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum26" class="lnum"> 26:</span> <span class="rem">// => make sure all vital data is persisted</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum27" class="lnum"> 27:</span> Debug.WriteLine(<span class="str">"Closing."</span>);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum28" class="lnum"> 28:</span> }</pre> <!--CRLF--></div> </div> <p>This is a very simple solution to the above mentioned problems that allows you to reliably handle the involved events of the application life cycle for your game too, without having to fight the limitations of the corresponding game class events. The much simpler and more consistent behavior of the PhoneApplicationService is shown by the following table:</p> <table border="1" cellspacing="0" cellpadding="2" width="651"> <tbody> <tr> <td style="width: 238px;" valign="top"> </td> <td style="width: 82px;" valign="top"> <p style="text-align: center;">Launching</p> </td> <td style="width: 95px;" valign="top"> <p style="text-align: center;">Deactivated</p> </td> <td style="width: 126px;" valign="top"> <p style="text-align: center;">Activated</p> </td> <td style="width: 126px;" valign="top"> <p style="text-align: center;">Closing</p> </td> </tr> <tr> <td style="width: 238px;" valign="top">Game is started</td> <td style="width: 82px;" valign="top"> <p style="text-align: center;"><strong>x</strong></p> </td> <td style="width: 95px;" valign="top"> <p style="text-align: center;"><strong> </strong></p> </td> <td style="width: 126px;" valign="top"> <p style="text-align: center;"><strong></strong></p> </td> <td style="width: 126px;" valign="top"> </td> </tr> <tr> <td style="width: 238px;" valign="top">Game is deactivated/tombstoned (RTM)</td> <td style="width: 82px;" valign="top"> </td> <td style="width: 95px;" valign="top"> <p style="text-align: center;"><strong>x</strong></p> </td> <td style="width: 126px;" valign="top"> </td> <td style="width: 126px;" valign="top"> </td> </tr> <tr> <td style="width: 238px;" valign="top">Game is deactivated/dormant (Mango)</td> <td style="width: 82px;" valign="top"> </td> <td style="width: 95px;" valign="top"> <p style="text-align: center;"><strong>x</strong></p> </td> <td style="width: 126px;" valign="top"> </td> <td style="width: 126px;" valign="top"> </td> </tr> <tr> <td style="width: 238px;" valign="top">Game is reactivated</td> <td style="width: 82px;" valign="top"> </td> <td style="width: 95px;" valign="top"> </td> <td style="width: 126px;" valign="top"> <p style="text-align: center;"><strong>x<span style="color: #ff0000;">*</span></strong></p> </td> <td style="width: 126px;" valign="top"> </td> </tr> <tr> <td style="width: 238px;" valign="top">Game is closed</td> <td style="width: 82px;" valign="top"> </td> <td style="width: 95px;" valign="top"> </td> <td style="width: 126px;" valign="top"> </td> <td style="width: 126px;" valign="top"> <p style="text-align: center;"><strong>x</strong></p> </td> </tr> </tbody> </table> <h3> </h3> <h3>Going dormant</h3> <p>In the above table, I marked the Activated event with a small asterisk to remind you that this is the once place where the involved code and logic is different for the RTM and Mango releases of Windows Phone. In particular, the event arguments for this event (<a href="http://msdn.microsoft.com/en-us/library/ff708202(v=VS.92).aspx">ActivatedEventArgs</a>) have been extended in the Mango release, where they contain a new boolean property named <a href="http://msdn.microsoft.com/en-us/library/microsoft.phone.shell.activatedeventargs.isapplicationinstancepreserved(v=VS.92).aspx">IsApplicationInstancePreserved</a>. You should use this property to determine whether your game was actually tombstoned (false), or if it is simply activated after returning from the new dormant state (true).</p> <p>In the latter case, you do not need to re-create the game's state and retrieve your data from e.g. isolated storage, like you usually would when the game is activated from tombstoning. All the state has been preserved, the game only was put into a frozen state in memory ("dormant"). So, the improved code for the Activated event handler in a Mango compatible game would look like this:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="kwrd">private</span> <span class="kwrd">void</span> PhoneApplicationService_Activated(<span class="kwrd">object</span> sender, ActivatedEventArgs e)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> <span class="rem">// execute any logic required after activation</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> <span class="kwrd">if</span> (e.IsApplicationInstancePreserved)</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> <span class="rem">// we are only returning from dormant state</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> <span class="rem">// => no need to re-create any state, it has been preserved</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> Debug.WriteLine(<span class="str">"Activated after dormant state."</span>);</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span> }</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span> <span class="kwrd">else</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> 12:</span> <span class="rem">// we are returning from being tombstoned</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> 13:</span> <span class="rem">// => restore from isolated storage, </span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> 14:</span> <span class="rem">// and retrieve temporary data from the transient state</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> 15:</span> Debug.WriteLine(<span class="str">"Activated after being tombstoned."</span>);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> 16:</span> }</pre> <!--CRLF--> <pre class="alt"><span id="lnum17" class="lnum"> 17:</span> }</pre> <!--CRLF--></div> </div> <h1></h1> <p>With this small additional check, you have added support for the new fast application switching feature in Mango, and returning to your game when it hasn't actually been tombstoned will be much faster and smoother for your users – another great improvement of the user experience on Windows Phone which you get almost for free as a developer!</p> <h4></h4> <h4>Testing dormant state and tombstoning</h4> <p>A quick note on how to test the new dormant state vs. real tombstoning. By default, when you have the Mango development tools installed, the emulator will behave like real devices and only put your application in the new dormant state upon deactivation during debugging. Sometimes however, you want to explicitly test the code and logic of your game that is executed when it was actually tombstoned. To simplify this, a new feature has been added on the property pages of your game project (tab "XNA Game Studio"):</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_________image_4.png"><img style="border:0px; background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/__________image_thumb_1.png" width="294" height="54" /></a></p> <p> This allows you to switch between testing tombstoning and the dormant state in the emulator. I talk a bit more about the new fast application switching feature in <a href="http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-13-Mango-2.aspx">part 13</a> of this series.</p> <h3>Additional considerations and resources</h3> <p>This article doesn’t specifically talk about <em>how</em> to persist and restore data when deactivation and tombstoning happens. If you are interested in that, you can find quite some resources on the web about how to make your life easier with more generic approaches and mini-frameworks for this problem. I’ve written <a href="http://www.pitorque.de/MisterGoodcat/post/Attribute-based-transient-Tombstoning.aspx">an article on the topic</a> myself which in turn links to additional resources too. Also, even though this is a seemingly straight-forward topic, a lot of not so obvious problems can surface quickly. I’ve talked about the requirements and pitfalls in serializing data for tombstoning in detail in <a href="http://www.pitorque.de/MisterGoodcat/post/Requirements-of-and-pitfalls-in-Windows-Phone-7-serialization.aspx">a separate article</a>. This of course also all applies to games.</p> <p>What we’ve learned here, and especially that last article I’ve just linked to, makes it obvious that tombstoning and properly saving and restoring your data is a topic you should plan ahead thoroughly. In fact, it is one of the crucial points that can make the difference for a great user experience. Even if your users don’t move away from your game deliberately, on the phone there are several unexpected situations that might lead to deactivation and/or tombstoning, for example incoming phone calls. Unfortunately, a lot of the games in the market place today force the player to e.g. restart a level from the beginning after tombstoning. In my eyes this is bad design; what you want to avoid in any case is punish your users for using their phone’s features. Of course it is ok to return to the menu or pause the game on reactivation, and offer an option to continue. But make sure that your game state is persisted and restored as accurately as possible, and users will love the experience.</p> <h3>Summary</h3> <p>I hope this article helped you learn about handling deactivation and tombstoning in XNA, and how to work around the limitations introduced by the built-in features of the Game base class by simply using the PhoneApplicationService in XNA too. We've also seen the new dormant state in Mango, and how easily you can support the new feature of fast application switching in your game.</p> <p>The very small code samples you can download here only demonstrate the behavior of the built-in features of the Game class for both the RTM release and Mango, as well as the behavior of the PhoneApplicationState events that we’ve looked at in the second part of the article.</p> <p><a href="http://www.silverlightshow.net/Storage/Sources/XNA_for_Silverlight_developers_Part11.zip">Download source code</a></p> <h3>About the author</h3> <p>Peter Kuhn aka "Mister Goodcat" has been working in the IT industry for more than ten years. After being a project lead and technical director for several years, developing database and device controlling applications in the field of medical software and bioinformatics as well as for knowledge management solutions, he founded his own business in 2010. Starting in 2001 he jumped onto the .NET wagon very early, and has also used Silverlight for business application development since version 2. Today he uses Silverlight, .NET and ASP.NET as his preferred development platforms and offers training and consulting around these technologies and general software design and development process questions.</p> <p>He created his first game at the age of 11 on the Commodore 64 of his father and has finished several hobby and also commercial game projects since then. His last commercial project was the development of Painkiller:Resurrection, a game published in late 2009, which he supervised and contributed to as technical director in his free time. You can find his tech blog here: <a href="http://www.pitorque.de/MisterGoodcat/">http://www.pitorque.de/MisterGoodcat/</a></p> http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-11-Tombstoning.aspx editorial@silverlightshow.net (Peter Kuhn ) http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-11-Tombstoning.aspx#comments http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-11-Tombstoning.aspx Wed, 25 May 2011 00:00:00 GMT Pencho Popadiyn on N-puzzle WP7 application <div style="padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; width: 200px; float: right; margin-left: 10px; padding-top: 5px;border: #dddddd 1px solid;"> <h3>Don't miss...</h3> <ul style="list-style-type: circle; margin: 0px; padding-left: 20px; font-size: 12px;"> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Windows-Phone-7-Creating-Custom-Keyboard.aspx">Pencho's 'WP7–Creating Custom Keyboard' article</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/C-for-Android-and-iOS-Developers.aspx">WP7 for iPhone and Android Devs series</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Gergely-Orosz-on-Cocktail-Flow-WP7-application.aspx">Cocktail Flow WP7 Application</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/book/Beginning-Windows-Phone-7-Development.aspx">WP7 Development book: </a></li> </ul> <p style="padding-bottom: 5px;">         <a href="http://www.silverlightshow.net/book/Beginning-Windows-Phone-7-Development.aspx"><img alt="Beginning Windows Phone 7 Development" src="http://www.silverlightshow.net/Storage/WP7book.jpg" /></a> </p> <p style="font-size: 12px;">              <a href="http://www.silverlightshow.net/Books.aspx">Show more books...</a></p> </div> <p><em>Next in our WP7 showcase apps comes N-puzzle – a new, free gaming app developed by </em><a href="http://www.completit.com/"><em>CompletIT</em></a><em> and Pencho Popadiyn. Pencho is also a recognized article author at SilverlightShow (</em><a href="http://www.silverlightshow.net/Search.aspx?q=Pencho+Popadiyn&ro=1&tg=true&adv=false&t=1"><em>check his contributions</em></a><em>). </em></p> <p><strong>Q1. Pencho - please introduce yourself briefly (experience, interests, key projects, etc) and tell us more about the application you created - what are the key functionalities, major differences from other similar applications on the market, why do you think people would want to install this app on their phone?</strong></p> <p>A. Hello! I’m Pencho Popadiyn, I live in Sofia, Bulgaria and work as a .NET Developer for CompletIT. I am developing Silverlight/WPF since the very beginning. I enjoy programming WP7 in my free time. In fact this is not my first WP7 game. Last month I released my first app, it was a mahjong based game (search for <em>Shisen-Sho</em> in the marketplace). And I received a lot of positive feedback from the users, and that encouraged me to move on. About the puzzle. Well, it is based on the classic and extremely popular N puzzle game, where you have a frame of numbered square tiles in random order with one tile missing. The object of the puzzle is to place the tiles in order by making sliding moves that use the empty space. The game offers a number of built-in images; also the user can choose a custom image from the phone gallery or even to use the today’s Bing image as a source. If you don’t like images, you can play the classic modes with numbers or letters. You can save a game in progress and complete it later; there are four different board sizes and many other features. Here are the links to the game: </p> <ul> <li><a href="http://www.windowsphoneapplist.com/n-puzzle_game-a16302.html">http://www.windowsphoneapplist.com/n-puzzle_game-a16302.html</a> </li> <li><a href="http://social.zune.net/redirect?type=phoneApp&id=9b99ba3f-2377-e011-9210-002264c2fb72">http://social.zune.net/redirect?type=phoneApp&id=9b99ba3f-2377-e011-9210-002264c2fb72</a> </li> </ul> <p>                       <a href="http://www.silverlightshow.net/Storage/Users/SilverlightShow/_Screenshot1_2.png"><img width="144" height="240" title="Screenshot1" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px solid;" alt="Screenshot1" src="http://www.silverlightshow.net/Storage/Users/SilverlightShow/_Screenshot1_thumb.png" /></a>                      <a href="http://www.silverlightshow.net/Storage/Users/SilverlightShow/_Screenshot2_2.png"><img width="144" height="240" title="Screenshot2" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px solid;" alt="Screenshot2" src="http://www.silverlightshow.net/Storage/Users/SilverlightShow/_Screenshot2_thumb.png" /></a>                      <a href="http://www.silverlightshow.net/Storage/Users/SilverlightShow/_Screenshot3_2.png"><img width="144" height="240" title="Screenshot3" style="padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px solid;" alt="Screenshot3" src="http://www.silverlightshow.net/Storage/Users/SilverlightShow/_Screenshot3_thumb.png" /></a></p> <p><strong>Q2. Why a puzzle? How did you get the idea to create this app?</strong></p> <p>A.Well I developed the same application for Silverlight a long time ago. And now I decided to port it for WP7. By the way, the original Silverlight application is available on SilverlightShow. Check out <a href="http://www.silverlightshow.net/items/Silverlight-n-Puzzle-Game.aspx">here</a>.</p> <p><strong>Q3. Which features were most challenging to develop in this game, and why?</strong></p> <p>A. Maybe the logic for downloading and using the <em>today’s Bing image</em> feature was most interesting. But generally as I said I had a Silverlight version of the puzzle, so it was relatively easy to port the project to WP7. <br /> However, since this is my second game, I am trying to take a more general look at the things. I am trying to extract common functionality and build something like an own framework (for internal usages of course). As I come to my code from time to time I am seeing a lot of repeating code and patterns. Besides the standards utilities and helper classes, I and my colleagues are spending some time in discussing and analyzing how to design some more complex stuff. So we could use it in our future projects.</p> <p><strong>Q4. Could you reveal some tip, some solution you used to work around a specific problem faced during the development of your application?</strong></p> <p>A. Hmm, that’s interesting question. The game is not complex, so I didn’t have any big challenges. However, during the development I hit several problems. Here are some of them. Let’s take a look at the PhotoChooserTask. It took me four hours to understand that navigating to another page directly from the Completed event is wrong. It leads to unbelievable behavior of the application, e.g. events are not firing, other are fired twice and something similar. <br /> I found other issues (bugs)<strong> </strong>with the tombstoning. For example, the Remove method of the application state dictionary is not working correctly, e.g. after the application activation event is raised, the entry is still there. <br /> One of the biggest challenges was related with the ApplicationBar. The ApplicationBarButton and MenuItem does not allow you data binding and attaching commands to the click events. Since I am a MVVM guy, having code for calling view model’s methods in the code-behind of the view is real pain in the a**. <br /> What I could advice the developers who are planning to start developing WP7 applications is to make their first steps with caution. There are a lot of unexpected obstacles. Also having WP7 device for testing is almost mandatory. I could tell you for sure, that testing on the emulator is not the same as testing on a real device (mainly in terms of performance and graphics).</p> <p>                       <a href="http://www.silverlightshow.net/Storage/Users/SilverlightShow/_Screenshot4_2.png"><img width="144" height="240" title="Screenshot4" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px solid;" alt="Screenshot4" src="http://www.silverlightshow.net/Storage/Users/SilverlightShow/_Screenshot4_thumb.png" /></a>                      <a href="http://www.silverlightshow.net/Storage/Users/SilverlightShow/Screenshot5_2.png"><img width="144" height="240" title="Screenshot5" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px solid;" alt="Screenshot5" src="http://www.silverlightshow.net/Storage/Users/SilverlightShow/Screenshot5_thumb.png" /></a>                      <a href="http://www.silverlightshow.net/Storage/Users/SilverlightShow/Screenshot6_2.png"><img width="144" height="240" title="Screenshot6" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px solid;" alt="Screenshot6" src="http://www.silverlightshow.net/Storage/Users/SilverlightShow/Screenshot6_thumb.png" /></a></p> <p><strong>Q5. Which technology did you used for your games, Silverlight or XNA?</strong></p> <p>A.Well, as a developer having experience with Silverlight and WPF, I used Silverlight for developing my first games. But right now I am getting started with XNA and it is not far the moment when I’ll release something written on XNA.</p> <p><strong>Q6. Do you plan to upgrade the game with any new functionality later on?</strong></p> <p>A. Yes I’ll definitely do it; furthermore I am planning to do it in the next 2 weeks’ time frame. I have a lot of ideas in my mind. For example, I am planning to add a <em>video puzzle</em>. So, instead of image, the user will be able to select a video for puzzle source. I think it will be really interesting. Also I’ll add <em>history </em>functionality. But first, next week I’ll release a small update for the <em>Shisen-Sho</em> game.</p> <p>                                     <strong> <iframe width="425" height="349" src="http://www.youtube.com/embed/trL1FZB04a0" frameborder="0"></iframe></strong></p> <p><strong>Q7. How do you see the future of WP7, and applications for WP7?</strong></p> <p>A. “The future is so bright”, as in the song of C-Block. Ok, now seriously, I think WP7 will transform in a great platform not only for games but for business applications too. We have to be just more patient. Remember what was the situation with Silverlight at the very beginning – lack of features and functionality, bugs, etc. Today Silverlight is a complete platform. The same is the situation with Windows Phone 7. It is just at the beginning of its way. Just wait till the end of the year, when the next major players - Nokia and Mango will enter the gameJ. </p> <p><strong>Q8. What other ideas for next WP7 applications do you have?</strong> <br /> A. I haven’t decided yet what will be my next application, whether it will be a game or some application. I have several ideas and I am still discussing them with my colleagues. I am planning to release several apps and games in the next few months. Although they will be relatively simple, I am trying to make each next game much more complex than the previous one, adding more features and logic. I am still not planning to enter deep inside into the muddy water of the game development (for example, to create some complex shooter or action game). I want to build more confidence and experience (to pave the way, so to say)J. I want learn all specifics of the WP7 programming model in great, great details and try different programming approaches first, and then I’ll think for something more complex.</p> <p><strong>Thank you, Pencho! Wish lots of people would enjoy your game!</strong></p> http://www.silverlightshow.net/items/Pencho-Popadiyn-on-N-puzzle-WP7-application.aspx editorial@silverlightshow.net (Silverlight Show ) http://www.silverlightshow.net/items/Pencho-Popadiyn-on-N-puzzle-WP7-application.aspx#comments http://www.silverlightshow.net/items/Pencho-Popadiyn-on-N-puzzle-WP7-application.aspx Mon, 09 May 2011 04:04:00 GMT Simulating rain in Silverlight Part 2 - Optimization <div style="padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; width: 200px; float: right; margin-left: 10px; padding-top: 5px;border: #dddddd 1px solid;"> <h3>Don't miss...</h3> <ul style="list-style-type: circle; margin: 0px; padding-left: 20px; font-size: 12px;"> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/Webinars.aspx">Free SilverlightShow Webinars</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Getting-ready-for-the-exams-Part-1.aspx">Getting ready for Silverlight Exam 70-506 series</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/ClassifiedCabinet-A-Quick-Start.aspx">ClassifiedCabinet: A Quick Start</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/book/Pro-Business-Applications-with-Silverlight-3.aspx">Pro Business Apps with SL4 book: </a></li> </ul> <p style="padding-bottom: 5px;">        <a href="http://www.silverlightshow.net/book/Pro-Business-Applications-with-Silverlight-3.aspx"> <img alt="Pro Business Applications with Silverlight 4 book" src="http://www.silverlightshow.net/Storage/ProSL4.jpg" /></a> </p> <p style="font-size: 12px;">           <a href="http://www.silverlightshow.net/Books.aspx">Show more books</a><img alt="" src="http://www.silverlightshow.net/Storage/arrow-content.jpg" /></p> </div> <strong><em><span style="line-height: 115%; font-family: calibri, sans-serif; font-size: 11pt;">This article is compatible with the latest version of Silverlight.</span></em></strong> <div><strong><em><span style="line-height: 115%; font-family: calibri, sans-serif; font-size: 11pt;"></span></em></strong><br /> <div><span style="line-height: 115%;"> <h2><span><strong>Introduction</strong></span></h2> </span>Following my first article in this series, <a href="http://www.silverlightshow.net/items/Simulating-rain-in-Silverlight.aspx">Simulating rain in Silverlight</a>, I tried to optimize the algorithm that generates the rain. After the big interest several weeks ago, in this article this time I made some major changes in the code that eventually result in smoother, faster and non-memory consuming technique to simulate rain effect in Silverlight application.<o:p _rdEditor_exists="1"></o:p>This new solution of the problem is based on the first one that you may get acquainted with in the first part of this series - <a href="http://www.silverlightshow.net/items/Simulating-rain-in-Silverlight.aspx">Simulating rain in Silverlight</a>.<o:p _rdEditor_exists="1"></o:p>Before I begin with the new approach, here is a demo of the same rain, but much more intense, a flood rain...<o:p _rdEditor_exists="1"></o:p><a href="http://www.silverlightshow.net/Storage/Users/lnikolov/RainEffectPart2.html">View live demo</a> - 1000 drops per second<br /> <p class="MsoNormal"><a href="http://www.silverlightshow.net/Storage/Users/lnikolov/RainEffectPart2.zip">Download source code</a></p> </div> <div> <p><strong></strong></p> <h2></h2> <h2><strong>Changes in a brief</strong></h2> <p>The solution that I present you now will dramatically affect the memory that the rain simulation consumes. One of the major drawbacks in the first attempt was that it eventually turned out that in some moment the Silverlight application tries to use your entire RAM. It may happen after hours pouring, or days, but it will. This increasing usage of memory affected the smoothness and CPU consumption as well. However, the algorithm for generating the drops is pretty well designed and working, but it definitely needs some optimization when it comes to memory usage.</p> <p><strong></strong></p> <h2><strong>The secret key - reusing the drops!</strong></h2> <p>From this perspective, I directed my efforts to exactly this issue. The main difference in this new idea is that I could reuse the drops that have already finished their storyboard. Imagine the following – you have a storyboard that lasts, for example, M milliseconds, C – drops that must be animated per a single interval and N – the interval duration in milliseconds. Then after M milliseconds you can reuse all drops that have finished their pouring. But how many are these drops? Having N milliseconds for an interval and C drops per interval, you have 1000 / N intervals per second and thus (1000 / N) * C drops in a second in total. Multiplying this by M / 1000, you result in the number of drops that will be poured by the end of the first storyboard. This is exactly the number of drops that you need to infinitely simulate the rain. You just need to track the current drop to reuse in a global collection.</p> <p><span style="font-family: consolas; color: #0000ff;"></span></p> <div class="reCodeBlock" style="overflow-y: auto;border: #7f9db9 1px solid;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">private</code> <code style="color: #000000;">List<Drop> sceneDrops;</code></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">private</code> <code style="color: #006699; font-weight: bold;">int</code> <code style="color: #000000;">intervalStartIndex;</code></span></div> </div> <p><span>In the constructor, I initialize the collection and fill it with drops (with no rain effects on them applied – these are added in the algorithm):</span></p> <p><span style="color: #0000ff;"></span></p> <div class="reCodeBlock" style="overflow-y: auto;border: #7f9db9 1px solid;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">int</code> <code style="color: #000000;">dropsCount = Convert.ToInt32(Constants.ANIMATION_DURATION * Math.Ceiling((</code><code style="color: #006699; font-weight: bold;">double</code><code style="color: #000000;">)1000 * (</code><code style="color: #006699; font-weight: bold;">double</code><code style="color: #000000;">)</code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.DropsPerInterval / (</code><code style="color: #006699; font-weight: bold;">double</code><code style="color: #000000;">)</code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.DropsInterval));</code></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.sceneDrops = </code><code style="color: #006699; font-weight: bold;">new</code> <code style="color: #000000;">List<Drop>(dropsCount);</code></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>            </code><span style="margin-left: 36px !important;"> </span></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.intervalStartIndex = 0;</code></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">for</code> <code style="color: #000000;">(</code><code style="color: #006699; font-weight: bold;">int</code> <code style="color: #000000;">i = 0; i < </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.sceneDrops.Capacity; ++i)</code></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important;"><code style="color: #000000;"><span style="font-size: 11px;">{</span></code></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #000000;">Drop drop = </code><code style="color: #006699; font-weight: bold;">new</code> <code style="color: #000000;">Drop(</code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.DropSettings);</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.sceneDrops.Add(drop);</code></span></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important;"><code style="color: #000000;"><span style="font-size: 11px;">}</span></code></span></div> </div> <p><span>The new algorithm looks like this:</span></p> <p><span style="color: #0000ff;"></span></p> <div class="reCodeBlock" style="overflow-y: auto;border: #7f9db9 1px solid;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">if</code> <code style="color: #000000;">(</code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.intervalStartIndex >= </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.sceneDrops.Count)</code></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important;"><code style="color: #000000;"><span style="font-size: 11px;">{</span></code></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.intervalStartIndex = 0;</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important;"><code style="color: #000000;"><span style="font-size: 11px;">}</span></code></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"> </span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #000000;">Random rand = </code><code style="color: #006699; font-weight: bold;">new</code> <code style="color: #000000;">Random();</code></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"> </span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">for</code> <code style="color: #000000;">(</code><code style="color: #006699; font-weight: bold;">int</code> <code style="color: #000000;">currentDrop = </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.intervalStartIndex; currentDrop < </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.intervalStartIndex + </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.DropsPerInterval; ++currentDrop)</code></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important;"><code style="color: #000000;"><span style="font-size: 11px;">{</span></code></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #008200;">// Add the drop to the scene</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #006699; font-weight: bold;">if</code> <code style="color: #000000;">(!</code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.Scene.Children.Contains(</code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.sceneDrops[currentDrop]))</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #000000;">{</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>        </code><span style="margin-left: 24px !important;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.Scene.Children.Add(</code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.sceneDrops[currentDrop]);</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #000000;">}</code></span></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"> </span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #008200;">// Generate the drop depth and scale it appropriately</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #006699; font-weight: bold;">double</code> <code style="color: #000000;">depth = rand.NextDouble();</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.sceneDrops[currentDrop].RenderTransform = </code><code style="color: #006699; font-weight: bold;">new</code> <code style="color: #000000;">ScaleTransform</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #000000;">{</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>        </code><span style="margin-left: 24px !important;"><code style="color: #000000;">ScaleX = depth,</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>        </code><span style="margin-left: 24px !important;"><code style="color: #000000;">ScaleY = depth,</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #000000;">};</code></span></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"> </span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #008200;">// Generate angle for the falling drop</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #006699; font-weight: bold;">double</code> <code style="color: #000000;">angle = rand.NextDouble() * 10;</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.sceneDrops[currentDrop].RenderTransform = </code><code style="color: #006699; font-weight: bold;">new</code> <code style="color: #000000;">RotateTransform</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #000000;">{</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>        </code><span style="margin-left: 24px !important;"><code style="color: #000000;">Angle = angle,</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #000000;">};</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>                </code><span style="margin-left: 48px !important;"> </span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #008200;">// Generate the drop distance from most left border</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.sceneDrops[currentDrop].Margin = </code><code style="color: #006699; font-weight: bold;">new</code> <code style="color: #000000;">Thickness(Convert.ToInt32(rand.NextDouble() * </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.Width), 0, 0, 0);</code></span></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"> </span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #008200;">// Animate the drop falling</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.sceneDrops[currentDrop].Animate();</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important;"><code style="color: #000000;"><span style="font-size: 11px;">}</span></code></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"> </span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.intervalStartIndex += </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.DropsPerInterval;</code></span></div> </div> <p><strong></strong></p> <h2><strong>Using GPU Acceleration</strong></h2> <p>So, that was for the memory optimization. There is one more thing that can be done to improve the smoothness. Caching. To enable composition caching at the plug-in level you must set the value of an <em>EnableGPUAcceleration</em> param element to <em>true</em> as part of the object tag that declares the Silverlight plug-in. </p> <p><span style="color: #0000ff;"></span></p> <div class="reCodeBlock" style="overflow-y: auto;border: #7f9db9 1px solid;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #000000;"><</code><code style="color: #006699; font-weight: bold;">param</code> <code style="color: #808080;">name</code><code style="color: #000000;">=</code><code style="color: blue;">"enableGPUAcceleration"</code> <code style="color: #808080;">value</code><code style="color: #000000;">=</code><code style="color: blue;">"true"</code><code style="color: #000000;">/></code></span></div> </div> <p>This allows you to take advantage of the hardware acceleration in the GPU. All this can yield in significant performance improvements in some specific scenarios. To specify that the drop should be cached and benefit from GPU acceleration, I use a <em>BitmapCache</em> object.</p> <p><span style="color: #a31515;"></span></p> <div class="reCodeBlock" style="overflow-y: auto;border: #7f9db9 1px solid;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #000000;"><</code><code style="color: #006699; font-weight: bold;">Grid.CacheMode</code><code style="color: #000000;">></code></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 11px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #000000;"><</code><code style="color: #006699; font-weight: bold;">BitmapCache</code> <code style="color: #808080;">RenderAtScale</code><code style="color: #000000;">=</code><code style="color: blue;">"4"</code><code style="color: #000000;">/></code></span></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 11px;"><code style="color: #000000;"></</code><code style="color: #006699; font-weight: bold;">Grid.CacheMode</code><code style="color: #000000;">></code></span></div> </div> <p>You can read more about <em>CacheMode</em> in <a href="http://msdn.microsoft.com/en-us/library/system.windows.uielement.cachemode(v=vs.95).aspx">MSDN</a>.</p> <p><strong></strong></p> <h2><strong>Conclusion</strong></h2> <div><strong><br /> </strong></div> <span style="line-height: 115%;">After all above, the first rain simulation I made several weeks ago now looks like a polished one. Thanks to your feedback I was encouraged to spend some time searching for a better approach. As a conclusion, it, again, as it always happens, is obvious and straightforward to realize, just waiting for you to discover it, escaping from the boundaries that you build in front of you...</span><br /> </div> <div><span style="line-height: 115%;"><br /> </span></div> </div> <h3>About the author</h3> <p><img alt="" style="float: left; margin-right: 10px;" src="http://www.silverlightshow.net/Storage/lazar.png" />Lazar Nikolov is a software developer at <a href="http://www.completit.com/" target="_blank">CompletIT</a> - the company behind <a href="http://www.silverlightshow.net/">SilverlightShow.net</a> and leader in <strong>Silverlight</strong> related technologies in Bulgaria. His main interests are focused on Silverlight rich applications and <strong>Windows Phone 7</strong> development.</p> <p>In addition to authoring Silverlight articles (his series on ‘<a href="http://www.silverlightshow.net/items/Simulating-rain-in-Silverlight.aspx">Simulating rain in Silverlight</a>’ became one of the most popular articles on SilverlightShow!), Lazar is involved in the verification of all SilverlightShow articles, making sure that all content is consistent and in-line with the latest Silverlight/WP7 release updates.</p> http://www.silverlightshow.net/items/Simulating-rain-in-Silverlight-Part-2-Optimization.aspx editorial@silverlightshow.net (Lazar Nikolov ) http://www.silverlightshow.net/items/Simulating-rain-in-Silverlight-Part-2-Optimization.aspx#comments http://www.silverlightshow.net/items/Simulating-rain-in-Silverlight-Part-2-Optimization.aspx Thu, 05 May 2011 00:00:00 GMT XNA for Silverlight developers: Part 10 - UI Elements and Menus <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; padding-top: 5px; text-align: center;">Are you interested in <strong>creating games for Windows Phone 7</strong> but don't have XNA experience? <br /> <a href="http://www.silverlightshow.net/video/XNA-for-WP7-Webinar.aspx">Watch the recording</a> of the recent intro webinar delivered by Peter Kuhn '<strong>XNA for Windows Phone 7</strong>'.<a href="https://www1.gotomeeting.com/register/256344145" target="_blank"></a><br /> </div> <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; width: 196px; float: right; height: 401px; margin-left: 10px; margin-right: 5px; padding-top: 5px;"> <h3>Don't miss...</h3> <ul style="list-style-type: circle; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-left: 20px; font-size: 12px;"> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/WP7-What-is-Windows-Phone-7.aspx">What is Windows Phone series</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Exploring-Silverlight-XNA-integration-on-Windows-Phone-7.aspx">XNA article by Levente Mihály</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Windows-Phone-7-Part-1-Getting-Started.aspx">WP7 series by Andrea Boschin</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx">The XNA series is now available as a fully offline resource</a> </li> </ul> <p style="padding-bottom: 5px; text-align: center;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx"><img style="border:0px solid;" alt="XNA for Silverlight Developers Ebook" src="http://www.silverlightshow.net/storage/thumb_xna_ebook_cover_150_0.png" usemap="#rade_img_map_1291385581316" /></a></p> <p style="font-size: 12px;">     <a href="http://www.silverlightshow.net/ebooks.aspx">All SilverlightShow Ebooks</a> <img alt="" src="http://www.silverlightshow.net/Storage/arrow-content.jpg" /></p> </div> <p>This article is part 10 of the series "XNA for Silverlight developers".</p> <p style="text-align: left;"><strong>This article is compatible with Windows Phone "Mango". <em><br /> Latest update: Aug 1, 2011.</em></strong></p> <p>Games inherently have a different UI and user experience concept compared to business applications. Where you scroll through endless lists of items and enter data in greyish forms on one side, you use a game pad or touch input to control a character through a thrilling fantasy world on the other side. In some situations however, you suddenly find yourself to be in need of all these boring controls of normal desktop applications for your game too. Whenever you want to create a menu or request certain data from the user for example, you need the same kind of UI in one way or another: text boxes, combo boxes, buttons. In this article, we'll evaluate the options you have in XNA, compare this to Silverlight, and take a quick look at the future.</p> <h3>The situation with Silverlight</h3> <p>Desktop Silverlight has a strong background in business applications, and its little brother on the phone has inherited a lot of the powerful features to create rich user interfaces. Not only do we have a built-in set of all kinds of controls that offer options for almost all scenarios, things like data binding, styles and templates also make it very easy to create appealing interfaces quickly without giving up a clear separation of data and views. A sophisticated event model helps us interact with these controls and react to user input easily, and additional containers and features allow us to create nice screen layouts and dynamic arrangements within minutes. When you are coming from this background, the situation in XNA will be – gently said – shocking.</p> <h3>What we have in XNA</h3> <p>Simply put, nothing of all this. In previous parts, we have talked about some of the involved differences regarding UI elements and user input in both worlds already. I very much encourage you to read <a href="http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-1-Fundamentals.aspx">Part 1</a> if you haven't done yet. I explain the difference between Silverlight's event-driven programming model and XNA's polling-oriented design in detail there. In <a href="http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-5-Input-touch-gestures.aspx">Part 5</a>, when we were talking about touch input for the first time, I also reviewed Silverlight's concept of controls and contrasted that with XNA's much more low-level approach. I won't go into further details regarding these difference in this article again. </p> <p>So, what <em>do</em> we have in XNA? The good news is that we have all the tools and possibilities to create a similar user experience at hand. The bad news of course is that we have to do all this manually, and depending on what your requirements are, this may result in a lot of effort and code up to a level where it is advisable to look for other options, like third-party libraries.</p> <h3>A look at a working sample</h3> <p>If you recall the sample we used in the last article you may remember that it already had an options menu that allowed the user to change some (bogus) parameters of the game:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Menu_2.jpg"><img style="border:0px solid; background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;" title="Menu" alt="Menu" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Menu_thumb.jpg" width="216" height="404" /></a></p> <p>If you inspect the code for this menu, you will see that it actually tries to modularize the involved parts. There are base classes for the menu screen, a derived class for this options menu (all of which are inherited game screens from the last part), and a separate class for the menu entries on the screen that even works with events when the user selects one. The logic to determine whether a menu entry has been selected by the user or not is contained in the "MenuScreen" class and looks like this:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="rem">// look for any taps that occurred and select any entries that were tapped</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> <span class="kwrd">foreach</span> (GestureSample gesture <span class="kwrd">in</span> input.Gestures)</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> <span class="kwrd">if</span> (gesture.GestureType == GestureType.Tap)</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> <span class="rem">// convert the position to a Point that we can test against a Rectangle</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> Point tapLocation = <span class="kwrd">new</span> Point((<span class="kwrd">int</span>)gesture.Position.X, (<span class="kwrd">int</span>)gesture.Position.Y);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span> <span class="rem">// iterate the entries to see if any were tapped</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span> <span class="kwrd">for</span> (<span class="kwrd">int</span> i = 0; i < menuEntries.Count; i++)</pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> 12:</span> MenuEntry menuEntry = menuEntries[i];</pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> 13:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> 14:</span> <span class="kwrd">if</span> (GetMenuEntryHitBounds(menuEntry).Contains(tapLocation))</pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> 15:</span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> 16:</span> <span class="rem">// select the entry</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum17" class="lnum"> 17:</span> OnSelectEntry(i, PlayerIndex.One);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum18" class="lnum"> 18:</span> }</pre> <!--CRLF--> <pre class="alt"><span id="lnum19" class="lnum"> 19:</span> }</pre> <!--CRLF--> <pre class="alteven"><span id="lnum20" class="lnum"> 20:</span> }</pre> <!--CRLF--> <pre class="alt"><span id="lnum21" class="lnum"> 21:</span> }</pre> <!--CRLF--></div> </div> <p>The approach used here is that the input is handled on the screen level. The screen iterates over all controls and uses the available information about the current user input and control dimensions to decide whether a menu entry is affected by the user input or not. Here is a flow chart of how the logic works:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/____image_8.png"><img style="border:0px solid; background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/____image_thumb_3.png" width="248" height="702" /></a></p> <p>What the sample implementation does when a 'click' is detected is tell the menu entry to raise it's "Selected" event. This may seem a bit convoluted at first, but it actually enables derived classes (like the options menu) to do something meaningful with this event. </p> <p>A different approach would be to not have any logic in the screen class itself, but simply pass through the request to handle user input to each one of the controls on the screen. Then every menu entry could decide itself what to do with the input, which would be more flexible. Not matter what alternative or variation you use, this is basically how you would create the core logic for any custom UI concept: Since there is no built-in concept of UI elements in XNA, let alone a mechanism to inform you a particular one has been selected, it really comes down to creating your own elements, and then simply iterating over all of them to see if one is affected by the current user input.</p> <p>Of course all the menu entries in the example only cycle through a given set of values (including an on/off switch) or implement only a simple increment logic. But based on this rather simple structure you could already build a set of controls that is able to handle a lot of the most common requirements. The "MenuEntry" class is designed very openly for this. By overriding the "Update" and "Draw" methods you are able to change and extend the logic as well as the visual appearance of the entries.</p> <h4>Text input</h4> <p>Things are a bit different for text input you need to request from the user. While it would be possible to design and display a textbox-like control, the problem with this is the input method on the phone device. Apart from few exceptions with hardware keyboards, Windows Phone 7 relies on a SIP (<a href="http://en.wikipedia.org/wiki/Soft_Input_Panel">Soft Input Panel</a>). At the moment, there is no way to bring up the SIP to interact with your game, which means you would have to create your own on-screen keyboard. This is something far from being trivial (think different cultures etc.) and would most likely result in a user experience different from the rest of the phone. It also doesn't scale once additional languages are supported by the system and therefore needs adjustments once updates for this are rolled out by Microsoft.</p> <p>The solution to this once again is the <a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.gamerservices.guide.aspx">Guide class</a>. It has a method "<a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.gamerservices.guide.beginshowkeyboardinput.aspx">BeginShowKeyboardInput</a>" that brings up a simple data input screen with a customizable title, description and a text box. It handles all the SIP logic for you, and once the user has finished entering their data, a callback will be invoked to notify you about the result, which you then can use for whatever you want.</p> <p>This limitation has been heavily discussed in the community. It means that you cannot have a consistent design for the data input screen and your game (you cannot change its appearance), and you also don't have any direct control over the functionality (e.g. you cannot restrict input to certain characters or do other sorts of validation on that screen etc.). We'll see how this will be changed in the future below.</p> <p>As an example I've extended the sample from the last article and added text input to it. For this, I simply switch to the high scores list when the user taps the game play screen to simulate a successfully finished game. In this case (coming from the game play instead of the main menu) the user is prompted for their name to create an entry in the high scores list:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="rem">// is the user supposed to enter their name?</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> <span class="kwrd">if</span> (EnterHighscore)</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> <span class="rem">// start showing the input screen</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> Guide.BeginShowKeyboardInput(ControllingPlayer.Value,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> <span class="str">"High Score!"</span>,</pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> <span class="str">"Please enter your name"</span>,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> <span class="kwrd">string</span>.Empty,</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span> KeyboardInputFinished,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span> <span class="kwrd">null</span>,</pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span> <span class="kwrd">false</span>);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> 12:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> 13:</span> <span class="rem">// set the flags we need</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> 14:</span> EnterHighscore = <span class="kwrd">false</span>;</pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> 15:</span> _userInputInProgress = <span class="kwrd">true</span>;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> 16:</span> <span class="kwrd">return</span>;</pre> <!--CRLF--> <pre class="alt"><span id="lnum17" class="lnum"> 17:</span> }</pre> <!--CRLF--></div> </div> <p>The most important argument of the method is the "KeyboardInputFinished" callback, a method which is invoked automatically when the user closes the input screen. The second flag is used to pause our custom update and draw logic as long as the input screen is visible. This is necessary because the keyboard input screen works asynchronously, which means that the game continues to run in the background. The callback implementation looks like this:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="kwrd">private</span> <span class="kwrd">void</span> KeyboardInputFinished(IAsyncResult ar)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> <span class="rem">// reset flag</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> _userInputInProgress = <span class="kwrd">false</span>;</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> <span class="rem">// get the result</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> <span class="kwrd">string</span> result = Guide.EndShowKeyboardInput(ar);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> <span class="kwrd">if</span> (!<span class="kwrd">string</span>.IsNullOrEmpty(result))</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span> <span class="rem">// add a bogus highscore entry</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span> <span class="kwrd">string</span> entry = <span class="kwrd">string</span>.Format(<span class="str">"11. {0} - {1}"</span>, result, 10);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> 12:</span> _highScores.Add(entry);</pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> 13:</span> }</pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> 14:</span> }</pre> <!--CRLF--></div> </div> <p>As you can see, by invoking the "<a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.gamerservices.guide.endshowkeyboardinput.aspx">EndShowKeyboardInput</a>" method on the Guide class you will receive the string the user has input. In our case this string is only used to create a bogus additional high score entry, but you should get the idea how you can use this mechanism to query input from the user.</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Highscores_2.jpg"><img style="border:0px solid; background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;" title="Highscores" alt="Highscores" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Highscores_thumb.jpg" width="216" height="403" /></a></p> <h3>Further resources</h3> <p>Apart from the game state sample that we've used here, Microsoft has also published another example in the App Hub Education section that covers UI elements and menus in XNA. It is named "User Interface Controls" and can be found <a href="http://create.msdn.com/en-US/education/catalog/sample/ui_controls">here</a>. This example contains a set of base classes for the control itself as well as a panel. Derived from this it has an image control, a page flip control to flick through items and scrolling controls to display lists of items. Even if you don't use the code as-is, it should also be possible to get some ideas from the samples to develop your own controls.</p> <p>In the past there have also been some attempts to create UI frameworks for XNA, some of which looked very promising. Unfortunately a lot of them were discontinued after a while, a few even disappeared completely. One that is still actively developed (and brings a lot more than just UI elements) is the Nuclex Framework which can be found <a href="http://nuclexframework.codeplex.com/wikipage?title=Nuclex.UserInterface&referringTitle=Documentation">on Codeplex</a>.</p> <p>Another interesting framework is <a href="http://red-badger.com/Blog/post/Introducing-XPF-e28093-A-Layout-Framework-for-XNA.aspx">XPF</a>, which has some strong layout features. It has not yet reached production level quality, but it has nightly debug builds you can use for testing. Apparently <a href="http://red-badger.com/Blog/post/XPF-Roadmap-and-Licensing.aspx">the licensing</a> will include a free version for non-commercial and open source development.</p> <h3>Integrating XNA and Silverlight</h3> <p>An obvious solution to all of XNA's lacking UI features on Windows Phone 7 would be to allow mixing Silverlight with XNA graphics in the same application, so a game can make use of Silverlight's powerful features too. In fact, some people started to investigate this very early on, but of course application certification requirements of the RTM version explicitly forbid doing this, so any success with it was futile. </p> <p>Things very much change with Windows Phone "Mango", where you have the chance and are allowed to do exactly that. The general application structure is significantly different when you make use of this, because then Silverlight needs to take the leading role, and using XNA is achieved by switching to it from Silverlight using a feature named "sharing mode". I discuss and demonstrate all the technical details in <a href="http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-12-Mango-1.aspx">part 12</a> of this series, which of course also comes with full source code for you to explore this.</p> <p>The important thing is that you can decide what parts of your game you want to implement with Silverlight, and what parts should be handled by XNA. You can use all of Silverlight's UI features to create and design your menus and other data-centric screens like highscore lists, and then switch to XNA for the actual gameplay and game content rendering. The following diagram shows a logical screen structure for such a setup, with each rectangle representing a phone application page:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/__________image_2.png"><img style="border:0px; background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/___________image_thumb.png" width="419" height="290" /></a></p> <p>But the good news doesn't stop here. You are even able to mix Silverlight and XNA content on the <em>same</em> page if you want. This allows you to e.g. use the more sophisticated Silverlight text rendering directly on your XNA gameplay screen. HUD overlays or other things can be handled with default Silverlight UI elements more easily, and then used in your XNA rendering process with the help of the new <a href="http://msdn.microsoft.com/en-us/library/hh221570(v=XNAGameStudio.41).aspx">UIElementRenderer class</a>. </p> <p>The beauty of this approach is that Silverlight elements are not forced into a passive role by this. User input can be routed to these elements, and with a little bit of work you can even use elements like text boxes on XNA-controlled pages, and have the user enter data using the SIP. And of course all the dynamic goodies of Silverlight, for example it's excellent animation features, can be used in this scenario too.</p> <p>Again, <a href="http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-12-Mango-1.aspx">part 12</a> of this series shows how to do most of this by demonstrating the UIElementRenderer in combination with Silverlight animations on an application page that is rendered by XNA.</p> <h3>Summary</h3> <p>In this article we once again learned that the more low-level nature of XNA (which has a lot of advantages in game programming) comes at the cost of a lot less comfort. Especially when it comes to creating user interfaces like menus things can easily become complex and result in a lot of work, to a point where it absolutely makes more sense to use some already available commercial or free third-party libraries. However, we've also seen that the first major update to the platform ("Mango") brings exciting news about the future of XNA and Silverlight integration, which will make a lot of these things much easier. If you are interested in the updated sample code that contains the user text input code, you can get it here:</p> <p><a href="http://www.silverlightshow.net/Storage/Sources/XNA_for_Silverlight_developers_Part10.zip" target="_blank">Download source code</a></p> <p>As always, feel free to add your comments below or provide feedback to me directly.</p> <h3>About the author</h3> <p>Peter Kuhn aka "Mister Goodcat" has been working in the IT industry for more than ten years. After being a project lead and technical director for several years, developing database and device controlling applications in the field of medical software and bioinformatics as well as for knowledge management solutions, he founded his own business in 2010. Starting in 2001 he jumped onto the .NET wagon very early, and has also used Silverlight for business application development since version 2. Today he uses Silverlight, .NET and ASP.NET as his preferred development platforms and offers training and consulting around these technologies and general software design and development process questions.</p> <p>He created his first game at the age of 11 on the Commodore 64 of his father and has finished several hobby and also commercial game projects since then. His last commercial project was the development of Painkiller:Resurrection, a game published in late 2009, which he supervised and contributed to as technical director in his free time. You can find his tech blog here: <a href="http://www.pitorque.de/MisterGoodcat/">http://www.pitorque.de/MisterGoodcat/</a></p> http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-10-UI-Elements-and-Menus.aspx editorial@silverlightshow.net (Peter Kuhn ) http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-10-UI-Elements-and-Menus.aspx#comments http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-10-UI-Elements-and-Menus.aspx Wed, 27 Apr 2011 00:00:00 GMT XNA for Silverlight developers: Part 9 - Navigation and structure <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; padding-top: 5px; text-align: center;">Are you interested in <strong>creating games for Windows Phone 7</strong> but don't have XNA experience? <br /> <a href="http://www.silverlightshow.net/video/XNA-for-WP7-Webinar.aspx">Watch the recording</a> of the recent intro webinar delivered by Peter Kuhn '<strong>XNA for Windows Phone 7</strong>'.<a href="https://www1.gotomeeting.com/register/256344145" target="_blank"></a><br /> </div> <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; width: 196px; float: right; height: 401px; margin-left: 10px; margin-right: 5px; padding-top: 5px;"> <h3>Don't miss...</h3> <ul style="list-style-type: circle; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-left: 20px; font-size: 12px;"> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/WP7-What-is-Windows-Phone-7.aspx">What is Windows Phone series</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Exploring-Silverlight-XNA-integration-on-Windows-Phone-7.aspx">XNA article by Levente Mihály</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Windows-Phone-7-Part-1-Getting-Started.aspx">WP7 series by Andrea Boschin</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx">The XNA series is now available as a fully offline resource</a> </li> </ul> <p style="padding-bottom: 5px; text-align: center;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx"><img style="border:0px solid;" alt="XNA for Silverlight Developers Ebook" src="http://www.silverlightshow.net/storage/thumb_xna_ebook_cover_150_0.png" usemap="#rade_img_map_1291385581316" /></a></p> <p style="font-size: 12px;">     <a href="http://www.silverlightshow.net/ebooks.aspx">All SilverlightShow Ebooks</a> <img alt="" src="http://www.silverlightshow.net/Storage/arrow-content.jpg" /></p> </div> <p>This article is part 9 of the series "XNA for Silverlight developers".</p> <p style="text-align: left;"><strong>This article is compatible with Windows Phone "Mango". <em><br /> Latest update: Aug 1, 2011.</em></strong></p> <p>Until now, we have looked at individual features of game programming on the phone in an isolated manner. We learned about graphics, animations, input, audio and others, and for each of these samples, we created new empty game projects and put the logic in the main game screen and few helper classes. In a real world scenario, your game will combine various different aspects and features, and you naturally would want to structure your game a lot more. Once again some of these details are also subject to restrictions given by the certification requirements. In this article, we will take a look at the concept of "places" and game screens that can be used to create a navigational structure for your game. As always you can download the source code at the end of the article.</p> <h3>Certification requirements</h3> <p>In the official guidelines and certification documents from Microsoft games are treated separately in a lot of ways, and they in fact are at liberty to do things normal applications are not allowed or able to do. One particular noteworthy statement can be found in the <a href="http://go.microsoft.com/fwlink/?LinkID=183218" target="_blank">UI Design and Interaction Guide</a> in the section about game UI design:</p> <blockquote> <p><em>For full-screen games, developers are free to implement whatever in-game UI elements they see fit.</em></p> </blockquote> <p>This pretty much gives you the freedom to create any UI concept you want for your game as long as it is not an integrated part of a normal application (i.e. not full-screen). If you look through some of the games available in the market place today you can see that developers actively make use of this. Aside from "classic" menu systems that resemble normal Silverlight application design you will notice that there are quite a few that go new ways in terms of user interaction.</p> <p>The <a href="http://go.microsoft.com/fwlink/?LinkID=183220" target="_blank">Application Certification Requirements</a> on the other hand make some clear statements on games though. In particular, section 5, which describes the actual requirements, is prefaced with the following note:</p> <blockquote> <p><em>These requirements apply equally to an application that implements game functionality, <br /> commonly referred to as a game.</em></p> </blockquote> <p>The decisive part for navigational concepts is located in section 5.2.4 which describes the use of the back button. So, even though your game is free in its UI design and pretty much has to implement navigation manually, you have to follow basic requirements when it comes to navigation, like that pressing the back button on the first screen must exit the game. In addition, there is a requirement specific to games in that section:</p> <blockquote> <p><em>d.  For games, when the Back button is pressed during gameplay, the game can choose to present a pause context menu or dialog or navigate the user to the prior menu screen. Pressing the Back button again while in a paused context menu or dialog closes the menu or dialog.</em>  </p> </blockquote> <p>This is an interesting exception to the normal rules for applications: instead of navigating back to the previous page, you are actually allowed to open a new dialog when the user presses the back button during game play. </p> <h3>The concept of places</h3> <p>Even though XNA games do not integrate with the built-in concept of pages and the back stack of Windows Phone 7 applications, and you have to create your navigation concept manually, it's well worth taking a look at how this is normally done in Silverlight. After all you will run into many similar questions as with normal apps, and you likely want to offer your users an experience that is similar to what they find on the rest of the phone. Peter Torr has posted an interesting article on something he calls "places" within an application. You can find it <a href="http://blogs.msdn.com/b/ptorr/archive/2010/08/28/introducing-the-concept-of-places.aspx" target="_blank">here</a>, and if you don't know it you should definitely read it. It deals with some common problems and solutions around the navigation concept on Windows Phone 7. While the default stack behavior of the navigation works well in a lot of situations, there are some situations where this inevitably causes problems. Many of these points also apply to games. Let me give you some examples:</p> <ul> <li>You don't want to take the user all the way back to your game's splash screen when they use the back button. The main game screen or menu should be the starting point of your game, and pressing back from there should exit the game, even if you initially showed a splash screen before the main menu on startup. </li> <li>You don't want certain dialogs to appear again if the user navigates back. For example, imagine you prompt the user to enter a name for a high score list. If they decide to navigate back from the high score list later, you want to skip over that dialog. </li> <li>If your game is segmented into levels, it's likely that once the user has finished one, you prompt them to start the next one or show some intermediate/summary information in between levels. If the user navigates back from the main game screen, you however want return to the menu of course, and not back to those intermediate level transition screens. </li> </ul> <p>So it's obvious you need to think about the navigation flow through your game and what screens you want to make distinct "places". Peter's above linked post assists and tries to give you some clues how to make this task easier, for example by asking questions like: "<em>Would the user explicitly want to visit this screen? Would the user remember they were on this screen, and want to return to it?</em>" This is the conceptional work you have to do before you actually start implementing your game's architecture.</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/______image_4.png"><img style="border:0px solid; background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_______image_thumb_1.png" width="578" height="361" /></a></p> <p>This is a sample of what paths a user could take through the different screens of a game. As you can see there are a lot of situations where a stack concept works very well (the whole options section, for example). In other places sub-elements need to be left out of the stack (individual levels of the gameplay). And then there are situations where you need to find a way to break out of the stack concept completely (splash screen, from a finished game to the high scores and back to the main menu).</p> <h3>Game screens and a Screen Manager</h3> <p>Individual game screens and having some sort of manager that controls the currently active screens is something common in games, and I have used it for many years. It was in fact something I already described in my first game programming tutorial ten years ago. For Windows Phone 7 Microsoft provides its own sample for that, and it's really worth investigating because it's well thought-out and a good start to build upon. That sample is named "Game State Management" and can be found in the App Hub:</p> <p><a href="http://create.msdn.com/en-US/education/catalog/sample/game_state_management">http://create.msdn.com/en-US/education/catalog/sample/game_state_management</a></p> <p>When you download and simply run that example you can see that it has most of the above already covered: you see a main menu with some entries that take you to an options screen or to the actual game. The screens use nice transition effects like fade in/out as well as animations. There's an intermediate loading screen for long-running actions which of course is skipped when you navigate backwards, and the back button is fully functional. Let's dissect this sample to see how it works.</p> <h4></h4> <h4>The Game Screens</h4> <p>All screens in the game derive from a base class "GameScreen" that handles most of the work required for the transitions, and it provides a bunch of virtual methods you can override to plug-in your own logic for loading and unloading content, processing input as well as update and draw your content. There are additional methods for topics we haven't covered yet, like serializing and deserialization, and some more convenient methods and properties. All other game screens in the sample make use of this base class and override the default behavior and properties to create their own functionality:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_5.png"><img style="background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;border-width: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/______image_thumb.png" width="520" height="243" /></a></p> <p>The interesting part is that this small example already shows the flexibility of this system. For example, the "BackgroundScreen" is a very static screen that only shows the background image for the menus. It does not participate in transitions at all, thus creating a consistent look between the different menus. The user doesn't really perceive the menu screens as isolated parts because the background acts as a bridging element for all of them. On the other hand, the menus show that this concept includes the ability to have multiple screens visible at once; they are only put on top of the background screen, and together they form the final user experience.</p> <p>The "LoadingScreen" also is a special screen in that its only purpose is to handle the transition between other screens while displaying a notification to the user. It has some built-in logic to wait for all the current screens to transition off, tell the screen manager to load the follow-up screens and eventually remove itself. In the sample this is used to load the "GameplayScreen", where the actual game itself "happens". This of course is the most important screen of them all, because all your game's logic will be implemented and/or used here. This is the place where we would add what we have learned in all the previous parts of this series so far.</p> <p>Finally there is the "MessageBoxScreen", which isn't even used in the sample (we'll add it in a minute). It demonstrates how to use the concept of having a "Popup" in the screen management. This is another feature that allows you to build additional overlay screens like notifications or to ask the user for confirmation.</p> <h4>The Screen Manager</h4> <p>The "ScreenManager" class in the project is the workhorse that does everything necessary to maintain the current list of screens, handling user input and similar things correctly. It also offers some convenient properties, like global font and sprite batch objects. Since each screen has a reference to the screen manager, they don't have to bother creating their own fonts for example (which of course they still could). In addition to the properties, it has public methods to add and remove screens which are used by the individual screens to initiate the transitions to different parts of the game.</p> <p><em>Note: This design decision may seem odd to some at first, because it means the game screens know each other, and control about the flow through the game is distributed across all involved screens. However, it makes a lot of the other available features much easier. I've seen more generic implementations of this concept, where screens don't know about each other and control of transitions is handled in a central place; what sounds like a good idea at first will quickly result in unnecessary difficulties for certain features and over-complicate things in a lot of ways, which especially on the limited phone platform is very undesirable. The number of screens for a game usually won't grow sky high, and normally a "natural" grouping of screens will evolve automatically too (there's no point in jumping from a network configuration screen to the high scores or the gameplay directly) – which will result in a limited set of transition points and keep the structure maintainable.</em></p> <p>The logic of the main menu screen is an example of various possible options to control the game flow:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_7.png"><img style="background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;border-width: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/______image_thumb_2.png" width="323" height="475" /></a></p> <p>If the back button is pressed, the game is simply exited (contrary to the situation in Silverlight, it's possible to explicitly exit an XNA game). If the user wants to play a new game, the main menu screen makes use of the loading screen to transition to the gameplay. This is necessary because loading all the game content may take a while and a notification should be presented to the user (the sample simulates this by letting the current thread sleep for a while). Behind the scenes, the loading screen uses the screen manager for the necessary transitions. If the user wants to look at the game's options however, the main menu uses the screen manager directly to transition to the next screen:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="kwrd">void</span> OptionsMenuEntrySelected(<span class="kwrd">object</span> sender, PlayerIndexEventArgs e)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> ScreenManager.AddScreen(<span class="kwrd">new</span> OptionsMenuScreen(), e.PlayerIndex);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> }</pre> <!--CRLF--></div> </div> <p>The core of the screen manager is the update mechanism. A list of all currently active screens is maintained and processed in reverse order, which means the screen on top is handled first. The screen manager ensures that only the top most screen gets a chance to process user input, and that all screens further down the stack are notified they are covered by other screens, so they can act accordingly (like pause whatever is happening, for example). The draw mechanism on the other hand is pretty simple. The screen manager only iterates over the current screens and in turn calls their draw methods. </p> <p>The last detail is how the screen manager is integrated into the game. When you look at the game class you will see that it contains next to no logic. In fact it doesn't even have an own update method anymore. The crucial part happens in the constructor, where the screen manager is instantiated and added to the game components:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="rem">// Create the screen manager component.</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> screenManager = <span class="kwrd">new</span> ScreenManager(<span class="kwrd">this</span>);</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> Components.Add(screenManager);</pre> <!--CRLF--></div> </div> <p>This is something we haven't used until now. <a href="http://msdn.microsoft.com/en-us/library/bb196990.aspx" target="_blank">Game components</a> are a way of modularization. You can create game components by deriving from the base classes provided by XNA, and then adding the components to your game class as shown above. All game components registered like this will then be maintained by the game and have their initialize, update and draw (if applicable) methods called automatically. This is a way of keeping your main game code lean and the overall project design better structured. When you look at the screen manager class in more detail you will realize that it indeed derives from the <a href="http://msdn.microsoft.com/en-us/library/bb196397.aspx" target="_blank">DrawableGameComponent class</a> to enable this feature.</p> <h3>Sample</h3> <p>To get more accustomed to the principle and structure of the game state management example, and to show you how you can use and extend it for your own games, we're going to add a new screen now: a high scores list. The first thing to do is add a new class to the Screens folder and make it derive from the "GameScreen" base class. If you use the "override" keyword you can take a look at your options:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_11.png"><img style="background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;border-width: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/___image_thumb_4.png" width="579" height="226" /></a></p> <p>What we are going to do is create some fake high score data in the "LoadContent" method. In a real game this is where you would hit a web service instead, for example. The "Update" method does the layout of the items, and the "Draw" override will draw the high scores list on the screen. Since none of these steps contain anything new to us, I don't show the code here. </p> <p>To integrate the new screen with the game, we extend the "MainMenuScreen" by adding another menu entry. We haven't talked about menu systems yet (that will be part of an upcoming article), so I won't go into details here. If you look at how the other menu items are created it's easy to figure out how to set up a new one though:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="rem">// in the "MainMenuScreen" constructor</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> <span class="rem">// add a new entry for the high scores</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> MenuEntry highscoresMenuEntry = <span class="kwrd">new</span> MenuEntry(<span class="str">"High Scores"</span>);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> highscoresMenuEntry.Selected += HighscoresMenuEntry_Selected;</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> MenuEntries.Add(highscoresMenuEntry);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> ...</pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span> <span class="kwrd">void</span> HighscoresMenuEntry_Selected(<span class="kwrd">object</span> sender, PlayerIndexEventArgs e)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span> ScreenManager.AddScreen(<span class="kwrd">new</span> HighscoresScreen(), e.PlayerIndex);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> 12:</span> }</pre> <!--CRLF--></div> </div> <p>When the new entry is selected, we use the screen manager to add a new instance of the high scores screen. In the following, we'll look at two more interesting aspects with game screens.</p> <h4>Input and a Message Box</h4> <p>To deal with user input, you can override the "HandleInput" method. The argument to that method is an "InputState" object that is a convenient wrapper of the sample project around the currently pressed keys and current gestures. One thing you need to do is handle the back key. When the user presses this key, we simply exit the high scores screen. If the user however taps anywhere on the screen, we want to show a message (to put the included MessageBox screen to some use). </p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">void</span> HandleInput(InputState input)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> <span class="kwrd">base</span>.HandleInput(input);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> <span class="rem">// if the user presses the back button, we exit from the high scores</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> <span class="rem">// this will re-activate whatever screen was active before (=> main menu)</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> PlayerIndex player;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> <span class="kwrd">if</span> (input.IsNewButtonPress(Buttons.Back, ControllingPlayer, <span class="kwrd">out</span> player))</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span> ExitScreen();</pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span> }</pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> 12:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> 13:</span> <span class="rem">// look for any taps that occurred and show a message box as response</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> 14:</span> <span class="kwrd">foreach</span> (GestureSample gesture <span class="kwrd">in</span> input.Gestures)</pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> 15:</span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> 16:</span> <span class="kwrd">if</span> (gesture.GestureType == GestureType.Tap)</pre> <!--CRLF--> <pre class="alt"><span id="lnum17" class="lnum"> 17:</span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum18" class="lnum"> 18:</span> <span class="rem">// a very simple message box</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum19" class="lnum"> 19:</span> var messageBox = <span class="kwrd">new</span> MessageBoxScreen(<span class="str">"A message!"</span>, <span class="kwrd">false</span>);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum20" class="lnum"> 20:</span> ScreenManager.AddScreen(messageBox, ControllingPlayer);</pre> <!--CRLF--> <pre class="alt"><span id="lnum21" class="lnum"> 21:</span> }</pre> <!--CRLF--> <pre class="alteven"><span id="lnum22" class="lnum"> 22:</span> }</pre> <!--CRLF--> <pre class="alt"><span id="lnum23" class="lnum"> 23:</span> }</pre> <!--CRLF--></div> </div> <p>This code snippet demonstrates how to use the screen manager to add a new screen to the stack. It also shows that we don't need to bother what happens after we exit the current screen. The screen manager will handle this situation for us and transition the underlying screen back in (the main menu). </p> <p>The final step to make this work is to define that we are interested in processing tap gestures. This can be controlled for each screen separately by setting a corresponding property in its constructor (again the screen manager will do the rest for you):</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="kwrd">public</span> HighscoresScreen()</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> <span class="rem">// we are interested in receiving tap gestures</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> EnabledGestures = GestureType.Tap;</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> }</pre> <!--CRLF--></div> </div> <p>When you run the sample you can see that pressing the back key returns to the main menu, and tapping the screen opens a message box.</p> <h4>Transitions</h4> <p>At the moment the main menu uses nice transitions when it becomes active or inactive, but the high scores just suddenly appear and disappear. Especially when the screen appears it's overlapping with the fading content of the main menu which doesn't look nice. Let's see how we can make use of the built-in transition features to improve this.</p> <p>First of all, similar to enabling input gestures, you can tell the screen manager what duration you want for the screen's transitions by setting some properties in the screen constructor:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> TransitionOnTime = TimeSpan.FromSeconds(0.5);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> TransitionOffTime = TimeSpan.FromSeconds(0.5);</pre> <!--CRLF--></div> </div> <p>With this in place, you can then make use of base class properties like the "ScreenState" and "TransitionPosition". With the first one you can do a basic check to see if the screen is currently transitioning (on or off), and the second value ranges from 0.0f to 1.0f to describe how far you're into the transition, with 0.0f describing an active screen and 1.0f being fully transitioned off. You can make use of this value to calculate an animation offset or similar, for example. In addition, "TransitionAlpha" can be used to create a (linear) alpha fade effect based on the current transition position.</p> <p>In our sample, we calculate the offset from the top of the screen depending on the transition position in the "Update" method of the high scores screen. In addition, in the "Draw" method we control the alpha value of the drawn text by the transition alpha value.</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="rem">// in the Update method:</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> <span class="rem">// top offset</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> <span class="kwrd">float</span> y = 100.0f;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> <span class="rem">// are we transitioning on/off?</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> <span class="kwrd">if</span> (ScreenState == ScreenState.TransitionOn || ScreenState == ScreenState.TransitionOff)</pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> var screenHeight = ScreenManager.Game.GraphicsDevice.Viewport.Height;</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span> var distance = screenHeight - y;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span> <span class="rem">// calculate a new offset depending on the transition value</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> 12:</span> y += distance * TransitionPosition;</pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> 13:</span> }</pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> 14:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> 15:</span> ...</pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> 16:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum17" class="lnum"> 17:</span> <span class="rem">// in the Draw method:</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum18" class="lnum"> 18:</span> <span class="rem">// determine color</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum19" class="lnum"> 19:</span> Color color = <span class="kwrd">new</span> Color(255, 255, 255) * TransitionAlpha;</pre> <!--CRLF--></div> </div> <p>The result is that the high scores list now slides in and out from and to the bottom, and at the same time the transparency of the high score entries is varied.</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/high-scores_2.jpg"><img style="background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;border-width: 0px;" title="high-scores" alt="high-scores" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/high-scores_thumb.jpg" width="144" height="240" /></a></p> <h3>Summary</h3> <p>By structuring your game using a concept of "places" and screens, you achieve a lot in terms of both user experience and maintainability of your project when it grows. With the Game State Management sample Microsoft already provides a pretty sophisticated mini-framework you can use to built upon or at least to get some ideas for your own implementation. With little work you can extend these classes and – by moving them to a class library – even easily reuse them for future projects. Download the source code below to get the additions we've seen in the last parts of the article. If you have any questions or feedback, please feel free to post your comments or contact me directly.</p> <p><a href="http://www.silverlightshow.net/Storage/Sources/XNA_for_Silverlight_developers_Part9.zip" target="_blank">Download Source Code</a></p> <h3>About the author</h3> <p>Peter Kuhn aka "Mister Goodcat" has been working in the IT industry for more than ten years. After being a project lead and technical director for several years, developing database and device controlling applications in the field of medical software and bioinformatics as well as for knowledge management solutions, he founded his own business in 2010. Starting in 2001 he jumped onto the .NET wagon very early, and has also used Silverlight for business application development since version 2. Today he uses Silverlight, .NET and ASP.NET as his preferred development platforms and offers training and consulting around these technologies and general software design and development process questions.</p> <p>He created his first game at the age of 11 on the Commodore 64 of his father and has finished several hobby and also commercial game projects since then. His last commercial project was the development of Painkiller:Resurrection, a game published in late 2009, which he supervised and contributed to as technical director in his free time. You can find his tech blog here: <a href="http://www.pitorque.de/MisterGoodcat/">http://www.pitorque.de/MisterGoodcat/</a></p> http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-9-Navigation-and-structure.aspx editorial@silverlightshow.net (Peter Kuhn ) http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-9-Navigation-and-structure.aspx#comments http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-9-Navigation-and-structure.aspx Mon, 11 Apr 2011 00:00:00 GMT XNA for Silverlight developers: Part 8 - Music and sound effects <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; padding-top: 5px; text-align: center;">Are you interested in <strong>creating games for Windows Phone 7</strong> but don't have XNA experience? <br /> <a href="http://www.silverlightshow.net/video/XNA-for-WP7-Webinar.aspx">Watch the recording</a> of the recent intro webinar delivered by Peter Kuhn '<strong>XNA for Windows Phone 7</strong>'.<a href="https://www1.gotomeeting.com/register/256344145" target="_blank"></a><br /> </div> <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; width: 196px; float: right; height: 401px; margin-left: 10px; margin-right: 5px; padding-top: 5px;"> <h3>Don't miss...</h3> <ul style="list-style-type: circle; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-left: 20px; font-size: 12px;"> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/WP7-What-is-Windows-Phone-7.aspx">What is Windows Phone series</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Exploring-Silverlight-XNA-integration-on-Windows-Phone-7.aspx">XNA article by Levente Mihály</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Windows-Phone-7-Part-1-Getting-Started.aspx">WP7 series by Andrea Boschin</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx">The XNA series is now available as a fully offline resource</a> </li> </ul> <p style="padding-bottom: 5px; text-align: center;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx"><img style="border:0px solid;" alt="XNA for Silverlight Developers Ebook" src="http://www.silverlightshow.net/storage/thumb_xna_ebook_cover_150_0.png" usemap="#rade_img_map_1291385581316" /></a></p> <p style="font-size: 12px;">     <a href="http://www.silverlightshow.net/ebooks.aspx">All SilverlightShow Ebooks</a> <img alt="" src="http://www.silverlightshow.net/Storage/arrow-content.jpg" /></p> </div> <p>This article is part 8 of the series "XNA for Silverlight developers".</p> <p style="text-align: left;"><strong>This article is compatible with Windows Phone "Mango". <em><br /> Latest update: Aug 1, 2011.</em></strong></p> <p>What would a game be without sound effects and music? Naturally Windows Phone 7 offers enough possibilities to add a rich acoustic experience to your apps and games. However, due to the nature of the device, there are some things to keep in mind. Even with the lack of real multi-tasking, you don't have the whole platform at your disposal exclusively. The user may already listen to music from the media library when they start your game, for example. In addition, a mobile device may also be used in an environment where noise is not welcome. These are things you have to consider when you add audio to your game. Some of these aspects are even regulated in the certification requirements for Windows Phone 7 applications and are a potential source for certification failure if implemented incorrectly. In this article, we'll take a look at how audio can be integrated in your games and what the implications are for playing sound and music in Silverlight and XNA. As always, the source code is available at the end of the article.</p> <h3>Silverlight</h3> <p>In Silverlight, there is a dedicated element for audio and video playback: the <a href="http://msdn.microsoft.com/en-us/library/system.windows.controls.mediaelement(v=vs.95).aspx" target="_blank">MediaElement</a>. This component easily enables playback of audio in your Silverlight application. Simply add an instance of it to your page, point it to the source file (needs to be embedded as "Resource") or an Uri, and set some basic values:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <MediaElement x:Name=<span class="str">"Media"</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> Source=<span class="str">"Song.mp3"</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> Volume=<span class="str">"0.9"</span> </pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> AutoPlay=<span class="str">"True"</span> /></pre> <!--CRLF--></div> </div> <p>Of course you can control the playback through various methods like Play, Pause and Stop, and properties like Position allow to implement seeking and similar features. In addition, the media element has events to notify you about e.g. when the media has finished playing. All in all it's a quick way to add media to your application or Silverlight game.</p> <p>However, one huge limitation of the media element is that only one is supported at a time. It is not possible to play multiple media files simultaneously, and even having multiple elements that don't play at the same time on the same page is not supported. To overcome this limitation, you can use the XNA libraries for audio playback. XNA's audio features are one of those parts which are allowed and safe to use from Silverlight. In addition this approach opens the door to a whole set of features and parameters that aren't accessible through the media element and allow to create even more interesting sound effects. If you are interested in doing that, I strongly recommend downloading the simple "Silverlight Sound Sample" from the <a href="http://msdn.microsoft.com/en-us/library/ff431744(v=vs.92).aspx" target="_blank">Windows Phone 7 code samples page</a> on MSDN. It demonstrates how to use the XNA features that we will learn about below in a Silverlight project. All of the following applies to both XNA and Silverlight if you're doing that.</p> <h3>Differentiation and certification requirements</h3> <p>Regarding audio on Windows Phone 7, especially the <a href="http://download.microsoft.com/download/1/2/D/12D67308-29EE-463D-A442-142F6982AECE/Windows%20Phone%207%20Application%20Certification%20Requirements.pdf" target="_blank">certification requirements</a> differentiate between music and sound effects. Sound effects are played through the XNA class named alike (see below). Those sounds are mixed with music that is played by the media player automatically and it is allowed to play them at any time, even simultaneously to already running music. For background music on the other hand you are supposed to use a different method (the requirements explicitly state that the sound effects class should not be used for that), and your app needs to be aware of already running music. In particular (section 6.5.1, Initial Launch Functionality):</p> <blockquote> <p><em>When the user is already playing music on the phone when the application is launched, the application must not pause, resume, or stop the active music [...]</em></p> </blockquote> <p>And:</p> <blockquote> <p><em>If the application plays its own background music or adjusts background music volume, it must ask the user for consent to stop playing/adjust the background music (e.g. message dialog or settings menu).</em></p> </blockquote> <p>Additionally, the requirements state that if your application plays background music, you must provide a user interface that allows configuration of the settings for that.</p> <p>To many developers the wording of this section is confusing, and working with these requirements can be quite tricky and cause certification failure easily. So what does all this mean?</p> <h4></h4> <h4>Sample startup procedure</h4> <p>When your game starts, you can use the media player's static property "<a href="http://msdn.microsoft.com/en-us/library/Dd254741(v=XNAGameStudio.40).aspx" target="_blank">GameHasControl</a>". If it returns true it means that there's no currently playing music and you can start your own background music right away. If it returns false, you are not allowed to manipulate the currently playing music (like stop or pause it) without asking the user first. So what you need to do is present the user a message where they confirm that it is ok to stop the currently running music. Only if that request is confirmed by the user you are allowed to replace it with your own. You can use the <a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.gamerservices.guide.beginshowmessagebox.aspx" target="_blank">message box</a> functionality of the Guide class for the dialogs.</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_____image_4.png"><img style="background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;border-width: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/______image_thumb_1.png" width="287" height="379" /></a></p> <p>There is a full example with additional details available in the education section of the App Hub, named <a href="http://create.msdn.com/en-US/education/catalog/sample/windows_phone_music_manager" target="_blank">Music Management for Windows Phone 7</a>. If you think about adding background music to your game, I recommend downloading and working with that sample. The included helper class is a good starting point and can easily be extended to fit your needs.</p> <h3>Background music</h3> <p>Now that we have clarified the dos and don'ts of background music, let's see how to implement it. The central element for this is XNA's <a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.media.song.aspx" target="_blank">Song class</a>. Like with any other media, the standard procedure for music and sound effects in XNA is to import it through your content project. When you add an MP3 file (or any other of the supported audio formats) you can choose "Song – XNA Framework" as content processor to turn it into a song:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_____image_6.png"><img style="background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;border-width: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_____image_thumb_2.png" width="400" height="261" /></a></p> <p>Playing the song then is a matter of only a few lines of code:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="rem">// load the song</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> _backgroundMusic = Content.Load<Song>(<span class="str">"Song"</span>);</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> <span class="rem">// try to start the music if we are allowed to do that</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> <span class="kwrd">if</span> (MediaPlayer.GameHasControl)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> <span class="kwrd">try</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span> <span class="rem">// start playing the music</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span> MediaPlayer.Play(_backgroundMusic);</pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> 12:</span> <span class="rem">// set some parameters</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> 13:</span> MediaPlayer.Volume = 0.9f;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> 14:</span> MediaPlayer.IsRepeating = <span class="kwrd">true</span>;</pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> 15:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> 16:</span> _message = <span class="str">"You should hear the background music now."</span>;</pre> <!--CRLF--> <pre class="alt"><span id="lnum17" class="lnum"> 17:</span> }</pre> <!--CRLF--> <pre class="alteven"><span id="lnum18" class="lnum"> 18:</span> <span class="kwrd">catch</span> (InvalidOperationException)</pre> <!--CRLF--> <pre class="alt"><span id="lnum19" class="lnum"> 19:</span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum20" class="lnum"> 20:</span> <span class="rem">// you cannot play songs if the device is connected to Zune </span></pre> <!--CRLF--> <pre class="alt"><span id="lnum21" class="lnum"> 21:</span> _message = <span class="str">"Playing the background music failed."</span>;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum22" class="lnum"> 22:</span> }</pre> <!--CRLF--> <pre class="alt"><span id="lnum23" class="lnum"> 23:</span> }</pre> <!--CRLF--> <pre class="alteven"><span id="lnum24" class="lnum"> 24:</span> <span class="kwrd">else</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum25" class="lnum"> 25:</span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum26" class="lnum"> 26:</span> _message = <span class="str">"Already playing music detected."</span>;</pre> <!--CRLF--> <pre class="alt"><span id="lnum27" class="lnum"> 27:</span> }</pre> <!--CRLF--></div> </div> <h4>Important requirement</h4> <p>The above sample code already incorporates some changes that will be introduced by the first minor update to Windows Phone 7 ("NoDo") which is coming soon to existing devices and already used by some of the currently sold new phones. The problem: the media player on the phone does not have a separate volume control on a per-song basis or mute functionality. That means that if you used parameters like the above "Volume" property while music from the media library was playing, you were able to make changes that the user could not undo later through the media player controls anymore.</p> <p>To overcome this problem, changing any of these parameters is ignored when the currently playing music source is located in the media library. Here is an example:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> MediaPlayer.Play(<span class="kwrd">new</span> MediaLibrary().Songs);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> <span class="rem">// setting this has no effect;</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> <span class="rem">// Volume is 1.0f after this line!</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> MediaPlayer.Volume = 0.5f;</pre> <!--CRLF--></div> </div> <p>So it is important to remember and/or get used to the following recommended pattern of actions:</p> <ul> <li><strong>First </strong>start playing your own music. </li> <li><strong>Then</strong> adjust the necessary parameters like volume etc. </li> </ul> <p>If you follow this recommendation, playing your music will work as expected in both the RTM and updated version of Windows Phone 7. The topic is described in more detail in <a href="http://blogs.msdn.com/b/nicgrave/archive/2011/03/18/mediaplayer-changes-in-xna-game-studio-4-0-for-windows-phone.aspx" target="_blank">this recent blog post</a> by Nick Gravelyn.</p> <h3>Sound effects</h3> <p>Sound effects, just like music, are imported into your game through the content project. This time however, select "Sound Effect – XNA Framework" as content processor:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/___image_8.png"><img style="background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;border-width: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/___image_thumb_3.png" width="387" height="247" /></a></p> <p>Loading the sound effect also is nothing new to us:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="rem">// load sound effect</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> _soundEffect = Content.Load<SoundEffect>(<span class="str">"ShotEffect"</span>);</pre> <!--CRLF--></div> </div> <p>When you look at the properties and methods the <a href="http://msdn.microsoft.com/en-us/library/dd282429.aspx" target="_blank">sound effect class</a> offers for a second, you can see that it in fact has a "Play" method that can play the sound in a "fire and forget" fashion. This is somewhat similar to what we have seen for the Song type above. An <a href="http://msdn.microsoft.com/en-US/library/dd904205.aspx" target="_blank">overload</a> of that method allows you to pass in values for various parameters, like volume, pitch and pan.</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="rem">// play sound </span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> var pan = (<span class="kwrd">float</span>)((_rnd.NextDouble() - 0.5) * 2.0);</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> var pitch = (<span class="kwrd">float</span>)((_rnd.NextDouble() - 0.5) * 2.0);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> var volume = (<span class="kwrd">float</span>)((_rnd.NextDouble() / 2.0) + 0.5);</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> _soundEffect.Play(volume, pitch, pan);</pre> <!--CRLF--></div> </div> <p>It's noteworthy that this method also allows you to play the same sound multiple times simultaneously.</p> <h4>Modifying sounds as they play</h4> <p>A more interesting approach to play sound effects however is to create a <a href="http://msdn.microsoft.com/en-us/library/dd282421.aspx" target="_blank">sound effect instance</a>. The great thing is that by doing this you have a reference to the sound that currently plays and can change its properties on the fly. For example, let's create a sound that pans from the far left to the far right or vice versa as it goes. First of all we create the sound instance by using the respective method of the sound effect, set some initial values and start playing:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="rem">// set the pan speed so a full pan (-1.0 to 1.0 = 2.0)</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> <span class="rem">// is achieved over the duration of the sound</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> _panSpeed = 2.0f / (<span class="kwrd">float</span>)_soundEffect.Duration.TotalSeconds;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> <span class="rem">// randomly choose panning direction</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> _panSpeed = _rnd.NextDouble() > 0.5 ? _panSpeed : -_panSpeed;</pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> <span class="rem">// create the actual effect and play it</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span> _panningSoundEffect = _soundEffect.CreateInstance();</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span> _panningSoundEffect.Pan = _panSpeed > 0.0f ? -1.0f : 1.0f;</pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span> _panningSoundEffect.Play();</pre> <!--CRLF--></div> </div> <p>Then, in the "Update" method of the game, as long as the sound effect instance plays, we update its pan property:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="rem">// update panning sound if present</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> <span class="kwrd">if</span> (_panningSoundEffect != <span class="kwrd">null</span>)</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> <span class="rem">// remove sound if it has finished</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> <span class="kwrd">if</span> (_panningSoundEffect.State == SoundState.Stopped)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> _panningSoundEffect.Dispose();</pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> _panningSoundEffect = <span class="kwrd">null</span>;</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span> }</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span> <span class="kwrd">else</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> 12:</span> <span class="rem">// caluclate new pan value</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> 13:</span> var newPan = _panningSoundEffect.Pan +</pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> 14:</span> (<span class="kwrd">float</span>)(gameTime.ElapsedGameTime.TotalSeconds * _panSpeed);</pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> 15:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> 16:</span> <span class="rem">// make sure the new value doesn't exceed</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum17" class="lnum"> 17:</span> <span class="rem">// the allowed bounds</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum18" class="lnum"> 18:</span> newPan = Math.Max(-1.0f, Math.Min(newPan, 1.0f));</pre> <!--CRLF--> <pre class="alt"><span id="lnum19" class="lnum"> 19:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum20" class="lnum"> 20:</span> <span class="rem">// update pan value</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum21" class="lnum"> 21:</span> _panningSoundEffect.Pan = newPan;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum22" class="lnum"> 22:</span> }</pre> <!--CRLF--> <pre class="alt"><span id="lnum23" class="lnum"> 23:</span> }</pre> <!--CRLF--></div> </div> <p>An important detail to note here is the use of the "Dispose" method. When you're done using a sound effect instance, let it clean up its unmanaged resources that way and remove all references in your code so the garbage collector can do the rest.</p> <h3>3D Sounds</h3> <p>The above example tries to mimic positioning a sound in the coordinate space around the player by manipulating the pan value in real-time. The sound effect instance class actually offers a more sophisticated feature to accomplish this for more complex scenarios: 3D sounds. You can turn any sound effect into a 3D sound by calling the <a href="http://msdn.microsoft.com/en-US/library/dd231890.aspx" target="_blank">"Apply3D" method</a> at least once before you call Play. To make use of the 3D features, you have to repeat this frequently, for example call the method in the Update method of your game.</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="rem">// create a new sound effect</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> _3dSoundEffect = _soundEffect.CreateInstance();</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> <span class="rem">// set looping and make it a 3D sound</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> _3dSoundEffect.IsLooped = <span class="kwrd">true</span>;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> _3dSoundEffect.Apply3D(_audioListener, _audioEmitter);</pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> _3dSoundEffect.Play();</pre> <!--CRLF--></div> </div> <p>As you can see, the method takes an audio listener and an audio emitter as arguments. Both of these are entities that are located in 3D space and share the following properties of interest:</p> <ul> <li>Position: A Vector3 that describes the current position. </li> <li>Velocity: A Vector3 that describes the current movement (= speed + direction) of the entity. </li> <li>Up: A Vector3 that points upward in the entity's space. </li> <li>Forward: A Vector3 that is the direction the entity is facing at. </li> </ul> <p>This allows to set up pretty complex scenarios, where entities are tilted and move sideways, with relative speed to each other. The sound mixing reacts accordingly; the sound is ought to come from the emitter and will be distorted to match what the listener would hear in its current position (using things like attenuation and the doppler effect). </p> <p>A lot of games conceptionally work in a way that the player (the listener) stays positioned in the origin, with the forward and up vectors axis-aligned to the y- and z-axes, and never actually moves around. Instead, the emitter (and the rest of the world) is moved around the player. I have added a sample for this kind of a 3D positioned sound in the source code for this article. It moves an emitter around the player in an ellipse to demonstrate how the sound mixing applies not only panning, but also adjusts the volume as the emitter is moving away from and back towards the player.</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="rem">// calculate the new position</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> var emitterPosition = <span class="kwrd">new</span> Vector3(</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> (<span class="kwrd">float</span>)Math.Cos(gameTime.TotalGameTime.TotalSeconds),</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> 0,</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> (<span class="kwrd">float</span>)Math.Sin(gameTime.TotalGameTime.TotalSeconds) * 3);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> <span class="rem">// update the audio emitter position</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> _audioEmitter.Position = emitterPosition;</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span> <span class="rem">// update the 3D sound</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span> _3dSoundEffect.Apply3D(_audioListener, _audioEmitter);</pre> <!--CRLF--></div> </div> <p>The question of course is whether that level of sophistication is needed on a mobile device (and in 2D games). However, it adds interesting new possibilities to the use of audio, something a lot of games tend to ignore a bit. And when the player wears headphones it can actually create quite a nice acoustic environment.</p> <h3>Summary</h3> <p>Adding sounds and music to your game is pretty straight forward, and as long as you follow a few rules certification for the market place should not become a problem. Audio is one of the areas where XNA has clear advantages regarding the feature set, and luckily Silverlight developers can make use of that easily too. Even on the limited mobile platform the sound libraries have pretty sophisticated support for advanced audio scenarios, including the use of 3D sounds. I hope I was able to demonstrate some of those possibilities in this article. Feel free to leave any feedback or comments.</p> <p><a href="http://www.silverlightshow.net/Storage/Sources/XNA_for_Silverlight_developers_Part8.zip">Download source code</a></p> <h3>About the author</h3> <p>Peter Kuhn aka "Mister Goodcat" has been working in the IT industry for more than ten years. After being a project lead and technical director for several years, developing database and device controlling applications in the field of medical software and bioinformatics as well as for knowledge management solutions, he founded his own business in 2010. Starting in 2001 he jumped onto the .NET wagon very early, and has also used Silverlight for business application development since version 2. Today he uses Silverlight, .NET and ASP.NET as his preferred development platforms and offers training and consulting around these technologies and general software design and development process questions.</p> <p>He created his first game at the age of 11 on the Commodore 64 of his father and has finished several hobby and also commercial game projects since then. His last commercial project was the development of Painkiller:Resurrection, a game published in late 2009, which he supervised and contributed to as technical director in his free time. You can find his tech blog here: <a href="http://www.pitorque.de/MisterGoodcat/">http://www.pitorque.de/MisterGoodcat/</a></p> http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-8-Music-and-sound-effects.aspx editorial@silverlightshow.net (Peter Kuhn ) http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-8-Music-and-sound-effects.aspx#comments http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-8-Music-and-sound-effects.aspx Fri, 25 Mar 2011 00:00:00 GMT XNA for Silverlight developers: Part 7 - Collision detection <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; padding-top: 5px; text-align: center;">Are you interested in <strong>creating games for Windows Phone 7</strong> but don't have XNA experience? <br /> <a href="http://www.silverlightshow.net/video/XNA-for-WP7-Webinar.aspx">Watch the recording</a> of the recent intro webinar delivered by Peter Kuhn '<strong>XNA for Windows Phone 7</strong>'.<a href="https://www1.gotomeeting.com/register/256344145" target="_blank"></a><br /> </div> <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; width: 196px; float: right; height: 401px; margin-left: 10px; margin-right: 5px; padding-top: 5px;"> <h3>Don't miss...</h3> <ul style="list-style-type: circle; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-left: 20px; font-size: 12px;"> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/WP7-What-is-Windows-Phone-7.aspx">What is Windows Phone series</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Exploring-Silverlight-XNA-integration-on-Windows-Phone-7.aspx">XNA article by Levente Mihály</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Windows-Phone-7-Part-1-Getting-Started.aspx">WP7 series by Andrea Boschin</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx">The XNA series is now available as a fully offline resource</a> </li> </ul> <p style="padding-bottom: 5px; text-align: center;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx"><img style="border:0px solid;" alt="XNA for Silverlight Developers Ebook" src="http://www.silverlightshow.net/storage/thumb_xna_ebook_cover_150_0.png" usemap="#rade_img_map_1291385581316" /></a></p> <p style="font-size: 12px;">     <a href="http://www.silverlightshow.net/ebooks.aspx">All SilverlightShow Ebooks</a> <img alt="" src="http://www.silverlightshow.net/Storage/arrow-content.jpg" /></p> </div> <p>This article is part 7 of the series "XNA for Silverlight developers".</p> <p><strong><strong>This article is compatible with Windows Phone "Mango". <em style="font-style: italic;"><br /> Latest update: Aug 1, 2011.</em></strong></strong></p> <p>Detection of and reaction to collisions is a very fundamental element in games. As soon as it has moving components of some sort, it is likely that collisions are of concern. For example, the player character may collide with the scenery of the game. The player may also collide with other characters (enemies, monsters etc.), and of course many games work with projectiles that can hit various other parts on the game screen. But what exactly are collisions in a game, and what are good ways to detect and handle them? This article tries to give answers.</p> <h3>What collisions are</h3> <p>The definition of collisions in our sense is pretty simple: a collision is the state of two or more objects that intersect. But at second glance, we will see that it can become pretty difficult to translate this seemingly simple definition to the context of a game. </p> <p>We have learned that in XNA, all sprites are actually textures, or parts of textures, which are drawn on the screen. In both cases, the shape of a sprite is a rectangle. This is the only option available; we can either draw a whole texture on the screen (which is rectangular per se), or define a portion of a texture that should be drawn using the "source rectangle" mechanism. So apparently, working with collisions comes down to detecting the intersection of rectangles:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_____image_2.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_____image_thumb.png" width="261" height="261" /></a></p> <p>Of course it's not as simple as that. We already have used ways to work around the rectangle restriction in former parts of this series and know that we are very well able to create irregularly shaped sprites in XNA, using the instrument of transparency. For example, if we wanted to create a round object, like a ball, we'd simply make those parts that don't belong to the ball transparent:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/__image_12.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/__image_thumb_5.png" width="252" height="185" /></a></p> <p>And this inevitably leads to potential problems:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/__image_10.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/__image_thumb_4.png" width="262" height="261" /></a></p> <p>So a better definition of collisions for 2D games would be: <em>a collision is the state of two or more objects that intersect in their non-transparent parts</em>. In the following paragraphs, we will look at different approaches to optimize the detection of collisions in both accuracy and performance.</p> <h3></h3> <h3>Pixel-perfect collision detection</h3> <p>If we take the above mentioned extended definition and translate it into an algorithm, then it would read like this:</p> <ul> <li>Check whether the rectangle bounds of two sprites intersect. </li> <li>If they do, check whether any pair of pixels within the intersection are non-transparent on both sprite textures. </li> <li>If we found at least one pair of non-transparent pixels, we have detected a "real" collision. </li> </ul> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/__image_8.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/__image_thumb_3.png" width="301" height="291" /></a></p> <p>How does XNA help us with this approach?</p> <h4>Retrieving and accessing the texture data</h4> <p>In XNA you can get access to the raw texture data by using the <a href="http://msdn.microsoft.com/en-us/library/bb197089.aspx" target="_blank">"GetData" generic method</a> of the Texture2D class. If you think about it, a texture really is only a huge array of color values – each pixel in that texture is made of the red, green, blue and alpha values in that place. That is why you can retrieve the raw texture values as color array:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> var texture = content.Load<Texture2D>(textureName);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> Color[] textureData = <span class="kwrd">new</span> Color[texture.Width * texture.Height];</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> texture.GetData(textureData);</pre> <!--CRLF--></div> </div> After loading the texture, you have to create an array that is big enough to hold all color values of the texture and then pass it into the "GetData" method. You will notice that the result actually is a one-dimensional array of color values, whereas the texture itself is a two-dimensional structure. This is by design because it is faster and offers more flexibility than a multi-dimensional structure. If you want to access the texture data in a more "natural" way using x and y coordinates, you have to calculate the offset in the one-dimensional array as <em>x + y * textureWidth</em>. <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Texture_array_2.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px;" title="Texture_array" alt="Texture_array" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Texture_array_thumb.png" width="384" height="322" /></a></p> <p>In the above example, the texture width is 5 pixels, and if you wanted to access the pixel at position (3, 2) in the one-dimensional array you retrieve by calling "GetData", you would calculate the index as 3 + 2 * 5 = 13.</p> <p>Note: When you are using a sprite sheet (e.g. multiple sprites or sprite animation frames on a single texture), you have to take the current source rectangle your sprite uses into account too. The calculation then would look like:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="kwrd">public</span> Color GetColorAt(<span class="kwrd">int</span> x, <span class="kwrd">int</span> y)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> <span class="rem">// we need to take the source rectangle into account</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> var transformedX = x + SourceRectangle.X;</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> var transformedY = y + SourceRectangle.Y;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> <span class="rem">// calculate the offset and return the color from</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> <span class="rem">// the one-dimensional texture data returned by the "GetData" method</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span> <span class="kwrd">return</span> TextureData[transformedX + transformedY * Texture.Width];</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span> }</pre> <!--CRLF--></div> </div> <p>This demonstrates the extra step involved to get the correct offset in the sprite texture, depending on the source rectangle.</p> <h4>Calculating the intersection</h4> <p>Using the location and dimensions of two sprites, we can calculate the intersecting rectangle fairly easily. To this end, we first calculate the bounds of the sprite in screen coordinates, which I once again have made a method of my custom sprite class:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="kwrd">public</span> Rectangle GetScreenRectangle()</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> <span class="rem">// calculate the rectangle from the current location</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> <span class="rem">// and the current source rectangle</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> var rectangle = <span class="kwrd">new</span> Rectangle(</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> (<span class="kwrd">int</span>)Location.X,</pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> (<span class="kwrd">int</span>)Location.Y,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> SourceRectangle.Width,</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span> SourceRectangle.Height);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span> <span class="kwrd">return</span> rectangle;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> 12:</span> }</pre> <!--CRLF--></div> </div> <p>If your sprite only uses a whole texture, you can use that texture's width and height for this. I used the source rectangle my custom sprite class defines, which can potentially be different from the texture dimensions, e.g. when a sprite sheet is used.</p> <p>Given two of those screen space rectangles, the intersection can be calculated as:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="kwrd">public</span> <span class="kwrd">static</span> Rectangle Intersect(<span class="kwrd">this</span> Rectangle rectangleA, Rectangle rectangleB)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> <span class="rem">// get the top, bottom, left and right coordinates</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> <span class="rem">// of the intersection</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> <span class="kwrd">int</span> top = Math.Max(rectangleA.Top, rectangleB.Top);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> <span class="kwrd">int</span> bottom = Math.Min(rectangleA.Bottom, rectangleB.Bottom);</pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> <span class="kwrd">int</span> left = Math.Max(rectangleA.Left, rectangleB.Left);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> <span class="kwrd">int</span> right = Math.Min(rectangleA.Right, rectangleB.Right);</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span> <span class="rem">// do the rectangles intersect at all?</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span> <span class="kwrd">if</span> (top > bottom || left > right)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> 12:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> 13:</span> <span class="kwrd">return</span> Rectangle.Empty;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> 14:</span> }</pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> 15:</span> <span class="kwrd">else</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> 16:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum17" class="lnum"> 17:</span> <span class="kwrd">return</span> <span class="kwrd">new</span> Rectangle(left, top, right - left, bottom - top);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum18" class="lnum"> 18:</span> }</pre> <!--CRLF--> <pre class="alt"><span id="lnum19" class="lnum"> 19:</span> }</pre> <!--CRLF--></div> </div> <p>Again this is pretty straight-forward, but some people have problems picturing it. It really helps to use a piece of paper and draw a few possible cases to realize that this actually creates the intersection rectangle (unless there is no intersection, which results in the static "Empty" rectangle as a result).</p> <h4>Comparing pixels</h4> <p>It's now time to put those pieces together to compare the pixel colors of the intersection rectangle of two sprites. To this end, we first get the intersection rectangle and then inspect every pixel in it. To retrieve the color values of a certain pixel for a sprite, we need to transform these coordinates back to the respective sprite's space. We can then use the "GetColorAt" method we've created above to retrieve both color values and compare their alpha values:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">bool</span> IsPerPixelCollidingWith(<span class="kwrd">this</span> Sprite spriteA, Sprite spriteB)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> <span class="rem">// get the bounds in screen coordinates</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> var rectangleA = spriteA.GetScreenRectangle();</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> var rectangleB = spriteB.GetScreenRectangle();</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> <span class="rem">// find the bounds of the rectangle intersection in screen space</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> var intersection = rectangleA.Intersect(rectangleB);</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span> <span class="kwrd">if</span> (intersection == Rectangle.Empty)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span> <span class="kwrd">return</span> <span class="kwrd">false</span>;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> 12:</span> }</pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> 13:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> 14:</span> <span class="rem">// Check every point within the intersection bounds</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> 15:</span> <span class="kwrd">for</span> (<span class="kwrd">int</span> y = intersection.Top; y < intersection.Bottom; y++)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> 16:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum17" class="lnum"> 17:</span> <span class="kwrd">for</span> (<span class="kwrd">int</span> x = intersection.Left; x < intersection.Right; x++)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum18" class="lnum"> 18:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum19" class="lnum"> 19:</span> <span class="rem">// to retrieve the color of a pixel</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum20" class="lnum"> 20:</span> <span class="rem">// we need to transform the coordinates back</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum21" class="lnum"> 21:</span> <span class="rem">// to the sprite's local space</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum22" class="lnum"> 22:</span> <span class="kwrd">int</span> xA = x - rectangleA.Left;</pre> <!--CRLF--> <pre class="alt"><span id="lnum23" class="lnum"> 23:</span> <span class="kwrd">int</span> yA = y - rectangleA.Top;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum24" class="lnum"> 24:</span> Color colorA = spriteA.GetColorAt(xA, yA);</pre> <!--CRLF--> <pre class="alt"><span id="lnum25" class="lnum"> 25:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum26" class="lnum"> 26:</span> <span class="kwrd">int</span> xB = x - rectangleB.Left;</pre> <!--CRLF--> <pre class="alt"><span id="lnum27" class="lnum"> 27:</span> <span class="kwrd">int</span> yB = y - rectangleB.Top;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum28" class="lnum"> 28:</span> Color colorB = spriteB.GetColorAt(xB, yB);</pre> <!--CRLF--> <pre class="alt"><span id="lnum29" class="lnum"> 29:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum30" class="lnum"> 30:</span> <span class="rem">// If both pixels are not completely transparent,</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum31" class="lnum"> 31:</span> <span class="kwrd">if</span> (colorA.A != 0 && colorB.A != 0)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum32" class="lnum"> 32:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum33" class="lnum"> 33:</span> <span class="rem">// then an intersection has been found</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum34" class="lnum"> 34:</span> <span class="kwrd">return</span> <span class="kwrd">true</span>;</pre> <!--CRLF--> <pre class="alt"><span id="lnum35" class="lnum"> 35:</span> }</pre> <!--CRLF--> <pre class="alteven"><span id="lnum36" class="lnum"> 36:</span> }</pre> <!--CRLF--> <pre class="alt"><span id="lnum37" class="lnum"> 37:</span> }</pre> <!--CRLF--> <pre class="alteven"><span id="lnum38" class="lnum"> 38:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum39" class="lnum"> 39:</span> <span class="kwrd">return</span> <span class="kwrd">false</span>;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum40" class="lnum"> 40:</span> }</pre> <!--CRLF--></div> </div> <p>The transitions between different coordinate systems may seem confusing at first, but you should be able to get accustomed to it quickly once you understand the chain of involved actions required to retrieve the actual color value:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_image_18.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_image_thumb_8.png" width="558" height="360" /></a></p> <h4>A sample application</h4> <p>The first sample you can find in the download package creates some balls with random initial positions and velocities that move around the screen. In each frame, collisions are detected using the above described approach, and if one is found, the involved sprites are tinted red. Running in the emulator, it looks like this:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_image_22.png"><img style="border:0px solid; background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_image_thumb_10.png" width="640" height="384" /></a></p> <p>When you observe the balls, you can see that indeed no collisions are detected if their transparent corners overlap.</p> <h3>Performance considerations</h3> <p>A collision detection algorithm like this is not only very accurate, it is also pretty expensive. Especially when you have sprites with large amounts of transparency you can easily end up with thousands of individual pixels to compare in each frame. Also, the number of collision checks that need to be performed obviously raises with the number of objects. The above screenshot includes that number, and as you can see, 15 objects on the screen already require 105 checks. The first ball is compared to all the 14 others, the second one to the remaining 13, the third one to 12 and so on. More accurately:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_24.png"><img style="border:0px solid; background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_thumb_11.png" width="140" height="69" /></a></p> <p>I was wondering when this would become a problem and did some tests on my phone. Interestingly I was able to raise the number of balls to 100 without any problems. 4950 of these collision checks are performed in this case in each frame update. Performance declined quickly after that. 150 balls (11175 collision checks) are pretty horrible, and depending on the random distribution of the sprites reached frame rates as low as 2 per second. Again the question is whether you will really ever need and have more than 100 objects on the screen that need collision detection. But still let's talk about possible improvements and alternatives.</p> <p>Please note that a rule of thumb for the following actions is that you shouldn't put too much effort into preliminary performance optimization until you actual start seeing problems, or unless it's low hanging fruits you know will pay off. </p> <h4></h4> <h4>Working with bounding geometries</h4> <p>Instead of working with pixel perfect collision detection, an often used alternative is to use some sort of bounding geometry, like a rectangle or a circle. In more sophisticated scenarios, even multiple geometries can be used. For example, a concept borrowed from 3D games is to use three circles for a human-like character (head, torso, legs). In almost all cases this means losing precision regarding the detection of collisions, but it can result in quite a performance gain.</p> <p>In our sample, the obvious improvement is to use a circle as bounding geometry, which in this special case results in the same perfect collision detection, because we do use, well, circles as sprites. A modified collision detection in this case can use a custom circle structure, like:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="kwrd">public</span> <span class="kwrd">class</span> Circle</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> <span class="kwrd">public</span> Vector2 Center;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> <span class="kwrd">public</span> <span class="kwrd">float</span> Radius;</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> <span class="kwrd">public</span> <span class="kwrd">bool</span> Intersects(Circle other)</pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> <span class="rem">// calculate the minimum distance of the centers</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span> <span class="rem">// the circles are not intersecting at</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span> var minCenterDistance = Radius + other.Radius;</pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> 12:</span> <span class="rem">// calculate the actual distance and compare</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> 13:</span> var distanceVector = Center - other.Center;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> 14:</span> <span class="kwrd">if</span> (distanceVector.Length() < minCenterDistance)</pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> 15:</span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> 16:</span> <span class="kwrd">return</span> <span class="kwrd">true</span>;</pre> <!--CRLF--> <pre class="alt"><span id="lnum17" class="lnum"> 17:</span> }</pre> <!--CRLF--> <pre class="alteven"><span id="lnum18" class="lnum"> 18:</span> <span class="kwrd">else</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum19" class="lnum"> 19:</span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum20" class="lnum"> 20:</span> <span class="kwrd">return</span> <span class="kwrd">false</span>;</pre> <!--CRLF--> <pre class="alt"><span id="lnum21" class="lnum"> 21:</span> }</pre> <!--CRLF--> <pre class="alteven"><span id="lnum22" class="lnum"> 22:</span> }</pre> <!--CRLF--> <pre class="alt"><span id="lnum23" class="lnum"> 23:</span> }</pre> <!--CRLF--></div> </div> <p>A sprite can then create its bounding circle depending on its current position and size:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="kwrd">public</span> Circle GetBoundingCircle()</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> <span class="rem">// create a new bounding circle</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> Circle result = <span class="kwrd">new</span> Circle();</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> <span class="rem">// the center is the location + half the size</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> result.Center = <span class="kwrd">new</span> Vector2(Location.X + SourceRectangle.Width / 2.0f,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> Location.Y + SourceRectangle.Height / 2.0f);</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span> <span class="rem">// use half the width as radius</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span> result.Radius = SourceRectangle.Width / 2.0f;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> 12:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> 13:</span> <span class="kwrd">return</span> result;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> 14:</span> }</pre> <!--CRLF--></div> </div> <p>With this approach and the same sample as above, the demo was able to provide a decent frame rate for almost twice as much balls: 190, which require 17955 collision checks in each frame. This illustrates how much more expensive the pixel-perfect check actually is.</p> <p>Unfortunately, no general recommendation can be made with regards to bounding geometries. In some cases you can achieve very good results using them (like in this case), but in other situations you may really see that a bounding box or similar cannot stand up to your requirements and deliver the desired accuracy. Often using bounding geometries requires fine-tuning, and customizing the detection algorithms for your particular game to a point where you even differentiate between collisions of certain game object types (e.g. treating player-monster collisions differently from player-projectile etc.).</p> <h4></h4> <h4>Tiling your game</h4> <p>If you look at the above numbers, you can see that 10 times the balls (150 instead of 15) resulted in more than 100 times more collision checks (11175 compared to 105). Reducing the number of checks by additional tricks is therefore highly desired. An often used technique you may be able to take advantage of for this is to tile your game. What this means in this context is to divide the screen into multiple areas that are treated independently from each other. At the moment, we are checking each sprite on the screen against all the others, even if they are located on opposite ends and obviously cannot collide. So the idea is to separate your sprites into groups depending on their rough location. This makes perfect sense for example for platformers, where monsters often cannot leave certain areas and it wouldn't make sense to check these against the player when they are not even near that area. Each of those groups can be inspected separately which can save a lot of the required checks.</p> <p>Of course this can also be contradictory, for example if your sprites can move between multiple tiles/areas and you need to determine what area they are currently in very often. Once again no general recommendation can be made and the decision should be based on your scenario.</p> <h3></h3> <h3>Potential problems</h3> <h4>Scale and rotate</h4> <p>If you are using a pixel-perfect collision detection algorithm like the one above, you will inevitably run into some problems if you use scaling and rotation with the involved sprites. In our sample, when we are only using translations, there is a one-to-one relationship between the pixels on the screen and the pixels on the involved textures (texels), and we have a very simple geometry to check (an intersection rectangle). This makes these checks relatively easy.</p> <p>Now when you zoom and/or rotate your sprites, you will find that a texel suddenly can span multiple pixels on the screen (or the other way round: one pixel's final color is determined by multiple texels), and that the involved geometries are not axis-aligned anymore. How to overcome this problem?</p> <p>Once again the solution is to use coordinate transforms. In particular, what you'd typically do is create a transformation matrix that transforms coordinates from the one sprite's local space to the other sprite's local space. This allows you to iterate over the pixels in one sprite like we did above and get the corresponding pixel color value in the other sprite, whilst taking into account the involved rotate and scale transforms. The education section of Microsoft's App Hub site actually has an example for this that shows how to handle rotations (<a href="http://create.msdn.com/en-US/education/catalog/tutorial/collision_2d_perpixel_transformed" target="_blank">link</a>), but unfortunately it only includes source code for the desktop and Xbox version of XNA. For your convenience, I have converted the sample to Windows Phone 7, cleaned it up a bit and added the code for scaling. Since a full discussion of the required steps is beyond this article, I strongly recommend downloading the source code package below if you are interested in it. I hope the extensive comments in the code help understand the concepts. Here is what the sample looks like in the phone emulator:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_13.png"><img style="background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;border-width: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_____image_thumb_1.png" width="640" height="342" /></a></p> <p>You can use the arrow keys to move around the small player character at the bottom; to enable keyboard input in the emulator, you have to press the "Pause" key on your keyboard first. The sample demonstrates how pixel-perfect collision detection still works even for the rotated and scaled elements on the screen.</p> <h4>The a posteriori bug</h4> <p>The approach we are using here (and that is used in most games) is named "a posteriori", which means we are first updating the scene and move all objects, and then check for collisions afterwards. If a collision is detected, the game logic reacts accordingly. Whether that is a simple color tint change like in our example or an attempt to simulate physical laws (bouncing off etc.) depends on the game. In a lot of cases this works surprisingly well, but in some situations you may come across the "a posteriori bug". The problem with this technique in general is that it always only looks at a current snapshot of the game situation instead of considering a continuous analysis. This is easy to implement and provides good performance, however it can actually lead to collisions that are missed, for example when objects are moving very fast, if very thin or small objects are involved, or both. Let's look at an example:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_26.png"><img style="border:0px solid; background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_thumb_12.png" width="261" height="255" /></a></p> <p>In the above picture, we see two successive frames. The green ball is moving very fast to the right and is supposed to bounce off the orange wall. However, due to the high velocity the update process has moved the ball so far to the right that it traveled past the wall within one frame. In neither of the two frames the collision detection is able to do its job, because there never is an actual intersection. In real world scenarios, this often happens with projectiles because usually they're pretty small and move around much faster than other objects.</p> <p>If you identify such situations and are not able to find a simpler workaround, you indeed have to do a temporal analysis for these cases. In the above example, you would always look at two successive frames, for example by comparing the current position to the previous one, and use that information to check if collisions happened "in between" the frames. From a mathematical point of view, this involves intersecting a line (through the previous and current position) with another line or geometry (the wall) and checking whether the intersection point (if existent) lies between the previous and current position of the object.</p> <h3>What about Silverlight?</h3> <p>In all the articles so far I've started with an evaluation of how people would implement the respective features in Silverlight, and then make the transition to XNA. The reason there is no such analysis in this article simply is that Silverlight has no built-in features and support for collision detection in games. When you are creating a game in Silverlight you will find that you have to think about the same concepts we were exploring in XNA here, and then manually implement the required code.</p> <p>Of course others have done this already; you can find a nice sample implementation by Christophe Geers <a href="http://cgeers.wordpress.com/2010/06/20/silverlight-asteroids-part-8-pixel-perfect-collision-detection/" target="_blank">here</a> (which in turn is based on an <a href="http://www.andybeaulieu.com/Home/tabid/67/EntryID/160/Default.aspx" target="_blank">implementation</a> of Andy Beaulieu). When you look at the code you can see that it's quite similar to what we did: first bounding rectangles are checked for performance optimization, and then every pixel in the intersection rectangle is inspected separately using the <a href="http://msdn.microsoft.com/en-us/library/cc838402(v=VS.95).aspx" target="_blank">FindElementsInHostCoordinates</a> method of the visual tree helper. And just like in XNA, this is a very expensive operation that should not be used excessively.</p> <h3></h3> <h3>Summary and further readings</h3> <p>Collision detection is a complex topic, and even though we have learned a pretty good method to detect collisions accurately in this article, it only scratches the surface of it. For example, a completely different issue is how to actually react to collisions once they are detected. In simple cases you are just interested in the hit itself ("shoot at monster"). But sooner or later you may also want to create more sophisticated features that involve simple or complex physics ("bouncing ball") which adds a whole new set of problems to solve, and a whole new world of interesting things to do (think physics engines etc.).</p> <p>A good place to find additional material on the topic is the <a href="http://create.msdn.com/en-US/education/catalog/?devarea=8" target="_blank">App Hub education section about collision detection</a>. It has some articles specific to Windows Phone 7 and even some full games you can dissect and learn from. Another example from that site which is not explicitly mentioned in the collision section is the <a href="http://create.msdn.com/en-US/education/catalog/sample/platformer" target="_blank">Platformer code sample</a>. That game uses bounding boxes for collision detection and achieves some pretty good results with it. It also shows how to handle simple physics like gravity and common situations with collision detection like characters standing on the floor.</p> <p>The source code package this time contains three solutions: the pixel-perfect approach we learned about in the first part of the article, a different solution using bounding circles, and the reworked Microsoft sample that shows how to handle collisions of transformed objects on the phone.</p> <p><a href="http://www.silverlightshow.net/Storage/Sources/XNA_for_Silverlight_developers_Part7.zip">Download source code</a></p> <h3>About the author</h3> <p>Peter Kuhn aka "Mister Goodcat" has been working in the IT industry for more than ten years. After being a project lead and technical director for several years, developing database and device controlling applications in the field of medical software and bioinformatics as well as for knowledge management solutions, he founded his own business in 2010. Starting in 2001 he jumped onto the .NET wagon very early, and has also used Silverlight for business application development since version 2. Today he uses Silverlight, .NET and ASP.NET as his preferred development platforms and offers training and consulting around these technologies and general software design and development process questions.</p> <p>He created his first game at the age of 11 on the Commodore 64 of his father and has finished several hobby and also commercial game projects since then. His last commercial project was the development of Painkiller:Resurrection, a game published in late 2009, which he supervised and contributed to as technical director in his free time. You can find his tech blog here: <a href="http://www.pitorque.de/MisterGoodcat/">http://www.pitorque.de/MisterGoodcat/</a></p> http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-7-Collision-detection.aspx editorial@silverlightshow.net (Peter Kuhn ) http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-7-Collision-detection.aspx#comments http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-7-Collision-detection.aspx Wed, 16 Mar 2011 02:58:00 GMT Simulating rain in Silverlight <p><strong><em>This article is compatible with the latest version of Silverlight.</em></strong></p> <p><strong></strong></p> <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; width: 200px; float: right; margin-left: 10px; padding-top: 5px;"> <h3>Don't miss...</h3> <ul style="list-style-type: circle; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-left: 20px; font-size: 12px;"> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/Webinars.aspx">Free SilverlightShow Webinars</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Getting-ready-for-the-exams-Part-1.aspx">Getting ready for Silverlight Exam 70-506 series</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/ClassifiedCabinet-A-Quick-Start.aspx">ClassifiedCabinet: A Quick Start</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/book/Pro-Business-Applications-with-Silverlight-3.aspx">Pro Business Apps with SL4 book: </a></li> </ul> <p style="padding-bottom: 5px;">        <a href="http://www.silverlightshow.net/book/Pro-Business-Applications-with-Silverlight-3.aspx"> <img alt="Pro Business Applications with Silverlight 4 book" src="http://www.silverlightshow.net/Storage/ProSL4.jpg" /></a> </p> <p style="font-size: 12px;">           <a href="http://www.silverlightshow.net/Books.aspx">Show more books</a><img alt="" src="http://www.silverlightshow.net/Storage/arrow-content.jpg" /></p> </div> <h3><strong> <h3>Introduction</h3> </strong></h3> <p>In this article I will try to simulate rain in Silverlight application that can be programmatically configured to meet your expectations. Although Silverlight at the time being is very CPU consuming, rain effect may be a common challenge for a developer or designer.</p> <p>I believe that there are many computer graphics techniques, such as <a href="http://en.wikipedia.org/wiki/Particle_system">Particle system</a>, to simulate realistically processes that involve tiny particles interacting with each other. However, in this article I will show you how to simulate rain with almost no effort with some very simple algorithm.</p> <p>Here is a little demo and source code of what we will have done at the end in just a few minutes:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/lnikolov/RainEffectTestPage.html">View live demo</a> - with a small amount of raindrops to avoid performance issues</p> <p><a href="http://www.silverlightshow.net/Storage/Users/lnikolov/RainEffect.zip">Download source code</a></p> <p>Also check the second part of this series - <a href="http://www.silverlightshow.net/items/Simulating-rain-in-Silverlight-Part-2-Optimization.aspx">"Simulating rain in Silverlight Part 2 - Optimization"</a>!</p> <p><strong></strong></p> <h2><strong> <h3>The idea</h3> </strong></h2> <p>Before looking at the algorithm, let me make the main flow clear. As I wrote a few lines above, the designer adjusts programmatically the rain on his own. That is – the height and width of the scene, the interval of falling drops and the amount of drops animated at a time. This all is achieved with a couple of classes that form the rain nature:</p> <ul> <li>Drop – holds the drop view </li> <li>DropSettings – describes a drop and its settings; currently only the height that the drop pours is implemented; this act as a view-model as I use it to bind in the drop view when animating the motion from top to bottom part of the scene </li> <li>Rain – this is my main object to maintain the rain process; it uses a simple algorithm, I will next describe, to simulate drops falling randomly through scene </li> </ul> <p><strong></strong></p> <h2><strong> <h3>The algorithm</h3> </strong></h2> <p>Now it’s time for the algorithm. It, actually, is really very simple one. What determines the average rain? Yes, you are right and this is what I was looking for – direction and intensity.</p> <p>First, to simulate the rain, we need to perform the algorithm exactly as many times, as we had previously defined to be the amount of drops to fall simultaneously. Furthermore, this should be done exactly as many times as we have specified to be the interval between the raindrops. </p> <p>We start with the following:</p> <div class="reCodeBlock" style="border:1px solid #7f9db9;overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 12px;"><code style="color: #006699; font-weight: bold;">for</code> <code style="color: #000000;">(</code><code style="color: #006699; font-weight: bold;">int</code> <code style="color: #000000;">i = 0; i < </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.DropsPerInterval; ++i)</code></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important;"><code style="color: #000000;"><span style="font-size: 12px;">{</span></code></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important;"><code style="color: #000000;"><span style="font-size: 12px;">}</span></code></span></div> </div> <p>To distinguish between UI elements in the scene, when constructing the Rain object, a canvas is specified, and this panel is where the rain will be simulated in. Every drop that we create should be a child of this panel.</p> <div class="reCodeBlock" style="border:1px solid #7f9db9;overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 12px;"><code style="color: #000000;">Drop drop = </code><code style="color: #006699; font-weight: bold;">new</code> <code style="color: #000000;">Drop(</code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.DropSettings);</code></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important; font-size: 12px;"><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.Scene.Children.Add(drop);</code></span></div> </div> <p>Next step is to generate the depth in which the drop is shown in the scene. Depth visually controls the scale and further you go deep in the scene, drops become smaller.</p> <div class="reCodeBlock" style="border:1px solid #7f9db9;overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 12px;"><code style="color: #006699; font-weight: bold;">double</code> <code style="color: #000000;">depth = rand.NextDouble();</code></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important; font-size: 12px;"><code style="color: #000000;">drop.RenderTransform = </code><code style="color: #006699; font-weight: bold;">new</code> <code style="color: #000000;">ScaleTransform</code></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important;"><code style="color: #000000;"><span style="font-size: 12px;">{</span></code></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 12px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #000000;">ScaleX = depth,</code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 12px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #000000;">ScaleY = depth,</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important;"><code style="color: #000000;"><span style="font-size: 12px;">};</span></code></span></div> </div> <p>The direction of the rain affects directly on the angle which drops fall the ground in. In the demo this angle is fixed to be between 0 and 10 degrees clockwise rotation.</p> <div class="reCodeBlock" style="border:1px solid #7f9db9;overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 12px;"><code style="color: #006699; font-weight: bold;">double</code> <code style="color: #000000;">angle = rand.NextDouble() * 10;</code></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important; font-size: 12px;"><code style="color: #000000;">drop.LayoutRoot.RenderTransform = </code><code style="color: #006699; font-weight: bold;">new</code> <code style="color: #000000;">RotateTransform</code></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important;"><code style="color: #000000;"><span style="font-size: 12px;">{</span></code></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 12px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #000000;">Angle = angle,</code></span></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important;"><code style="color: #000000;"><span style="font-size: 12px;">};</span></code></span></div> </div> <p>So far we had spread the drops randomly in depths and directions between 0 and 10 degrees. To put the finishing touches, we need to randomly scatter the raindrops in the two dimensional perspective of the scene.</p> <div class="reCodeBlock" style="border:1px solid #7f9db9;overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 12px;"><code style="color: #000000;">drop.Margin = </code><code style="color: #006699; font-weight: bold;">new</code> <code style="color: #000000;">Thickness(Convert.ToInt32(rand.NextDouble() * </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.Width), 0, 0, 0);</code></span></div> </div> <p>This will set gratuitously from 0 to the right most corner the gap between the drop and most left corner.</p> <p>Finally, the last thing to do is to start the animation.</p> <div class="reCodeBlock" style="border:1px solid #7f9db9;overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important;"><code style="color: #000000;"><span style="font-size: 12px;">drop.Animate();</span></code></span></div> </div> <p><strong></strong></p> <p>That's it!</p> <h3>Designing the drop</h3> <p>To design the raindrop view I use <a href="http://www.microsoft.com/expression/products/blend_overview.aspx">Expression Blend</a>. Here is the XAML for it (for clarity I have omitted the <em>Data </em>attribute):</p> <div class="reCodeBlock" style="border:1px solid #7f9db9;overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 12px;"><code style="color: #000000;"><</code><code style="color: #006699; font-weight: bold;">Path</code> <code style="color: #808080;">Stretch</code><code style="color: #000000;">=</code><code style="color: blue;">"Fill"</code> <code style="color: #000000;">Data="...</code></span><span style="font-size: 12px;"><code style="color: #000000;">" </code><code style="color: #808080;">VerticalAlignment</code><code style="color: #000000;">=</code><code style="color: blue;">"Stretch"</code> <code style="color: #808080;">RenderTransformOrigin</code><code style="color: #000000;">=</code><code style="color: blue;">"0.5,0.5"</code></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 12px;"><code style="color: #808080;">    Margin</code><code style="color: #000000;">=</code><code style="color: blue;">"1.222,-2.518,-4.94,-7.559"</code> <code style="color: #808080;">UseLayoutRounding</code><code style="color: #000000;">=</code><code style="color: blue;">"False"</code> <code style="color: #000000;">></code></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 12px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #000000;"><</code><code style="color: #006699; font-weight: bold;">Path.Fill</code><code style="color: #000000;">></code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 12px;"><code>        </code><span style="margin-left: 24px !important;"><code style="color: #000000;"><</code><code style="color: #006699; font-weight: bold;">LinearGradientBrush</code> <code style="color: #808080;">EndPoint</code><code style="color: #000000;">=</code><code style="color: blue;">"1.420529961586,0.976535022258759"</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 12px;"><code>            </code><span style="margin-left: 36px !important;"><code style="color: #808080;">StartPoint</code><code style="color: #000000;">=</code><code style="color: blue;">"0.331510990858078,0.976535022258759"</code><code style="color: #000000;">></code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 12px;"><code>            </code><span style="margin-left: 36px !important;"><code style="color: #000000;"><</code><code style="color: #006699; font-weight: bold;">LinearGradientBrush.RelativeTransform</code><code style="color: #000000;">></code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 12px;"><code>                </code><span style="margin-left: 48px !important;"><code style="color: #000000;"><</code><code style="color: #006699; font-weight: bold;">TransformGroup</code><code style="color: #000000;">></code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 12px;"><code>                    </code><span style="margin-left: 60px !important;"><code style="color: #000000;"><</code><code style="color: #006699; font-weight: bold;">SkewTransform</code> <code style="color: #808080;">AngleY</code><code style="color: #000000;">=</code><code style="color: blue;">"0"</code> <code style="color: #808080;">AngleX</code><code style="color: #000000;">=</code><code style="color: blue;">"17.4704"</code> <code style="color: #808080;">CenterY</code><code style="color: #000000;">=</code><code style="color: blue;">"0.976535"</code> <code style="color: #808080;">CenterX</code><code style="color: #000000;">=</code><code style="color: blue;">"0.331511"</code><code style="color: #000000;">/></code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 12px;"><code>                    </code><span style="margin-left: 60px !important;"><code style="color: #000000;"><</code><code style="color: #006699; font-weight: bold;">RotateTransform</code> <code style="color: #808080;">Angle</code><code style="color: #000000;">=</code><code style="color: blue;">"-61.3206"</code> <code style="color: #808080;">CenterY</code><code style="color: #000000;">=</code><code style="color: blue;">"0.976535"</code> <code style="color: #808080;">CenterX</code><code style="color: #000000;">=</code><code style="color: blue;">"0.331511"</code><code style="color: #000000;">/></code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 12px;"><code>                </code><span style="margin-left: 48px !important;"><code style="color: #000000;"></</code><code style="color: #006699; font-weight: bold;">TransformGroup</code><code style="color: #000000;">></code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 12px;"><code>            </code><span style="margin-left: 36px !important;"><code style="color: #000000;"></</code><code style="color: #006699; font-weight: bold;">LinearGradientBrush.RelativeTransform</code><code style="color: #000000;">></code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 12px;"><code>            </code><span style="margin-left: 36px !important;"><code style="color: #000000;"><</code><code style="color: #006699; font-weight: bold;">GradientStop</code> <code style="color: #808080;">Color</code><code style="color: #000000;">=</code><code style="color: blue;">"#FE739EAD"</code><code style="color: #000000;">/></code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 12px;"><code>            </code><span style="margin-left: 36px !important;"><code style="color: #000000;"><</code><code style="color: #006699; font-weight: bold;">GradientStop</code> <code style="color: #808080;">Color</code><code style="color: #000000;">=</code><code style="color: blue;">"#FFD3E9F9"</code> <code style="color: #808080;">Offset</code><code style="color: #000000;">=</code><code style="color: blue;">"1"</code><code style="color: #000000;">/></code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 12px;"><code>        </code><span style="margin-left: 24px !important;"><code style="color: #000000;"></</code><code style="color: #006699; font-weight: bold;">LinearGradientBrush</code><code style="color: #000000;">></code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 12px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #000000;"></</code><code style="color: #006699; font-weight: bold;">Path.Fill</code><code style="color: #000000;">></code></span></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 12px;"><code style="color: #000000;"></</code><code style="color: #006699; font-weight: bold;">Path</code><code style="color: #000000;">></code></span></div> </div> <p>It is nothing more than a path with some gradient brush on it. This results in the following, magnified about 20 times:</p> <p><img alt="" src="http://www.silverlightshow.net/Storage/Users/lnikolov/raindrop.png" /></p> <p>To simulate the drop falling I use a storyboard that:</p> <ol> <li>animates the height the drop is in each moment </li> <li>animate the visibility to collapsed when the desired height is reached, that is the drop has fallen through the whole scene </li> </ol> <div class="reCodeBlock" style="border:1px solid #7f9db9;overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 12px;"><code style="color: #000000;"><</code><code style="color: #006699; font-weight: bold;">Storyboard</code> <code style="color: #808080;">x:Name</code><code style="color: #000000;">=</code><code style="color: blue;">"Pour"</code><code style="color: #000000;">></code></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 12px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #000000;"><</code><code style="color: #006699; font-weight: bold;">DoubleAnimationUsingKeyFrames</code> <code style="color: #808080;">BeginTime</code><code style="color: #000000;">=</code><code style="color: blue;">"00:00:00"</code> <code style="color: #808080;">Storyboard.TargetName</code><code style="color: #000000;">=</code><code style="color: blue;">"drop"</code></span></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 12px;"><code style="color: #808080;">Storyboard.TargetProperty</code><code style="color: #000000;">=</code><code style="color: blue;">"(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)"</code><code style="color: #000000;">></code></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 12px;"><code>        </code><span style="margin-left: 24px !important;"><code style="color: #000000;"><</code><code style="color: #006699; font-weight: bold;">SplineDoubleKeyFrame</code> <code style="color: #808080;">KeyTime</code><code style="color: #000000;">=</code><code style="color: blue;">"00:00:00"</code> <code style="color: #808080;">Value</code><code style="color: #000000;">=</code><code style="color: blue;">"0"</code><code style="color: #000000;">/></code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 12px;"><code>        </code><span style="margin-left: 24px !important;"><code style="color: #000000;"><</code><code style="color: #006699; font-weight: bold;">SplineDoubleKeyFrame</code> <code style="color: #808080;">KeyTime</code><code style="color: #000000;">=</code><code style="color: blue;">"00:00:00.6"</code> <code style="color: #808080;">Value</code><code style="color: #000000;">=</code><code style="color: blue;">"{Binding FallHeight}"</code><code style="color: #000000;">/></code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 12px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #000000;"></</code><code style="color: #006699; font-weight: bold;">DoubleAnimationUsingKeyFrames</code><code style="color: #000000;">></code></span></span></div> <div style="background-color: #ffffff;"><span style="font-size: 12px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #000000;"><</code><code style="color: #006699; font-weight: bold;">ObjectAnimationUsingKeyFrames</code> <code style="color: #808080;">BeginTime</code><code style="color: #000000;">=</code><code style="color: blue;">"00:00:00"</code> <code style="color: #808080;">Storyboard.TargetName</code><code style="color: #000000;">=</code><code style="color: blue;">"drop"</code></span></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important; font-size: 12px;"><code style="color: #808080;">Storyboard.TargetProperty</code><code style="color: #000000;">=</code><code style="color: blue;">"(UIElement.Visibility)"</code><code style="color: #000000;">></code></span></div> <div style="background-color: #ffffff;"><span style="font-size: 12px;"><code>        </code><span style="margin-left: 24px !important;"><code style="color: #000000;"><</code><code style="color: #006699; font-weight: bold;">DiscreteObjectKeyFrame</code> <code style="color: #808080;">KeyTime</code><code style="color: #000000;">=</code><code style="color: blue;">"00:00:00.6"</code> <code style="color: #808080;">Value</code><code style="color: #000000;">=</code><code style="color: blue;">"Collapsed"</code> <code style="color: #000000;">/></code></span></span></div> <div style="background-color: #f8f8f8;"><span style="font-size: 12px;"><code>    </code><span style="margin-left: 12px !important;"><code style="color: #000000;"></</code><code style="color: #006699; font-weight: bold;">ObjectAnimationUsingKeyFrames</code><code style="color: #000000;">></code></span></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 12px;"><code style="color: #000000;"></</code><code style="color: #006699; font-weight: bold;">Storyboard</code><code style="color: #000000;">></code></span></div> </div> <p>To obtain the scene height I use binding to the DropSettings object that is injected in the constructor when the raindrop is created.</p> <p><strong></strong></p> <h2><strong> <h3>Let’s start raining!</h3> </strong></h2> <p>This was all you need to simulate rain. To actually start it you have to call the classes we created in a manner that satisfies our expectations.</p> <div class="reCodeBlock" style="border:1px solid #7f9db9;overflow-y: auto;"> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important; font-size: 12px;"><code style="color: #000000;">DropSettings dropSettings = </code><code style="color: #006699; font-weight: bold;">new</code> <code style="color: #000000;">DropSettings(300);</code></span></div> <div style="background-color: #f8f8f8;"><span style="margin-left: 0px !important; font-size: 12px;"><code style="color: #000000;">Rain rain = </code><code style="color: #006699; font-weight: bold;">new</code> <code style="color: #000000;">Rain(dropSettings, </code><code style="color: #006699; font-weight: bold;">this</code><code style="color: #000000;">.Scene, 40, 3, 400);</code></span></div> <div style="background-color: #ffffff;"><span style="margin-left: 0px !important;"><code style="color: #000000;"><span style="font-size: 12px;">rain.Start();</span></code></span></div> </div> <p><strong></strong></p> <h2><strong> <h3>Conclusion</h3> </strong></h2> <p>Although Silverlight is still very CPU consuming, in this article I’ve shown you how to use a simple algorithm to simulate rain. For all that we still should not expect miracles when it comes to real time particle simulations. But I hope soon we shall have what we wish to make our application look and behave really realistic.</p> <h3>About the author</h3> <p><img alt="" style="float: left; margin-right: 10px;" src="http://www.silverlightshow.net/Storage/lazar.png" />Lazar Nikolov is a software developer at <a href="http://www.completit.com/" target="_blank">CompletIT</a> - the company behind <a href="http://www.silverlightshow.net/">SilverlightShow.net</a> and leader in <strong>Silverlight</strong> related technologies in Bulgaria. His main interests are focused on Silverlight rich applications and <strong>Windows Phone 7</strong> development.</p> <p>In addition to authoring Silverlight articles (his series on ‘<a href="http://www.silverlightshow.net/items/Simulating-rain-in-Silverlight.aspx">Simulating rain in Silverlight</a>’ became one of the most popular articles on SilverlightShow!), Lazar is involved in the verification of all SilverlightShow articles, making sure that all content is consistent and in-line with the latest Silverlight/WP7 release updates.</p> http://www.silverlightshow.net/items/Simulating-rain-in-Silverlight.aspx editorial@silverlightshow.net (Lazar Nikolov ) http://www.silverlightshow.net/items/Simulating-rain-in-Silverlight.aspx#comments http://www.silverlightshow.net/items/Simulating-rain-in-Silverlight.aspx Mon, 14 Mar 2011 10:58:00 GMT XNA for Silverlight developers: Part 6 - Input (accelerometer) <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; padding-top: 5px; text-align: center;">Are you interested in <strong>creating games for Windows Phone 7</strong> but don't have XNA experience? <br /> <a href="http://www.silverlightshow.net/video/XNA-for-WP7-Webinar.aspx">Watch the recording</a> of the recent intro webinar delivered by Peter Kuhn '<strong>XNA for Windows Phone 7</strong>'.<a href="https://www1.gotomeeting.com/register/256344145" target="_blank"></a><br /> </div> <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; width: 196px; float: right; height: 401px; margin-left: 10px; margin-right: 5px; padding-top: 5px;"> <h3>Don't miss...</h3> <ul style="list-style-type: circle; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-left: 20px; font-size: 12px;"> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/WP7-What-is-Windows-Phone-7.aspx">What is Windows Phone series</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Exploring-Silverlight-XNA-integration-on-Windows-Phone-7.aspx">XNA article by Levente Mihály</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Windows-Phone-7-Part-1-Getting-Started.aspx">WP7 series by Andrea Boschin</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx">The XNA series is now available as a fully offline resource</a> </li> </ul> <p style="padding-bottom: 5px; text-align: center;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx"><img style="border:0px solid;" alt="XNA for Silverlight Developers Ebook" src="http://www.silverlightshow.net/storage/thumb_xna_ebook_cover_150_0.png" usemap="#rade_img_map_1291385581316" /></a></p> <p style="font-size: 12px;">     <a href="http://www.silverlightshow.net/ebooks.aspx">All SilverlightShow Ebooks</a> <img alt="" src="http://www.silverlightshow.net/Storage/arrow-content.jpg" /></p> </div> <p>This article is part 6 of the series "XNA for Silverlight developers".</p> <p><strong><strong>This article is compatible with Windows Phone "Mango".<em style="font-style: italic;"> <br /> Latest update: Aug 1, 2011.</em></strong></strong></p> <p>Last time we learned about touch input and gestures, which probably is the first choice for user input in most games on the phone. However, the hardware of Windows Phone 7 devices offer more possibilities in the form of additional sensors that can be used for input, in particular the accelerometer. Starting with "Mango", this will even be extended to sensors like the compass and gyroscope (if supported by the phone's hardware).</p> <p>For example, Microsoft's game "Kombo" that was available on the phone very early proved that using the accelerometer as primary input for a game is very well possible (give it a try, it's free). In this part of the series we will learn how you can use this type of data for input and take a peek at advanced topics and further resources. As always, you can download the source code at the end of the article.</p> <h3>Preface</h3> <p>With the accelerometer, for the first time in this series we are in the situation that we're working with something XNA does not have built-in support for. This is the reason there won't be a separate inspection of how you would do the same in Silverlight in this article, because it really is identical. For the XNA developer the result is that for once they got to work with events in conjunction with input data and can enjoy the comfort of being notified instead of having to poll. </p> <h3>Theory...</h3> <p>When you read values from the accelerometer, you will receive X, Y and Z values that correspond to the the following axes:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Accelerometer-axes_2.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px;" title="Accelerometer-axes" alt="Accelerometer-axes" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Accelerometer-axes_thumb.png" width="419" height="461" /></a></p> <p>That means: if you tilt the phone so the top edge comes up (like in the drawing), you will receive negative Y values. If you tilt it so the edge that holds the buttons comes up, the Y will become positive. Tilt it to the right, so the left side comes up, and you will receive positive X values, and finally if you tilt it to the left so the right side comes up, the X values will become negative. The Z axis provides negative values when the phone's front is facing up, and the values will be positive if you flip the phone so the front is facing down. </p> <p>All those values are provided as G-forces, which means that a value of 1.0 equals 9.81 m/s². This is the conventional standard value for gravitational acceleration on Earth (you can read more about it <a href="http://en.wikipedia.org/wiki/Gravitational_acceleration" target="_blank">on Wikipedia</a> if you're are not familiar with this). Ideally, if your phone lies flat on a leveled surface, you would receive values of zero for both X and Y, and -1.0 for the Z value. If the phone stands on its left edge, you would receive X = -1.0 and Y and Z both as zero. And finally if it stands on the edge where the buttons sit, the values should be Y = -1.0 and zero for both X and Z.</p> <h3>... and practice</h3> <p>In practice, you will see that the accelerometer is a very sensitive source of input. Even the slightest change produces instant reaction, and due to the nature of the sensor hardware and physics, even if the phone is not moved at all it is likely that you will see a constant noise level of changing values.</p> <p>In addition, the reported values also depend on your location, in particular your current location's latitude (because the Earth is not a sphere but an ellipsoid) and the height above sea level. You'll most likely never see the ideal steady value of (-)1.0. For example, when I lie my phone down on what seems a perfectly leveled table, I see a Z value that constantly jumps between -0.97 and -0.99. Both the X and Y values jump equally in the range of 0.02-0.03. This is something you must accommodate for in your applications and games, for example by using a threshold or smoothing of the values (see below).</p> <h3>Setup</h3> <p>To use the accelerometer, an additional assembly reference is needed for your project, because it is not included by default when you create a new game solution. In particular, Microsoft.Devices.Sensors is required:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/____image_4.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/____image_thumb_1.png" width="493" height="405" /></a></p> <p>The next step is to create an instance of the <a href="http://msdn.microsoft.com/en-us/library/microsoft.devices.sensors.accelerometer(v=vs.92).aspx" target="_blank">Accelerometer class</a> that can be found in the same namespace. This class has a single event <a href="http://msdn.microsoft.com/en-us/library/microsoft.devices.sensors.accelerometer.readingchanged(v=vs.92).aspx" target="_blank">ReadingChanged</a> that is raised when new data is available. This event is raised 50 times a second, so it produces a lot of data. Because of this there's also a mechanism to turn reporting of the input data on and off, so you have the possibility to only activate it when you really need it. For example, if your game is paused or currently displaying a menu screen or similar, you should pause the accelerometer input. This can be done using the <a href="http://msdn.microsoft.com/en-us/library/microsoft.devices.sensors.accelerometer.start(v=VS.92).aspx" target="_blank">Start</a> and <a href="http://msdn.microsoft.com/en-us/library/microsoft.devices.sensors.accelerometer.stop(v=VS.92).aspx" target="_blank">Stop</a> methods of the class.</p> <p>The whole setup then looks like this:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="rem">// fields for the accelerometer</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> <span class="kwrd">private</span> Accelerometer _accelerometer;</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> <span class="kwrd">private</span> Vector3 _lastAccelerometerData;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> ...</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> <span class="rem">// create the accelerometer instance,</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> <span class="rem">// e.g. in your game's constructor;</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span> <span class="rem">// hook the ReadingChanged event</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span> _accelerometer = <span class="kwrd">new</span> Accelerometer();</pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span> _accelerometer.ReadingChanged += </pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> 12:</span> <span class="kwrd">new</span> EventHandler<AccelerometerReadingEventArgs>(Accelerometer_ReadingChanged);</pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> 13:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> 14:</span> ...</pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> 15:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> 16:</span> <span class="kwrd">private</span> <span class="kwrd">void</span> Accelerometer_ReadingChanged(<span class="kwrd">object</span> sender, AccelerometerReadingEventArgs e)</pre> <!--CRLF--> <pre class="alt"><span id="lnum17" class="lnum"> 17:</span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum18" class="lnum"> 18:</span> <span class="rem">// simply store the reading as new vector</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum19" class="lnum"> 19:</span> _lastAccelerometerData = <span class="kwrd">new</span> Vector3((<span class="kwrd">float</span>)e.X, (<span class="kwrd">float</span>)e.Y, (<span class="kwrd">float</span>)e.Z);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum20" class="lnum"> 20:</span> }</pre> <!--CRLF--> <pre class="alt"><span id="lnum21" class="lnum"> 21:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum22" class="lnum"> 22:</span> ...</pre> <!--CRLF--> <pre class="alt"><span id="lnum23" class="lnum"> 23:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum24" class="lnum"> 24:</span> <span class="rem">// for example in your game's Initialize method,</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum25" class="lnum"> 25:</span> <span class="rem">// start the accelerometer data collection</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum26" class="lnum"> 26:</span> <span class="kwrd">try</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum27" class="lnum"> 27:</span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum28" class="lnum"> 28:</span> _accelerometer.Start();</pre> <!--CRLF--> <pre class="alt"><span id="lnum29" class="lnum"> 29:</span> }</pre> <!--CRLF--> <pre class="alteven"><span id="lnum30" class="lnum"> 30:</span> <span class="kwrd">catch</span> (Exception)</pre> <!--CRLF--> <pre class="alt"><span id="lnum31" class="lnum"> 31:</span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum32" class="lnum"> 32:</span> <span class="rem">// this can fail if the sensor is not available etc.</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum33" class="lnum"> 33:</span> }</pre> <!--CRLF--></div> </div> <p>Aside from the X, Y and Z values, the <a href="http://msdn.microsoft.com/en-us/library/microsoft.devices.sensors.accelerometerreadingeventargs(v=vs.92).aspx" target="_blank">AccelerometerReadingEventArgs</a> also have a property "Timestamp" that is a DateTimeOffset and indicates the time at which the reading was taken. This can be of interest if you are analyzing the change of these values over time instead of taking a momentarily snapshot.</p> <h4>Some important notes about "Mango"</h4> <p>The above mentioned ReadingChanged event is marked as obsolete in the Windows Phone "Mango" release. A new event named <a href="http://msdn.microsoft.com/en-us/library/hh239103(v=vs.92).aspx">CurrentValueChanged</a> has been added to replace the old one. The reason for this change is that the class hierarchy for sensor classes has been changed by the update. In the RTM release of Windows Phone, the accelerometer was a stand-alone class with no base class. In Mango, we now have a generic <a href="http://msdn.microsoft.com/en-us/library/hh239315(v=VS.92).aspx">SensorBase class</a> that is the base class for all other sensor classes of the phone (also the compass, gyroscope etc.). The new hierarchy looks like this:  </p> <ul> <li><span style="color: #0000ff;">public abstract class</span> SensorBase<TSensorReading> : IDisposable <span style="color: #0000ff;">where</span> TSensorReading : ISensorReading</li> <ul> <li><span style="color: #0000ff;">public sealed class</span> Accelerometer : SensorBase<AccelerometerReading></li> <li><span style="color: #0000ff;">public sealed class</span> Compass : SensorBase<CompassReading></li> <li><span style="color: #0000ff;">public sealed class</span> Gyroscope : SensorBase<GyroscopeReading></li> <li><span style="color: #0000ff;">public sealed class</span> Motion : SensorBase<MotionReading></li> </ul> </ul> <p>The SensorBase class now is the place that provides general methods, properties and an event for the data retrieval, in particular:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="kwrd">virtual</span> <span class="kwrd">void</span> Start();</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> <span class="kwrd">virtual</span> <span class="kwrd">void</span> Stop();</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> TSensorReading CurrentValue { get; }</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> <span class="kwrd">bool</span> IsDataValid { get; }</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> TimeSpan TimeBetweenUpdates { set; get; }</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> <span class="kwrd">event</span> EventHandler<SensorReadingEventArgs<TSensorReading>> CurrentValueChanged</pre> <!--CRLF--></div> </div> <p>In this process of refactoring, the existing accelerometer API has been streamlined to fit these changes. Make sure you start using the new event when you target the Mango release with your game or application. Consequently, the received <a href="http://msdn.microsoft.com/en-us/library/ff403534(v=VS.92).aspx">arguments</a> for this event also are slightly different too. But they still provide the same data as in the RTM release; after all, the nature of the sensor hasn't changed.</p> <p>Another thing that is worth noting is the above listed <a href="http://msdn.microsoft.com/en-us/library/hh220884(v=vs.92).aspx">TimeBetweenUpdates</a> property of the SensorBase class. In Mango you have the possibility to influence the interval at which values are reported by changing that property. A limitation of this is that there might be differences in the capabilities of different sensors and devices. Your desired interval may not be possible to achieve, and the actual value used may be different from what you set this property to. You can query the property after you set an interval to see if changing the value actually worked.</p> <h3>Making use of the data</h3> <p>In this short example, I want to move a ball around the phone screen by using the accelerometer input. To achieve that, I add the X and Y values from the sensor to the current velocity of the ball. To work around the problem of the basic noise level that is always present even when the phone lies still, I'm using a threshold the values need to exceed to actually be considered for processing. For all this, I declare the following fields:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="rem">// the ball and its location + velocity</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> <span class="kwrd">private</span> Texture2D _ball;</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> <span class="kwrd">private</span> Vector2 _location; </pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> <span class="kwrd">private</span> Vector2 _velocity;</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> <span class="rem">// some limits to keep the ball on screen</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> <span class="rem">// and to not let it become too fast</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> <span class="kwrd">private</span> Vector2 _ballMaxLocation;</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span> <span class="kwrd">private</span> Vector2 _ballMaxVelocity;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span> <span class="rem">// the threshold for the accelerometer readings</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> 12:</span> <span class="rem">// all values lower than that are ignored</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> 13:</span> <span class="kwrd">private</span> <span class="kwrd">const</span> <span class="kwrd">float</span> Threshold = 0.1f;</pre> <!--CRLF--></div> </div> <p>The limit for the location of course are the screen bounds, and for the maximum velocity I simply chose an arbitrary value. Now let's take a look at the "Update" method of the game that simply takes the last sample from the accelerometer to modify the velocity:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="rem">// update the ball's velocity with the accelerometer values</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> <span class="kwrd">if</span> (Math.Abs(_lastAccelerometerData.Y) > Threshold)</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> _velocity.X -= _lastAccelerometerData.Y;</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> }</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> <span class="kwrd">if</span> (Math.Abs(_lastAccelerometerData.X) > Threshold)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span> _velocity.Y -= _lastAccelerometerData.X;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span> }</pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> 12:</span> <span class="rem">// limit the velocity to the maximum</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> 13:</span> _velocity = Vector2.Clamp(_velocity, -_ballMaxVelocity, _ballMaxVelocity);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> 14:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> 15:</span> <span class="rem">// add some "friction" to the ball's movement</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> 16:</span> _velocity = _velocity * 0.95f;</pre> <!--CRLF--></div> </div> <p>There's something really important to note about the first lines of code here. If you inspect them carefully, you can see that I'm subtracting the Y value of the accelerometer data from the X value of the velocity, and similarly use the X value for the Y component.</p> <p>The reason for this is that in the sample I'm using landscape orientation (LandscapeLeft to be more precise). As my initial graphic shows, the accelerometer coordinate system uses a certain alignment of the axes relative to the device. Unlike the coordinate system of your game, which actually is aligned with respect to the current screen orientation, not the hardware of the phone, the accelerometer's coordinate system is fixed and always stays the same. This means that as long as you are using portrait mode your game's coordinate system axes and the accelerometer's axes are aligned in the the same way, but when you switch to landscape mode, you actually have to swap X and Y coordinates. Even more, you also have to perform a reversal of the sign on the retrieved values, depending on whether you are in left or right landscape mode.</p> <p>A potentially cleaner solution probably would be to make this distinction in the reading changed event handler already. You could encapsulate the reading in a helper class or similar and swap the values dynamically depending on the current orientation there. That way you'd have a single point where this logic is implemented and could work with consistent values throughout the rest of your application to avoid confusion.</p> <p>The last line of the above update code adds an artificial slowdown of the ball over time to simulate friction as it moves over the virtual surface. It's just an attempt to add a bit of realism to the behavior, because without that the ball would feel like moving on ice or a mirror.</p> <p>Let's take a look at the rest of the Update method:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="rem">// clamp the location</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> var clampedLocation = Vector2.Clamp(_location, Vector2.Zero, _ballMaxLocation);</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> <span class="kwrd">if</span> (clampedLocation != _location)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> <span class="rem">// reset the velocity if the ball has hit a wall</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> <span class="kwrd">if</span> (clampedLocation.X != _location.X)</pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> _velocity.X = 0.0f;</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span> }</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span> <span class="kwrd">if</span> (clampedLocation.Y != _location.Y)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> 12:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> 13:</span> _velocity.Y = 0.0f;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> 14:</span> }</pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> 15:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> 16:</span> <span class="rem">// set the corrected location</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum17" class="lnum"> 17:</span> _location = clampedLocation;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum18" class="lnum"> 18:</span> }</pre> <!--CRLF--></div> </div> <p>All we do here is make sure the ball doesn't move off-screen and reset the velocity if any of the edges is hit.</p> <p>The "Draw method" draws the ball at the current location and also outputs the last accelerometer readings as well as the ball's velocity to visualize how everything works together.</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> _spriteBatch.Begin();</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> <span class="rem">// draw ball</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> _spriteBatch.Draw(_ball, _location, Color.White);</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span> <span class="rem">// draw readings</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> <span class="kwrd">string</span> readings = <span class="kwrd">string</span>.Format(<span class="str">"Accelerometer readings: X:{0}, Y:{1}, Z:{2}"</span>,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> _lastAccelerometerData.X.ToString(<span class="str">"0.00"</span>),</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span> _lastAccelerometerData.Y.ToString(<span class="str">"0.00"</span>),</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span> _lastAccelerometerData.Z.ToString(<span class="str">"0.00"</span>));</pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span> _spriteBatch.DrawString(_font, readings, Vector2.Zero, Color.White);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> 12:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> 13:</span> <span class="rem">// draw velocity</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> 14:</span> Vector2 readingsSize = _font.MeasureString(readings);</pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> 15:</span> <span class="kwrd">string</span> velocity = <span class="kwrd">string</span>.Format(<span class="str">"Ball velocity: X:{0}, Y:{1}"</span>,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> 16:</span> _velocity.X.ToString(<span class="str">"0.00"</span>),</pre> <!--CRLF--> <pre class="alt"><span id="lnum17" class="lnum"> 17:</span> _velocity.Y.ToString(<span class="str">"0.00"</span>));</pre> <!--CRLF--> <pre class="alteven"><span id="lnum18" class="lnum"> 18:</span> _spriteBatch.DrawString(_font, velocity, <span class="kwrd">new</span> Vector2(0.0f, readingsSize.Y), Color.White);</pre> <!--CRLF--> <pre class="alt"><span id="lnum19" class="lnum"> 19:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum20" class="lnum"> 20:</span> _spriteBatch.End();</pre> <!--CRLF--></div> </div> <p>And here is what the final result looks like on the device:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Phone_2.jpg"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border-width: 0px;" title="Phone" alt="Phone" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Phone_thumb.jpg" width="600" height="343" /></a></p> <h3>Possible problems and pitfalls</h3> <h4>The emulator</h4> <p>When you are working with the RTM release of Windows Phone, the sensor is not present in the emulator. In that case you are either forced to use a real device whenever you want to test an application or game, or revert to a custom solution to simulate the sensor, for example by mapping some keys to accelerometer input. Over time people also came up with a lot of clever external tools for this problem, including hardware solutions that make use of other accelerometer devices and route the data to the phone. There are some software projects available that use more comfortable client applications to simulate the accelerometer input and let the phone retrieve that simulated input through a web service or similar. One of these projects is the <a href="http://www.codeproject.com/KB/windows-phone-7/WP7AccelerometerEmulator.aspx" target="_blank">WP7 Accelerometer Emulator project</a>, and a similar one can be found on Codeplex as the <a href="http://wp7accelerometerkit.codeplex.com/" target="_blank">Simulator Kit</a>. These solutions can speed up and simplify your development process if you don't have access to a real device all the time or don't want to plug it in for each test.</p> <p>If you are developing for Windows Phone "Mango", things get a lot more easier and you don't have to use third-party solutions anymore. The improved emulator of the new development tools now has built-in support for the accelerometer and allows you to easily simulate and generate that kind of input data in a nice way:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_________image_2.png"><img style="border:0px; background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/__________image_thumb.png" width="540" height="480" /></a></p> <p>As you can see a visual representation of the phone is used, and by moving around the red dot you can change the orientation of the device in space, which produces accelerometer data accordingly, without the need to change anything in your code. That is a comfortable and easy way to test your code in the emulator too:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/________image_4.png"><img style="border:0px; background-image: none; margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_________image_thumb_1.png" width="640" height="384" /></a></p> <h4>Data noise</h4> <p>As I've mentioned multiple times, the data received from the sensor is really noisy. Here is a sample reading of me tilting the phone to the left, first slowly, then in a sudden movement and back:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/____image_6.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/____image_thumb_2.png" width="636" height="459" /></a></p> <p>In our example we could accommodate for that by using a simple threshold that filtered all the "ambient noise". Once the threshold was exceeded, the noise didn't hurt anymore. However, in other scenarios you might want to react to <em>changes</em> of the sensor values over time rather than process the momentarily values as we did here. This often requires to determine a trend in these values that ignores temporary spikes, for example. In these cases you will most likely want to perform some smoothing on the data, which in turn can introduce a new set of problems, like latency. Luckily you don't have to fight through those basics of signal processing from scratch all by yourself again. Dave Edson and Greg Hogdal from the Windows Phone 7 Applications Platform team have written <a href="http://windowsteamblog.com/windows_phone/b/wpdev/archive/2010/09/08/using-the-accelerometer-on-windows-phone-7.aspx" target="_blank">an excellent article on the topic</a> and introduce some helper classes that do the heavy lifting for you.</p> <h4>Screen saver</h4> <p>For power saving reasons, the Windows Phone 7 devices turn off the screen after a while when no user input happens. In this sense, accelerometer input is not considered user input. Obviously the device cannot distinguish between deliberate tilting of the phone in a game and e.g. changes in orientation that happen because the user carries the phone in their pocket or because they gesticulate in a discussion and are not actually using the phone. You can turn off the screen saver using the <a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.gamerservices.guide.isscreensaverenabled.aspx" target="_blank">IsScreenSaverEnabled property</a> of the Guide class to avoid that:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="rem">// disable automatic turning off</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> <span class="rem">// due to no user touch input</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> Guide.IsScreenSaverEnabled = <span class="kwrd">false</span>;</pre> <!--CRLF--></div> </div> <h3>Further accelerometer resources</h3> <p>An interesting sample application with free source is the "<a href="http://create.msdn.com/en-US/education/catalog/sample/level_starter_kit" target="_blank">Level Starter Kit</a>" that can be found in the education section of the app hub. It has the same helper classes in it as the source code of the article on data smoothing linked above and creates a virtual bubble level to demonstrate the use of the accelerometer.</p> <p>Another cool library is the recently released <a href="http://create.msdn.com/en-us/education/catalog/article/Recipe_Shake_Gesture_Library" target="_blank">Shake Gesture Library</a> that provides a high-level abstraction of "shake flicks" performed by the user. <a href="http://windowsteamblog.com/windows_phone/b/wpdev/archive/2011/02/22/windows-phone-shake-gestures-library.aspx" target="_blank">This post</a> on the Windows Phone Developer Blog gives a short introduction and explanation of the concept.</p> <h3>Additional sensors and combined motion API</h3> <p>With Windows Phone "Mango", developers will have access to additional sensors, in particular the compass and gyroscope. Unlike with the accelerometer, which was a requirement for all devices for the RTM release of Windows Phone, not all devices in the market will have a gyroscope, for example. So if you build an application or game that relies on a gyroscope, some users won't be able to use your application (they will receive a warning in the market place when they browse to your application). This is something you need to accommodate for – make sure the sensor you're trying to use is actually available on the current device to avoid errors.</p> <p>Since the API for sensors has been streamlined in Mango (see above), all of them work in a similar way. For example, making use of the compass very much resembles what has been written above for the accelerometer:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> 1:</span> <span class="rem">// make sure we have a compass</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> 2:</span> <span class="kwrd">if</span> (Compass.IsSupported)</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> 3:</span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> 4:</span> <span class="rem">// create a new compass</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> 5:</span> Compass _compass = <span class="kwrd">new</span> Compass();</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> 6:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> 7:</span> <span class="rem">// set the desired interval for value readings</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> 8:</span> _compass.TimeBetweenUpdates = TimeSpan.FromMilliseconds(100.0);</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> 9:</span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> 10:</span> <span class="rem">// use the new event for value readings</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> 11:</span> _compass.CurrentValueChanged += <span class="kwrd">new</span> EventHandler<SensorReadingEventArgs<CompassReading>>(Compass_CurrentValueChanged);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> 12:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> 13:</span> <span class="rem">// start reading</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> 14:</span> _compass.Start();</pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> 15:</span> }</pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> 16:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum17" class="lnum"> 17:</span> ...</pre> <!--CRLF--> <pre class="alteven"><span id="lnum18" class="lnum"> 18:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum19" class="lnum"> 19:</span> <span class="kwrd">private</span> <span class="kwrd">void</span> Compass_CurrentValueChanged(<span class="kwrd">object</span> sender, SensorReadingEventArgs<CompassReading> e)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum20" class="lnum"> 20:</span> {</pre> <!--CRLF--> <pre class="alt"><span id="lnum21" class="lnum"> 21:</span> <span class="rem">// the actual data is in the "SensorReading" property,</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum22" class="lnum"> 22:</span> <span class="rem">// which is a "CompassReading" object in this case</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum23" class="lnum"> 23:</span> var data = e.SensorReading;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum24" class="lnum"> 24:</span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum25" class="lnum"> 25:</span> <span class="rem">// do something with the read data...</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum26" class="lnum"> 26:</span> }</pre> <!--CRLF--></div> </div> <p>A special feature of the new sensor API is the <a href="http://msdn.microsoft.com/en-us/library/microsoft.devices.sensors.motion(v=VS.92).aspx">Motion class</a>. This class combines the data of the compass, the accelerometer and gyroscope to accurately perform an analysis of the device location and its orientation in space. This is a convenient wrapper around some sophisticated math to compute that data, so you don't have to do this manually. The beauty of this is that the Motion class also inherits from the SensorBase class and hence can be used like any other sensor – this makes it very easy to get complete sensor data information on the phone. </p> <h3>Summary</h3> <p>I hope this article has made your mouth water for this alternative input method. Already existing games and applications in the market place that use this technique prove that using it in real world scenarios is not only feasible but can be a valuable addition and even create a completely new user experience. The performance and quality of the returned data even make sophisticated features like the recognition of shake patterns possible. If you have any questions or requests, feel free to leave a comment or contact me directly.</p> <p><a href="http://www.silverlightshow.net/Storage/Sources/XNA_for_Silverlight_developers_Part6.zip">Download source code</a></p> <h3>About the author</h3> <p>Peter Kuhn aka "Mister Goodcat" has been working in the IT industry for more than ten years. After being a project lead and technical director for several years, developing database and device controlling applications in the field of medical software and bioinformatics as well as for knowledge management solutions, he founded his own business in 2010. Starting in 2001 he jumped onto the .NET wagon very early, and has also used Silverlight for business application development since version 2. Today he uses Silverlight, .NET and ASP.NET as his preferred development platforms and offers training and consulting around these technologies and general software design and development process questions.</p> <p>He created his first game at the age of 11 on the Commodore 64 of his father and has finished several hobby and also commercial game projects since then. His last commercial project was the development of Painkiller:Resurrection, a game published in late 2009, which he supervised and contributed to as technical director in his free time. You can find his tech blog here: <a href="http://www.pitorque.de/MisterGoodcat/">http://www.pitorque.de/MisterGoodcat/</a></p> http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-6-Input-accelerometer.aspx editorial@silverlightshow.net (Peter Kuhn ) http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-6-Input-accelerometer.aspx#comments http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-6-Input-accelerometer.aspx Wed, 09 Mar 2011 07:31:00 GMT XNA for Silverlight developers: Part 5 - Input (touch + gestures) <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; padding-top: 5px; text-align: center;">Are you interested in <strong>creating games for Windows Phone 7</strong> but don't have XNA experience? <br /> <a href="http://www.silverlightshow.net/video/XNA-for-WP7-Webinar.aspx">Watch the recording</a> of the recent intro webinar delivered by Peter Kuhn '<strong>XNA for Windows Phone 7</strong>'.<a href="https://www1.gotomeeting.com/register/256344145" target="_blank"></a><br /> </div> <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; width: 196px; float: right; height: 401px; margin-left: 10px; margin-right: 5px; padding-top: 5px;"> <h3>Don't miss...</h3> <ul style="list-style-type: circle; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-left: 20px; font-size: 12px;"> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/WP7-What-is-Windows-Phone-7.aspx">What is Windows Phone series</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Exploring-Silverlight-XNA-integration-on-Windows-Phone-7.aspx">XNA article by Levente Mihály</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Windows-Phone-7-Part-1-Getting-Started.aspx">WP7 series by Andrea Boschin</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx">The XNA series is now available as a fully offline resource</a> </li> </ul> <p style="padding-bottom: 5px; text-align: center;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx"><img style="border:0px solid;" alt="XNA for Silverlight Developers Ebook" src="http://www.silverlightshow.net/storage/thumb_xna_ebook_cover_150_0.png" usemap="#rade_img_map_1291385581316" /></a></p> <p style="font-size: 12px;">     <a href="http://www.silverlightshow.net/ebooks.aspx">All SilverlightShow Ebooks</a> <img alt="" src="http://www.silverlightshow.net/Storage/arrow-content.jpg" /></p> </div> <p>This article is part 5 of the series "XNA for Silverlight developers".</p> <p><strong>This article is compatible with Windows Phone "Mango". </strong><em style="font-style: italic; font-weight: bold;"><br /> Latest update: Aug 1, 2011.</em></p> <p>User input on a mobile device is always an interesting topic, because it's usually so much different from normal desktop applications on a conceptual level already. You have no pointing device like a mouse, which eliminates some possibilities altogether (hovering or right-click, for example), but on the other hand an all new class of features is added through modern technology, like multi-touch gestures and additional sensors (like accelerometers). </p> <p>I've already talked about the theory and reasons of "event-driven vs. polling" regarding input in the <a href="http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-1-Fundamentals.aspx" target="_blank">first part</a> of the series, and I encourage you to take another look at this paragraph to brush up on the topic once again. This article will mostly skip over these theoretical parts and take a closer look at how you actually make use of user input and dive into the involved code.</p> <h3>Silverlight</h3> <p>A Silverlight app on the phone very much works like the desktop version regarding input. You usually have some sort of control that exposes various user interaction events. You then add event handlers to those events you're interested in, and when the user action happens, your event handler is invoked automatically by the runtime. The following shows the button click event as an example.</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Silverlight-events_4.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="Silverlight-events" alt="Silverlight-events" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Silverlight-events_thumb_1.png" width="400" height="280" /></a></p> <p>The crucial part is not the way you add your event handlers, i.e. whether you use XAML to do that or code-behind. You might even use a completely different approach, for example in an application designed with the MVVM pattern where you use commands instead of events. The important detail is that your application is in the passive role here. It just sits there, waits and does nothing until something of interest happens. It reacts to events the runtime manages and pushes towards you.</p> <p>This is even true if you are not interested in input events for a certain control, but want to integrate with e.g. gestures. To do this you would use the gesture listener provided by the Silverlight Toolkit for Windows Phone. It raises events you can listen to whenever gestures are detected, hence it features the same programming model for this scenario too. If you want to learn more about the gesture listener in Silverlight, you can read <a href="http://www.pitorque.de/MisterGoodcat/post/Windows-Phone-7-Pinch-Gesture-Sample.aspx" target="_blank">one of my blog posts</a> were I give more explanations and a sample with source code.</p> <h3>XNA</h3> <p>In XNA the fundamental concept of how you acquire user input is completely different. All of a sudden, your code is in the active role and has to ask the runtime about user input. If you don't ask, you'll never take notice. One of the central classes for input in XNA on the phone is <a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.input.touch.touchpanel.aspx" target="_blank">TouchPanel</a>. It is a static class that only has a hand full of properties and methods which basically let you set up the input features as well as retrieve all input data that is available through the touch screen of a device. The required steps are:</p> <ul> <li>Tell the touch panel what kind of input you are interested in (this is a one time thing or needs only be changed when you want to be notified of different input data in different contexts, see below). </li> <li>Ask the touch panel if input data is available (you have to do this regularly/in each frame). </li> <li>Retrieve and process the data. </li> </ul> <p>Additionally you can ask the touch panel for its capabilities, but since all Windows Phone 7 devices report the same feature set, that currently is of little use on this platform. Let's see how this translates into code:</p> <h4>Enabling gestures</h4> <p>Enabling gestures is very simple. There is a <a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.input.touch.gesturetype.aspx" target="_blank">gesture type enumeration</a> with the different types of gestures that are supported on the device, and you can pass one or more of these values to the static "<a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.input.touch.touchpanel.enabledgestures.aspx" target="_blank">EnabledGestures</a>" property of the touch panel. This is something you typically do in your game class constructor for simple games; in most projects however you will work with a concept of different "screens" (for example for menus, the actual game, supplementary content like high-scores etc.). In these cases you can re-initialize this property for each screen that is shown to the user to optimize for only those gestures that you actually need in a particular context.</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span><span class="rem">// enable gestures</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span>TouchPanel.EnabledGestures = GestureType.Tap | GestureType.Flick;</pre> <!--CRLF--></div> </div> <p>Tap is the simplest gesture available and denotes that the user briefly touches a single point on the screen. You can think of it as the touch counterpart of a mouse click. When you look at the other enumeration values you can see that all of the common gestures are available:</p> <ul> <li><strong>Tap, DoubleTap, Hold:</strong> involve touching a single point on the screen and are usually used to invoke some action like "clicking" or selecting something. </li> <li><strong>HorizontalDrag, VerticalDrag, FreeDrag, DragComplete:</strong> These are the gestures that are a combination of touching and dragging on the screen. You can use these to move objects around the screen, or for scrolling, panning and similar actions. </li> <li><strong>Pinch, PinchComplete:</strong> Explained as "two-finger dragging" in the documentation. This is normally used for features like zooming. </li> <li><strong>Flick:</strong> A special gesture that is positionless. It describes quick swipes on the screen that often are used for flipping through items or pages etc. </li> </ul> <h3></h3> <h4>Reading gestures</h4> <p>You have to check for available gesture data in each call of the "Update" method in your game, or you might miss gesture data. The following code shows how to do this to retrieve individual <a href="http://msdn.microsoft.com/en-us/library/ff827690.aspx" target="_blank">GestureSample</a> objects which contain the actual details about the detected gesture:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span><span class="rem">// check whether gestures are available</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span><span class="kwrd">while</span> (TouchPanel.IsGestureAvailable)</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> </span>{</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> </span> <span class="rem">// read the next gesture</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> </span> var gesture = TouchPanel.ReadGesture();</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> </span> </pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> </span> <span class="rem">// has the user tapped the screen?</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> </span> <span class="kwrd">if</span> (gesture.GestureType == GestureType.Tap)</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> </span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> </span> <span class="rem">// move the sprite to the tap position </span></pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> </span> _position = <span class="kwrd">new</span> Vector2(gesture.Position.X - _sprite.Width / 2.0f,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> </span> gesture.Position.Y - _sprite.Height / 2.0f);</pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> </span> }</pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> </span> <span class="kwrd">else</span> <span class="kwrd">if</span> (gesture.GestureType == GestureType.Flick)</pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> </span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> </span> <span class="rem">// add a fraction of the flick delta (= velocity) to the position</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum17" class="lnum"> </span> _position += gesture.Delta * 0.1f;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum18" class="lnum"> </span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum19" class="lnum"> </span> <span class="rem">// make sure the sprite doesn't move off screen</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum20" class="lnum"> </span> var viewPort = _graphics.GraphicsDevice.Viewport;</pre> <!--CRLF--> <pre class="alt"><span id="lnum21" class="lnum"> </span> var min = <span class="kwrd">new</span> Vector2(-_sprite.Width / 2.0f, -_sprite.Height / 2.0f);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum22" class="lnum"> </span> var max = <span class="kwrd">new</span> Vector2(viewPort.Width - _sprite.Width / 2.0f, viewPort.Height - _sprite.Height / 2.0f);</pre> <!--CRLF--> <pre class="alt"><span id="lnum23" class="lnum"> </span> _position = Vector2.Clamp(_position, min, max);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum24" class="lnum"> </span> }</pre> <!--CRLF--> <pre class="alt"><span id="lnum25" class="lnum"> </span>}</pre> <!--CRLF--></div> </div> <p>You can ask the touch panel for available gestures using the "IsGestureAvailable" property. Please note that multiple gesture samples can be available, which is why I'm using a while loop here to make sure I retrieve all of them. Next you retrieve the gesture sample by calling "ReadGesture" on the touch panel. From there, you simply check for the gesture type that was detected and process the data accordingly. </p> <p>As you can see, all the different gestures work with the same data structures. But not all the properties of a gesture sample are meaningful for every gesture type. You have to refer to the above linked documentation to decide what pieces of information really make sense for a certain gesture. For example, in the above code I use "Position" for the tap and the "Delta" property for flicks; since flicks are positionless, you cannot use the "Position" property with them.</p> <h4>Conclusion</h4> <p>This sample shows how the major difference between XNA and Silverlight regarding input translates to code, and how your game actively has to ask for input data instead of receiving that information through events.</p> <h4></h4> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/XNA-Input_2.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="XNA-Input" alt="XNA-Input" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/XNA-Input_thumb.png" width="291" height="345" /></a></p> <p>The attentive reader notices another detail here. Where you can target a particular element in Silverlight and have notifications send to your application only for this element, the XNA approach is way more generic. The filtering for useful information has to happen in your application. That means that once you have the information that the user has touched the screen, you have to decide yourself if this is of any use for you, for example if the location the touch panel has reported belongs to some element on the screen that should react to user input. In Silverlight, if a click event like above happens, you already know that it belongs to the button and what action to invoke as a result, and you also will never receive that button click event when the user clicks outside the button bounds.</p> <h3>A simplified approach</h3> <p>What surprisingly many XNA developers do not know is that there's a simpler way to get access to input touch data for simple scenarios through the <a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.input.mouse.aspx" target="_blank">Mouse</a> class. Even though there's no such thing as a mouse on the phone, the library developers have mapped the primary touch point data to what corresponds to the left mouse button on the desktop. This means that if you're only interested in tap data, you can use something like this in your Update method:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span><span class="rem">// get the "mouse" state</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span>var mouseState = Mouse.GetState();</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> </span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> </span><span class="rem">// the left button press is equal to touching the screen</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> </span><span class="kwrd">if</span> (mouseState.LeftButton == ButtonState.Pressed)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> </span>{</pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> </span> <span class="rem">// move the sprite to the "mouse click" position </span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> </span> _position = <span class="kwrd">new</span> Vector2(mouseState.X - _sprite.Width / 2.0f,</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> </span> mouseState.Y - _sprite.Height / 2.0f);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> </span>}</pre> <!--CRLF--></div> </div> <p>Note that the "Pressed" state of the left mouse button is the only constellation where this class returns meaningful data for the X and Y coordinates. More details on this can be found <a href="http://msdn.microsoft.com/en-us/library/bb197572.aspx" target="_blank">here</a>. Since this data is also updated when the user moves their fingers around the screen, you can even create simple dragging functionality using this approach.</p> <h3>Taking a deeper dive</h3> <p>Working with gestures already is an abstraction of what actually happens with the touch panel. Flicking or dragging is only an interpretation of a series of low-level touch samples that happen over time to make it easier for developers to work with these common gestures. I you want, you can make use of these low-level samples for your own scenarios. Why would you want to do that? Well, every Windows Phone 7 touch screen is capable of handling up to four touch points at the same time. The built-in convenience wrappers for gestures only make use of up to two points (pinching). So digging deeper into the touch input features allows you not only to create your own gesture types, but also to make use of this fact and create new and innovative input concepts that go way beyond the usual. Let's see how that works.</p> <p>In your "Update" method, call the "GetState" method on the touch panel to retrieve a collection of touch locations. Each of these locations has the following properties:</p> <ul> <li><strong>Id</strong>: This value is very important for the tracking of successive samples that belong to the same logical touch action. Each time a finger touches the screen, a new id is generated. This id will stay the same for all of the following touch samples of this finger until the user removes it from the screen. Because you will retrieve multiple touch locations when the user puts multiple fingers on the screen, this is the value to determine which touch locations belong to the same finger over a period of time. </li> <li><strong>Position</strong>: This obviously is the position of the touch point as 2D vector. </li> <li><strong>State</strong>: One of the possible <a href="http://msdn.microsoft.com/en-us/library/ff434544.aspx" target="_blank">touch location state enum</a> values. New touch locations have a state of "Pressed", locations that were existing before have "Moved". Please note that you'll receive "Moved" location info even if the finger has not really moved physically. "Released" happens when the finger is removed, and "Invalid" should only occur when you try to use the <a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.input.touch.touchlocation.trygetpreviouslocation.aspx" target="_blank">TryGetPreviousLocation method</a> and there is no such previous location. </li> </ul> <p>Here is some sample code that makes use of touch locations. First the "Update" method:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span><span class="rem">// clear previous locations</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span>_touchLocations.Clear();</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> </span> </pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> </span><span class="rem">// get the raw state of the touch panel</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> </span>var touchCollection = TouchPanel.GetState();</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> </span> </pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> </span><span class="kwrd">foreach</span> (var touchLocation <span class="kwrd">in</span> touchCollection)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> </span>{</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> </span> <span class="rem">// we are only interested in pressed (= new)</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> </span> <span class="rem">// and updated (= moved) locations</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> </span> <span class="kwrd">if</span> (touchLocation.State == TouchLocationState.Moved</pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> </span> || touchLocation.State == TouchLocationState.Pressed)</pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> </span> {</pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> </span> <span class="rem">// store this location for the draw method</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> </span> _touchLocations.Add(touchLocation);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> </span> }</pre> <!--CRLF--> <pre class="alt"><span id="lnum17" class="lnum"> </span>}</pre> <!--CRLF--></div> </div> <p>Then, in the draw method, I simply output the number of recognized touch locations and one sprite for each of them (including a text overlay that prints out the id).</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span><span class="rem">// draw number of touch locations</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span>_spriteBatch.DrawString(_font, <span class="str">"Number of touch locations: "</span> + _touchLocations.Count, Vector2.Zero, Color.White);</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> </span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> </span><span class="kwrd">foreach</span> (var touchLocation <span class="kwrd">in</span> _touchLocations)</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> </span>{</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> </span> <span class="rem">// calculate position of sprite</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> </span> var position = <span class="kwrd">new</span> Vector2(touchLocation.Position.X - _sprite.Width / 2.0f,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> </span> touchLocation.Position.Y - _sprite.Height / 2.0f);</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> </span> </pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> </span> <span class="rem">// draw sprite</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> </span> _spriteBatch.Draw(_sprite, position, Color.Blue);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> </span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> </span> <span class="rem">// draw id</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> </span> _spriteBatch.DrawString(_font, touchLocation.Id.ToString(), position, Color.White);</pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> </span>}</pre> <!--CRLF--></div> </div> <p>Of course working with multiple touch locations makes most sense on a real device, not in the emulator.</p> <p>If you want to develop your own gestures, you have to collect these samples and try to interpret them as they occur, according to your requirements. This however is way beyond the scope of this article.</p> <h3></h3> <h3>Summary</h3> <p>In this part we have learned about the various possibilities of touch input on Windows Phone 7 in XNA. You can use the simplest method of the mouse analogon for simple situations, like menu screens, or go all the way down to collecting low-level touch samples to create a multi-touch input application. In between those extremes, you have access to pre-built gestures that allow a more comfortable access to most common input requirements. You can download the source code to the three above demonstrated samples here:</p> <p><a href="http://www.silverlightshow.net/Storage/Sources/XNA_for_Silverlight_developers_Part5.zip" target="_blank">Download source code</a></p> <p>Please feel free to add your comments and thoughts, or contact me directly with any questions you have. </p> <h3>About the author</h3> <p>Peter Kuhn aka "Mister Goodcat" has been working in the IT industry for more than ten years. After being a project lead and technical director for several years, developing database and device controlling applications in the field of medical software and bioinformatics as well as for knowledge management solutions, he founded his own business in 2010. Starting in 2001 he jumped onto the .NET wagon very early, and has also used Silverlight for business application development since version 2. Today he uses Silverlight, .NET and ASP.NET as his preferred development platforms and offers training and consulting around these technologies and general software design and development process questions.</p> <p>He created his first game at the age of 11 on the Commodore 64 of his father and has finished several hobby and also commercial game projects since then. His last commercial project was the development of Painkiller:Resurrection, a game published in late 2009, which he supervised and contributed to as technical director in his free time. You can find his tech blog here: <a href="http://www.pitorque.de/MisterGoodcat/">http://www.pitorque.de/MisterGoodcat/</a></p> http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-5-Input-touch-gestures.aspx editorial@silverlightshow.net (Peter Kuhn ) http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-5-Input-touch-gestures.aspx#comments http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-5-Input-touch-gestures.aspx Mon, 28 Feb 2011 00:22:00 GMT XNA for Silverlight developers: Part 3 - Animation (transforms) <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; padding-top: 5px; text-align: center;">Are you interested in <strong>creating games for Windows Phone 7</strong> but don't have XNA experience? <br /> <a href="http://www.silverlightshow.net/video/XNA-for-WP7-Webinar.aspx">Watch the recording</a> of the recent intro webinar delivered by Peter Kuhn '<strong>XNA for Windows Phone 7</strong>'.<a href="https://www1.gotomeeting.com/register/256344145" target="_blank"></a><br /> </div> <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; width: 196px; float: right; height: 401px; margin-left: 10px; margin-right: 5px; padding-top: 5px;"> <h3>Don't miss...</h3> <ul style="list-style-type: circle; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-left: 20px; font-size: 12px;"> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/WP7-What-is-Windows-Phone-7.aspx">What is Windows Phone series</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Exploring-Silverlight-XNA-integration-on-Windows-Phone-7.aspx">XNA article by Levente Mihály</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Windows-Phone-7-Part-1-Getting-Started.aspx">WP7 series by Andrea Boschin</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx">The XNA series is now available as a fully offline resource</a> </li> </ul> <p style="padding-bottom: 5px; text-align: center;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx"><img style="border:0px solid;" alt="XNA for Silverlight Developers Ebook" src="http://www.silverlightshow.net/storage/thumb_xna_ebook_cover_150_0.png" usemap="#rade_img_map_1291385581316" /></a></p> <p style="font-size: 12px;">     <a href="http://www.silverlightshow.net/ebooks.aspx">All SilverlightShow Ebooks</a> <img alt="" src="http://www.silverlightshow.net/Storage/arrow-content.jpg" /></p> </div> <p>This article is part 3 of the series "XNA for Silverlight developers". </p> <p><strong><strong>This article is compatible with Windows Phone "Mango". <em><br /> Latest update: Aug 1, 2011.</em></strong></strong></p> <p>Animation is a word of Latin origin and means "make alive/bring to live" (<em>animare</em>) or "spirit" (<em>animus</em>). It describes the technique of using a series of single images to create the illusion of movement for the beholder. We all know that concept from the movies and television where the rapid display of a sequence of frames results in the impression of natural motion. In computer graphics and especially games, we use the same technique to bring characters and other elements to life, give the player feedback about their input commands and create a more interesting overall experience. In this article, we'll learn more about animation in general and how it is done in XNA in particular.</p> <h3>Two kinds of animations</h3> <p>When I work with computer games, I like to distinguish between two kinds of animations: transformations and frame-based animation. We have already seen a few glimpses of transformations in the previous parts; when we drew the first sprite onto the screen and moved it around, we have used a translation to create a circular motion in part 1. In part 2 we talked about scaling, which is another simple transformation. These transformations will be part of this article.</p> <p>The other type of animation that can frequently be found in 2D games is frame-based, which means that the actual content of an element on the screen (i.e. the bitmap that forms the sprite) changes over time. This technique is used when a certain effect cannot be achieved by using the mentioned transformations. For example, imagine a player character that moves its feet when it is walking. There is no way of achieving this animation by applying a transformation or a set of transformations like translations, rotations and scaling to the whole sprite. Instead, you use multiple different images that are shown on the screen in succession to create this animation – just like in a movie. We'll take a deeper look at those animations in the second article about animations.</p> <h3>Transformations Theory</h3> <p>From a mathematical point of view, a transformation defines how to map points from one coordinate space to another one using transformation matrices. All of the basic transformations we have shortly talked about so far are so-called affine transformations, which simply put means that after you apply such a transformation, points that were on a line before are still on a line, lines that were parallel before are still parallel afterwards, and ratios of distances are preserved. Samples for these transformations are:</p> <ul> <li>Scale </li> <li>Skew </li> <li>Rotate </li> <li>Translate </li> </ul> <p>You most likely already guessed that there also are non-affine transformations that are more complex. Silverlight's perspective transforms (projections) that allow you to apply 3D-like effects to 2D elements are an example for that.</p> <p>Luckily for us the frameworks we are working with make things easier when we need to use most of those common transformations, so we don't have to dig deeper into the mathematical theory at this point. They are supported by pre-configured shortcuts, helper classes and simplifications, so chances are high you won't ever see a transformation matrix or have to do the involved computations manually when you're writing a 2D game.</p> <h4>The situation in Silverlight</h4> <p>In Silverlight, there are multiple factors that determine the position and final appearance of an object on the screen. It has in fact two different concepts that influence this: the layout process and render transforms. The layout process is the mechanism that is used to position an element with respect to its size, container and siblings. Of course the key component in that process is what layout container is used to hold the element. Some containers, like the stack panel for example, position their children automatically, whereas others like the canvas don't. In addition, properties like the margin affect the final layout position too.</p> <p>Separately from that, you have the possibility to change the visual appearance of an element by using render transforms. Those transformations are applied after the measure and arrange passes of Silverlight's layout process and hence do not affect the positioning of other elements on the screen. This makes them considerably cheaper to use than manipulating properties that trigger a layout update (so if you're able to achieve the same effect using both layout properties and render transforms, use the render transform). Render transforms cover pretty much all the transformations we've discussed above. They enable an extremely simple usage where you only need to set the required parameters (like an angle for a rotation), and the rest is done for you automatically behind the scene. The Silverlight documentation on MSDN actually provides a very sophisticated <a href="http://msdn.microsoft.com/en-us/library/cc189037(v=vs.95).aspx" target="_blank">explanation of transforms</a> and the involved classes and properties. </p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/SL_passes_2.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border-width: 0px;" title="SL_passes" alt="SL_passes" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/SL_passes_thumb.png" width="307" height="383" /></a></p> <p>When it comes to actually animating these transforms, Silverlight has the very powerful tool of storyboards. They can contain one or more animations that are used to manipulate an object's properties over time. Of course the mentioned render transforms are suitable targets for these animations. If you combine all these tools and make use of some further helpers like triggers and actions, you can create complete animations without writing a single line of code. Especially if you're using a software like Expression Blend, that is a matter of only a few minutes. Here is an example of an image declaration in XAML:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span><span class="kwrd"><</span><span class="html">Image</span> <span class="attr">x:Name</span><span class="kwrd">="image"</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span> <span class="attr">Source</span><span class="kwrd">="Block.png"</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> </span> <span class="kwrd"><</span><span class="html">Image.RenderTransform</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> </span> <span class="kwrd"><</span><span class="html">TranslateTransform</span> <span class="kwrd">/></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> </span> <span class="kwrd"></</span><span class="html">Image.RenderTransform</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> </span> <span class="kwrd"><</span><span class="html">i:Interaction.Triggers</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> </span> <span class="kwrd"><</span><span class="html">i:EventTrigger</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> </span> <span class="kwrd"><</span><span class="html">ei:ControlStoryboardAction</span> <span class="attr">Storyboard</span><span class="kwrd">="{StaticResource DemoStoryboard}"</span> <span class="kwrd">/></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> </span> <span class="kwrd"></</span><span class="html">i:EventTrigger</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> </span> <span class="kwrd"></</span><span class="html">i:Interaction.Triggers</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> </span><span class="kwrd"></</span><span class="html">Image</span><span class="kwrd">></span></pre> <!--CRLF--></div> </div> <p>What we can see here is how transforms are attached to an element in XAML (line 3-5). Lines 6-10 contain the magic of an event trigger that starts a storyboard named "DemoStoryboard" when the image is loaded. Such a storyboard can look like this:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span><span class="kwrd"><</span><span class="html">Storyboard</span> <span class="attr">x:Name</span><span class="kwrd">="DemoStoryboard"</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span> <span class="attr">RepeatBehavior</span><span class="kwrd">="Forever"</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> </span> <span class="kwrd"><</span><span class="html">DoubleAnimationUsingKeyFrames</span> <span class="attr">Storyboard</span>.<span class="attr">TargetProperty</span><span class="kwrd">="(UIElement.RenderTransform).(TranslateTransform.X)"</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> </span> <span class="attr">Storyboard</span>.<span class="attr">TargetName</span><span class="kwrd">="image"</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> </span> <span class="kwrd"><</span><span class="html">LinearDoubleKeyFrame</span> <span class="attr">KeyTime</span><span class="kwrd">="0"</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> </span> <span class="attr">Value</span><span class="kwrd">="0"</span> <span class="kwrd">/></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> </span> <span class="kwrd"><</span><span class="html">LinearDoubleKeyFrame</span> <span class="attr">KeyTime</span><span class="kwrd">="0:0:1"</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> </span> <span class="attr">Value</span><span class="kwrd">="170"</span> <span class="kwrd">/></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> </span> <span class="kwrd"><</span><span class="html">LinearDoubleKeyFrame</span> <span class="attr">KeyTime</span><span class="kwrd">="0:0:2"</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> </span> <span class="attr">Value</span><span class="kwrd">="340"</span> <span class="kwrd">/></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> </span> <span class="kwrd"><</span><span class="html">LinearDoubleKeyFrame</span> <span class="attr">KeyTime</span><span class="kwrd">="0:0:3"</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> </span> <span class="attr">Value</span><span class="kwrd">="200"</span> <span class="kwrd">/></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> </span> <span class="kwrd"><</span><span class="html">LinearDoubleKeyFrame</span> <span class="attr">KeyTime</span><span class="kwrd">="0:0:4"</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> </span> <span class="attr">Value</span><span class="kwrd">="0"</span> <span class="kwrd">/></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> </span> <span class="kwrd"></</span><span class="html">DoubleAnimationUsingKeyFrames</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> </span> <span class="kwrd"><</span><span class="html">DoubleAnimationUsingKeyFrames</span> <span class="attr">Storyboard</span>.<span class="attr">TargetProperty</span><span class="kwrd">="(UIElement.RenderTransform).(TranslateTransform.Y)"</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum17" class="lnum"> </span> <span class="attr">Storyboard</span>.<span class="attr">TargetName</span><span class="kwrd">="image"</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum18" class="lnum"> </span> <span class="kwrd"><</span><span class="html">LinearDoubleKeyFrame</span> <span class="attr">KeyTime</span><span class="kwrd">="0"</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum19" class="lnum"> </span> <span class="attr">Value</span><span class="kwrd">="0"</span> <span class="kwrd">/></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum20" class="lnum"> </span> <span class="kwrd"><</span><span class="html">LinearDoubleKeyFrame</span> <span class="attr">KeyTime</span><span class="kwrd">="0:0:1"</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum21" class="lnum"> </span> <span class="attr">Value</span><span class="kwrd">="440"</span> <span class="kwrd">/></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum22" class="lnum"> </span> <span class="kwrd"><</span><span class="html">LinearDoubleKeyFrame</span> <span class="attr">KeyTime</span><span class="kwrd">="0:0:2"</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum23" class="lnum"> </span> <span class="attr">Value</span><span class="kwrd">="200"</span> <span class="kwrd">/></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum24" class="lnum"> </span> <span class="kwrd"><</span><span class="html">LinearDoubleKeyFrame</span> <span class="attr">KeyTime</span><span class="kwrd">="0:0:3"</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum25" class="lnum"> </span> <span class="attr">Value</span><span class="kwrd">="50"</span> <span class="kwrd">/></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum26" class="lnum"> </span> <span class="kwrd"><</span><span class="html">LinearDoubleKeyFrame</span> <span class="attr">KeyTime</span><span class="kwrd">="0:0:4"</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum27" class="lnum"> </span> <span class="attr">Value</span><span class="kwrd">="0"</span> <span class="kwrd">/></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum28" class="lnum"> </span> <span class="kwrd"></</span><span class="html">DoubleAnimationUsingKeyFrames</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum29" class="lnum"> </span><span class="kwrd"></</span><span class="html">Storyboard</span><span class="kwrd">></span></pre> <!--CRLF--></div> </div> <p>This storyboard shows several concepts: it contains two separate animations, one that targets the "X" property of the translate transform that is attached to the image, and a second one that targets the "Y" property. Both animations work with multiple values and time stamps to move the image element around on the screen. We can also see that the "RepeatBehavior" property of the storyboard is set to "Forever" so the animation starts over once it is finished.</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Animation_2.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border-width: 0px;" title="Animation" alt="Animation" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Animation_thumb.png" width="201" height="244" /></a></p> <p>The beauty of all this is not only that we've created an animation without writing a single line of code. The great amount of built-in animation types which in turn often have a variety of properties (for example different kinds of key frames that could be used in the above example) make it possible to tune those animations with extremely little effort. Furthermore, since version 3 Silverlight offers so-called easing functions that allow you to apply custom mathematical formulas to animations. And again it already provides a variety of built-in functions that let you create bounce or spring animations and others without custom code. The <a href="http://samples.msdn.microsoft.com/Silverlight/SampleBrowser/#/?sref=easing_functions_gallery" target="_blank">Silverlight sample code gallery</a> on MSDN has a nice playground you can use to explore those features. All in all Silverlight's animation system in combination with render transforms is really powerful and simple to use.</p> <h4>... and in XNA</h4> <p>We've already learned that XNA has no layout engine like Silverlight does. On the one hand that means that you lose a lot of comfort which Silverlight's layout containers offer, on the other hand this kind of simplifies the process of using transformations, as you don't have to cope with multiple concepts in parallel when you're using XNA. There's basically one single place where all the parameters for the final visual appearance of a sprite on the screen can be set at once, and that is when you draw it using the sprite batch class (of course there are other factors that influence the final looks in XNA too, but for now this explanation is sufficient).</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span><span class="kwrd">public</span> <span class="kwrd">void</span> Draw (</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span> Texture2D texture,</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> </span> Vector2 position,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> </span> Nullable<Rectangle> sourceRectangle,</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> </span> Color color,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> </span> <span class="kwrd">float</span> rotation,</pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> </span> Vector2 origin,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> </span> <span class="kwrd">float</span> scale,</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> </span> SpriteEffects effects,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> </span> <span class="kwrd">float</span> layerDepth</pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> </span>)</pre> <!--CRLF--></div> </div> <p>As you can see the draw method of the sprite batch class has the possibility to pass in various arguments that allow manipulation of the rendering, including the position, rotation and scale values as well as the origin for transformations, a SpriteEffects enumeration value for mirroring features and a layer depth value for z-ordering. As you can see, there's no counterpart for Silverlight's skew transform in XNA. If you want to achieve such an effect, you have to create the matrix transform for it yourself.</p> <p>The other huge limitation in XNA is that there is no built-in feature like the storyboards in Silverlight. Where you create animation descriptions declaratively in Silverlight and behind the scenes all the computations are done for you at runtime automatically, you have to perform most of that work in XNA manually. It's still not that hard for simple animations, but if you try to mimic something like the easing functions of Silverlight for example, you'll quickly find yourself spending quite some time programming mathematical formulas. In the following paragraphs we'll take a look at basic and more advanced transforms and animations.</p> <h4>Translation</h4> <p>This is probably the most simple form of animation you can do in your game. It only involves manipulation of the position where an object will be drawn on the screen.</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Translation_2.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border-width: 0px;" title="Translation" alt="Translation" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Translation_thumb.png" width="501" height="293" /></a></p> <p>As you can see, translating an object requires changing the x- and y-coordinates of its position. By doing so you can move the object to any place on the screen and even off-screen. We had seen an example for that in Part 1 of the series already. In the Update method of our game class, we calculated the position (a 2-dimensional vector) like this:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span><span class="rem">// take the screen width</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span>var screenWidth = graphics.GraphicsDevice.Viewport.Width;</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> </span> </pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> </span><span class="rem">// calculate the new rotation agle</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> </span>angle -= gameTime.ElapsedGameTime.TotalSeconds;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> </span> </pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> </span><span class="rem">// update the x and y coordinates</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> </span>position.X = (<span class="kwrd">float</span>)(screenWidth / 2.0 - block.Width / 2.0 + 200 * Math.Cos(angle));</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> </span>position.Y = (<span class="kwrd">float</span>)(screenWidth / 2.0 - block.Height / 2.0 + 200 * Math.Sin(angle));</pre> <!--CRLF--></div> </div> <h4></h4> <p>This resulted in a circular motion around a central point of the screen with the circle having a radius of 200 pixels. The important part we had learned was that you need to update the position (and every other value as we'll see in a moment) dependent on the amount of time that has elapsed since the last call to the update method. In the above sample, the elapsed seconds were added to the rotation angle. Since 2*pi describes a full circle (remember, we are using radians here) that means that the sprite ("block") finished a full turn after 2*pi ~= 6.28 seconds. If you wanted to make the sprite take 10 seconds to finish one circle, you would have calculated the angle as:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span><span class="rem">// target a duration of 10 seconds for a full circle</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span><span class="rem">// full circle = Math.PI * 2</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> </span><span class="rem">// => (Math.PI * 2) / 10.0 units per second</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> </span>angle -= ((Math.PI * 2) / 10.0) * gameTime.ElapsedGameTime.TotalSeconds;</pre> <!--CRLF--></div> </div> <p>When you play around with this, you'll quickly get used to the following pattern: </p> <ul> <li>Determine how far you want to move an object per second (here: 1/10th of a circle) </li> <li>Multiply that by the amount of elapsed seconds (which usually will be a small fraction of one second) </li> </ul> <p>In the Draw method of the game class, we used that calculated position to actually draw the sprite on the screen:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span>spriteBatch.Begin();</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span>spriteBatch.Draw(block, position, Color.Red); </pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> </span>spriteBatch.End();</pre> <!--CRLF--></div> </div> <h4>Scale</h4> <p>Scaling describes the method to change the size of an object. In XNA, there are two methods for scaling sprites; the more common on is a uniform scale factor you can pass to the draw method of the sprite batch (we'll learn about the second one in the next section). Let's jump right into a sample. First of all, we define the two class fields we need for this:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span><span class="kwrd">float</span> scale = 1.0f;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span><span class="kwrd">float</span> scalingPerSecond = 0.5f;</pre> <!--CRLF--></div> </div> <p>The first one holds the current scale factor (in the beginning this will be 1.0f which is the original size of the sprite). The second variable describes the change of the scale factor per second we want to achieve. Once again we determine the amount of change for the transform dependent on the elapsed time and a target value per second. To this end, I've changed the Update method of the game class to calculate the scaling:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span><span class="rem">// calculate the scaling</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span>scale += scalingPerSecond * (<span class="kwrd">float</span>)gameTime.ElapsedGameTime.TotalSeconds;</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> </span><span class="kwrd">if</span> (scale > 1.5f)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> </span>{</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> </span> <span class="rem">// if the block has grown to 150%,</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> </span> <span class="rem">// reverse the scaling process</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> </span> scale = 1.5f;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> </span> scalingPerSecond *= -1.0f;</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> </span>}</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> </span><span class="kwrd">else</span> <span class="kwrd">if</span> (scale < 0.5f)</pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> </span>{</pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> </span> <span class="rem">// if the block has shrunk to 50%,</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> </span> <span class="rem">// reverste the scaling process</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> </span> scale = 0.5f;</pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> </span> scalingPerSecond *= -1.0f;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> </span>}</pre> <!--CRLF--></div> </div> <p>The content of line 2 is crucial. It adds the fractional change in scaling to the current scale factor, depending on the elapsed seconds. The rest of the code simply reverses the scaling (from growth to shrinking and vice versa) when the sprite size exceeds certain limits (the desired target values). When we draw the sprite, the code will look like this:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span><span class="rem">// draw a second block that is scaled</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span>spriteBatch.Draw(block,</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> </span> Vector2.Zero,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> </span> <span class="kwrd">null</span>,</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> </span> Color.Yellow,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> </span> 0.0f,</pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> </span> Vector2.Zero, <span class="rem">// this is the origin</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> </span> scale, <span class="rem">// pass in the current scaling</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> </span> SpriteEffects.None,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> </span> 0.0f);</pre> <!--CRLF--></div> </div> You can ignore most of the arguments for now, but please note how the scale factor is passed in in line 8. Also take notice of the argument on line 7 which is the origin used for all transformations. In the above code, we use the constant Vector2.Zero here. In this case this means that the upper left corner of the sprite will be the center of scaling and hence a fixed point. <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Scale_4.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border-width: 0px;" title="Scale" alt="Scale" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Scale_thumb_1.png" width="285" height="274" /></a></p> <p>You can pass in a different vector here, for example the center of the sprite, like this:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span>spriteBatch.Draw(block,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span> Vector2.Zero,</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> </span> <span class="kwrd">null</span>,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> </span> Color.Yellow,</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> </span> 0.0f,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> </span> <span class="kwrd">new</span> Vector2(block.Width / 2.0f, block.Height / 2.0f), <span class="rem">// this is the origin</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> </span> scale, <span class="rem">// pass in the current scaling</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> </span> SpriteEffects.None,</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> </span> 0.0f);</pre> <!--CRLF--></div> </div> <p>This will result in a scaling behavior that has the center of the sprite as origin:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Scale2_2.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border-width: 0px;" title="Scale2" alt="Scale2" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Scale2_thumb.png" width="285" height="274" /></a></p> <p>This hopefully reminds you of similar properties we have in Silverlight's transforms, for example the CenterX and CenterY properties of the <a href="http://msdn.microsoft.com/en-us/library/system.windows.media.scaletransform(v=vs.95).aspx" target="_blank">ScaleTransform</a>. XNA's origin parameter however affects all the transforms equally; in that it behaves similar to Silverlight's <a href="http://msdn.microsoft.com/en-us/library/system.windows.uielement.rendertransformorigin(v=vs.95).aspx" target="_blank">RenderTransformOrigin</a>, except that it does not use relative coordinates. This behavior is noteworthy, because the change in the above code snippet not only results in a different scaling, but it also moves the object to a different position compared to the previous code:</p> <p>Before, the Vector2.Zero position described the location of the upper left corner of the sprite. Now that the origin has been set to the center of the sprite, the Vector2.Zero position describes the location of that center, thus effectively moving the sprite to the upper left by half its size.</p> <p><em>Tip: you should be thoughtful regarding the origin parameter. In some situations it might be necessary to handle the origin differently on a case to case basis, depending on what you want to achieve in a particular situation. However m</em><em>y experience is that inexperienced XNA developers tend to lose track of this, especially when different developers working on the same project use different methods. The result then sometimes is very unpleasant situations where you have to track down "bugs" that surface as objects showing in the wrong places and are amazingly hard to analyze.</em></p> <h4></h4> <h4>Using the destination rectangle</h4> <p>The second method you can use for scaling in XNA is to use a destination rectangle when you draw your sprite. The destination rectangle is actually a combination of both the translate and scale transforms: the X and Y properties determine the position, whereas the width and height of the rectangle determine the size of the final sprite. Using this method has two advantages:</p> <ul> <li>You don't have to know the source size of a sprite to achieve a certain target size. That means that your code will continue to work and result in the same visual dimensions even when the size of the source texture changes at a later point. </li> <li>You can use the rectangle to stretch the sprite in a non-uniform way, which is not possible with the scale factor method. </li> </ul> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span><span class="rem">// draw a block with the destination rectangle parameter</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span>Rectangle destination = <span class="kwrd">new</span> Rectangle(10, 40, 100, 50);</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> </span>spriteBatch.Draw(block,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> </span> destination,</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> </span> Color.Yellow);</pre> <!--CRLF--></div> </div> <p>Here you see that the sprite is drawn at position (10,40), and has a width of 100 pixels and a height of 50 pixels. Since the source texture is square, this will squeeze the result to something like this:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/___image_2.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border-width: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/___image_thumb.png" width="146" height="125" /></a></p> <p>And again, no matter what size the source is, these absolute dimensions will stay the same, which would be different when you use the previous scale method.</p> <p><em>Please note that when you use the overload of the draw method that takes both a destination rectangle and an origin parameter, the result will behave as described before, i.e. the X and Y coordinates of the rectangle will determine the location of the origin.</em></p> <h4>Rotate</h4> <p>Rotating a sprite is pretty straight forward. The draw method of the sprite batch class takes an angle argument for this. All you have to remember is that this must be radians. In Silverlight when you are rotating an element using a rotate transform the <a href="http://msdn.microsoft.com/en-us/library/system.windows.media.rotatetransform.angle(v=VS.95).aspx" target="_blank">Angle property</a> takes degrees. If you are more comfortable using degrees then you can easily convert between both units:</p> <p><span style="font-family: 'courier new';">radians = degrees * (pi / 180)</span></p> <p>and</p> <p><span style="font-family: 'courier new';">degrees = radians * (180 / pi)</span></p> <p>XNA also has a <a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.mathhelper_members.aspx" target="_blank">MathHelper class</a> which has methods to convert between degrees and radians you can use instead.</p> <p>Once again the origin argument plays a crucial role in how your rotation is actually performed. The default value (Vector2.Zero) uses the upper left corner as fixed point which results in a rotation like this:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Rotate_2.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border-width: 0px;" title="Rotate" alt="Rotate" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Rotate_thumb.png" width="250" height="273" /></a></p> <p>When you want to rotate around the center of an object you need to set the origin accordingly:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Rotate2_4.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border-width: 0px;" title="Rotate2" alt="Rotate2" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Rotate2_thumb_1.png" width="231" height="196" /></a></p> <p>In our sample game, we already have calculated an angle for the movement of the block on the screen. We'll use that very same angle to rotate the block at the same time it does its circular motion, so the same side always faces the center of the circle. Since we also change the origin parameter so the block rotates around its center, we need to adjust the position calculation too - this is actually a good example of how fiddling around with the origin for your transformations can require changes in other places of your calculations. The new code looks like this:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span><span class="rem">// take the screen width</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span>var screenWidth = graphics.GraphicsDevice.Viewport.Width;</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> </span> </pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> </span><span class="rem">// target a duration of 10 seconds for a full circle</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> </span><span class="rem">// full circle = Math.PI * 2</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> </span><span class="rem">// => (Math.PI * 2) / 10.0 units per second</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> </span>angle -= ((Math.PI * 2) / 10.0) * gameTime.ElapsedGameTime.TotalSeconds;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> </span> </pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> </span><span class="rem">// update the x and y coordinates</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> </span><span class="rem">// we are now using the center of the sprite as origin,</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> </span><span class="rem">// which means we do not have to take the sprite size into account</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> </span><span class="rem">// for the position calculation</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> </span>position.X = (<span class="kwrd">float</span>)(screenWidth / 2.0 + 180 * Math.Cos(angle));</pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> </span>position.Y = (<span class="kwrd">float</span>)(screenWidth / 2.0 + 180 * Math.Sin(angle));</pre> <!--CRLF--></div> </div> <p>As you can see the code to calculate the position actually got simpler in this case, as we do not need to take the sprite's size into account now that the origin is set to the center of the sprite.</p> <p>The draw method now also takes the calculated angle as rotation value (line 6):</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span><span class="rem">// draw the first block that moves</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span>spriteBatch.Draw(block,</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> </span> position,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> </span> <span class="kwrd">null</span>,</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> </span> Color.Red,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> </span> (<span class="kwrd">float</span>)angle,</pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> </span> Vector2.Zero,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> </span> scale,</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> </span> SpriteEffects.None,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> </span> 0.0f);</pre> <!--CRLF--></div> </div> <h4>Advanced topic: easing functions</h4> <p>Linear movement often isn't very pleasing to the eye and doesn't model reality very accurately. For example, when an object starts moving, then it looks a lot more natural when it starts off slow and gains speed as it goes. People are used to this kind of movement from the real world (inertia), and even if they don't know the physics behind it, they feel that something isn't quite right when your animated objects don't account for this. In this example, I'll show you how to make use of the built-in math helper class to create non-linear movement of an object. The idea is the following:</p> <ul> <li>Take the current position of your object and use it as start position </li> <li>Determine the target position </li> <li>Determine the time you want to take the object to go from the start to the target position </li> <li>Depending on the elapsed time, use a mathematical formula to interpolate the new position </li> </ul> <p>This will work for a lot of easing functions, but of course there are endless possibilities, so some behave differently and may need less or more parameters to work. First of all, define the required class fields for this:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span>Vector2 currentPosition;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span>Vector2 sourcePosition;</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> </span>Vector2 targetPosition;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> </span><span class="kwrd">double</span> cycleTime = 3.0;</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> </span><span class="kwrd">double</span> currentCycleTime = 0.0;</pre> <!--CRLF--></div> </div> <p>We keep track of the current position of the object and need the source and target position for the calculation. We also keep track of the elapsed time and how long we want the whole movement to take. The next thing we need to do is initialize the position values. I wanted to move the sprite from the upper left corner to the lower right corner, so I put the initialization code into the LoadContent method of the class, because I need the sprite dimensions for this:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span>block = Content.Load<Texture2D>(<span class="str">"Block"</span>);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span> </pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> </span><span class="rem">// initialize the source positions</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> </span>sourcePosition = <span class="kwrd">new</span> Vector2(10, 10);</pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> </span> </pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> </span><span class="rem">// initialize the target position</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> </span>var screenWidth = graphics.GraphicsDevice.Viewport.Width;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> </span>var screenHeight = graphics.GraphicsDevice.Viewport.Height;</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> </span>targetPosition = <span class="kwrd">new</span> Vector2(screenWidth - block.Width - 10,</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> </span> screenHeight - block.Height - 10);</pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> </span> </pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> </span><span class="rem">// initialize the current position</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> </span>currentPosition = sourcePosition;</pre> <!--CRLF--></div> </div> <p>Nothing spectacular here. We just calculate the source and target positions with some margin, and set the current position to the start point. The interesting part is the code in the Update method, which looks like this:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span><span class="rem">// first of all we sum the elapsed time</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span>currentCycleTime += gameTime.ElapsedGameTime.TotalSeconds;</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> </span> </pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> </span><span class="rem">// now we calculate the weight</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> </span>var weight = (<span class="kwrd">float</span>)(currentCycleTime / cycleTime);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> </span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> </span><span class="rem">// the math helper provides the actual position</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> </span>var currentX = MathHelper.SmoothStep(sourcePosition.X, targetPosition.X, weight);</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> </span>var currentY = MathHelper.SmoothStep(sourcePosition.Y, targetPosition.Y, weight);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> </span>  </pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> </span><span class="rem">// set the new position</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> </span>currentPosition = <span class="kwrd">new</span> Vector2(currentX, currentY);</pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> </span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> </span><span class="rem">// check whether we've arrived at the target position yet</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> </span><span class="kwrd">if</span> (currentPosition == targetPosition)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> </span>{</pre> <!--CRLF--> <pre class="alt"><span id="lnum17" class="lnum"> </span> <span class="rem">// swap source and target</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum18" class="lnum"> </span> var oldSource = sourcePosition;</pre> <!--CRLF--> <pre class="alt"><span id="lnum19" class="lnum"> </span> sourcePosition = targetPosition;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum20" class="lnum"> </span> targetPosition = oldSource;</pre> <!--CRLF--> <pre class="alt"><span id="lnum21" class="lnum"> </span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum22" class="lnum"> </span> <span class="rem">// reset cycle time</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum23" class="lnum"> </span> currentCycleTime = 0f;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum24" class="lnum"> </span>}</pre> <!--CRLF--></div> </div> <p>First we sum the elapsed seconds and determine how far into the targeted duration for a full cycle we are. This will usually result in a value between zero and one, but might also exceed one slightly in certain cases. You should either clamp the value to zero and one or make sure that your mathematical function takes care of this (as it does in our sample).</p> <p>The crucial part is located in lines 8 and 9. We use the SmoothStep helper function to do a cubic interpolation between the source position and target position values, depending on the amount of cycle time that has passed. This will result in a motion that starts off slow and accelerates over time, just to decelerate and become slower again when it approaches the target position, much like a pendular movement. This is the place where you could also plug a custom function. The rest of the code just swaps the source and target position when the cycle has finished to make the object move back to the previous position, which results in an periodic movement. You can take a look at the final result in the included sample applications below.</p> <h3></h3> <h3>Summary</h3> <p>In this part of the series we have learned about the three most common basic transforms: translate, rotate and scale and how to use them in XNA. We have also seen how we can animate objects using these transforms, and how this requires more manual work in XNA compared to Silverlight's really powerful animation system. The last part gave you a glimpse of more complex and advanced animations, which can become more challenging but often are well worth the effort when you look at the results they make possible. You can download the source code to the various samples above here:</p> <p><a href="http://www.silverlightshow.net/Storage/Sources/XNA_for_Silverlight_developers_Part3.zip" target="_blank">Download source code</a></p> <p>While these animations are sufficient for simple games (think Spacewar! or Tetris) the next part of the series will take the concept of animations to the next level and introduce the frame-based approach which opens the door to really unlimited possibilities and most likely better suits what you associate with "animation" these days. </p> <h3>About the Author</h3> <p>Peter Kuhn aka "Mister Goodcat" has been working in the IT industry for more than ten years. After being a project lead and technical director for several years, developing database and device controlling applications in the field of medical software and bioinformatics as well as for knowledge management solutions, he founded his own business in 2010. Starting in 2001 he jumped onto the .NET wagon very early, and has also used Silverlight for business application development since version 2. Today he uses Silverlight, .NET and ASP.NET as his preferred development platforms and offers training and consulting around these technologies and general software design and development process questions.</p> <p>He created his first game at the age of 11 on the Commodore 64 of his father and has finished several hobby and also commercial game projects since then. His last commercial project was the development of Painkiller:Resurrection, a game published in late 2009, which he supervised and contributed to as technical director in his free time. You can find his tech blog here: <a href="http://www.pitorque.de/MisterGoodcat/" target="_blank">http://www.pitorque.de/MisterGoodcat/</a></p> http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-3-Animation-transforms.aspx editorial@silverlightshow.net (Peter Kuhn ) http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-3-Animation-transforms.aspx#comments http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-3-Animation-transforms.aspx Wed, 09 Feb 2011 02:20:00 GMT A classic memory game: Part 3 - Porting the game to Windows Phone 7 <p><em><strong>This article is compatible with the latest version of Silverlight for Windows Phone 7.</strong></em></p> <div style="padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; width: 200px; float: right; margin-left: 10px; padding-top: 5px;border: #dddddd 1px solid;"> <h3>Don't miss...</h3> <ul style="list-style-type: circle; margin: 0px; padding-left: 20px; font-size: 12px;"> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-1-Fundamentals.aspx">XNA for SL developers series</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/book/Pro-Game-Design-with-Silverlight-4.aspx">Pro Game Design with SL4 book: </a></li> </ul> <p style="padding-bottom: 5px;">         <a href="http://www.silverlightshow.net/book/Pro-Game-Design-with-Silverlight-4.aspx"><img alt="Beginning Windows Phone 7 Development" src="http://www.silverlightshow.net/Storage/GameDesign.jpg" /></a> </p> <p style="font-size: 12px;">             <a href="http://www.silverlightshow.net/Books.aspx">Show more books</a><img alt="" src="http://www.silverlightshow.net/Storage/arrow-content.jpg" /></p> </div> <p>This article is Part 3 of “A classic memory game”.</p> <p>The series is about building the following classic memory game in Silverlight, and porting it to Windows Phone 7. In the first article we started a new MVVM Light project, created the controls and designed the game-states. In the second part we put the logic behind the game, and connected it with the View thus completing the Silverlight version of the game. In this final part we will port the game to Windows Phone 7.</p> <p>You can download the original Silverlight version of the game <a href="http://www.silverlightshow.net/Storage/Sources/memoria_2.zip">here</a> and the <a href="http://www.silverlightshow.net/Storage/Sources/MemoryGame.zip">phone port from here</a>.</p> <h3>First Steps</h3> <p>As you already know Windows Phone 7’s application platform is Silverlight. The version of Silverlight on the phone is not Silverlight 4, and it’s not Silverlight 3 either, I think we can put it somewhere between version 3 and 4, with some limitations (like the missing sockets API) and with some extras (the phone specific things). </p> <p>The tools are totally free, both Visual Studio 2010 Express and Microsoft Expression Blend for Windows Phone, you can <a href="http://create.msdn.com/en-us/home/getting_started" target="_blank">download the tools here</a>. For the WrapPanel we will also need the <a href="http://silverlight.codeplex.com/releases/view/55034" target="_blank">Silverlight for Windows Phone Toolkit</a>. </p> <p>Since the Silverlight versions are not so different I just threw my original source code into a new Windows Phone 7 MVVM Light project:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/phone0_2.png"><img width="369" height="109" title="phone0" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px solid;" alt="phone0" src="http://www.silverlightshow.net/Storage/Users/wildfox/phone0_thumb.png" /></a></p> <p>I’ve added the original source (except App.xaml and App.xaml.cs) as already existing items. Of course to get it built I had to make a few modifications mainly in the namespaces. </p> <p>The Toolkit’s namespace changes </p> <div id="codeSnippetWrapper"> <div id="codeSnippet" style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">from</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">to</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit" </pre> <!--CRLF--></div> </div> <p>Interactivity and Interactions namespace becomes</p> <div id="codeSnippetWrapper"> <div id="codeSnippet" style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">from</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">to</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">xmlns:ei="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"</pre> <!--CRLF--></div> </div> <p>Only one thing was incompatible with the phone’s version of Silverlight and that was the text formatting in bindings. Once I removed that the project could be built successfully. (A workaround is using a converter.)</p> <h3>The Phone UI</h3> <p>So the projects was built, and it actually ran. Too bad I could only see the edge of my start-menu, some resizing was needed. The display size of the phone is fixed: 480x800 pixels on all phones. This means we can rely on this size and use fixed elements. As well as resizing I converted my Grids to Canvases where it didn’t interfere with the animations. Canvas is a perfect fit for a fixed scenario and also it’s easier on the CPU as it won’t need to do complex calculations about the layout. An easy way to convert grids to canvases in Blend is the Change layout-type function:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/phone1_2.png"><img width="342" height="274" title="phone1" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px solid;" alt="phone1" src="http://www.silverlightshow.net/Storage/Users/wildfox/phone1_thumb.png" /></a></p> <p>I’ve also changed the card sizes to fill the screen better, so it’s 3x4 on easy, 4x5 on medium and 5x6 on hard.</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/phone2_2.png"><img width="178" height="333" title="phone2" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px solid;" alt="phone2" src="http://www.silverlightshow.net/Storage/Users/wildfox/phone2_thumb.png" /></a>  <a href="http://www.silverlightshow.net/Storage/Users/wildfox/phone3_2.png"><img width="179" height="334" title="phone3" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px solid;" alt="phone3" src="http://www.silverlightshow.net/Storage/Users/wildfox/phone3_thumb.png" /></a></p> <p>After these adjustments the game was working fine in the emulator. And I finished these modifications in about 20 minutes, that’s pretty cool I think! Deploying it to the device is another story though, the game is working on the phone too, but it’s not a great experience yet. </p> <h4></h4> <h4>Adapting the UI to the phone – buttons</h4> <p>First, let’s see the problems with the UI. </p> <p>I’ve increased the size of the buttons, but it still felt a little hard to tap on them. It is strange, because the original buttons are smaller than the ones in the game, but they’re still easier to press. Let’s inspect the original button’s style in the phone. I dragged a button on the page, and edited a copy of its style, this is what I got:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/phone4_2.png"><img width="244" height="162" title="phone4" style="padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px solid;" alt="phone4" src="http://www.silverlightshow.net/Storage/Users/wildfox/phone4_thumb.png" /></a>  <a href="http://www.silverlightshow.net/Storage/Users/wildfox/phone5_2.png"><img width="281" height="391" title="phone5" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px solid;" alt="phone5" src="http://www.silverlightshow.net/Storage/Users/wildfox/phone5_thumb.png" /></a></p> <p><a href="http://go.microsoft.com/fwlink/?LinkID=183218">The UI Design and Interaction Guide</a> states:</p> <p><em>“Touch targets should not be smaller than 9 mm or 34 pixels square and provide at least 2 mm or 8 pixels between touchable controls. In exceptional cases, controls can be smaller but never more than 9 mm or 26 pixels square.”</em></p> <p>This is achieved by giving the buttons a bigger border which is transparent (highlighted red in the picture). So the actual button is bigger than what you see, and this way it enables you to be less accurate when tapping on them.</p> <p>I simply copied this behavior in my custom controls. For the RadioButton I set up a fix sized rectangle, increased the size of the button and gave the outer Grid a transparent background. This last part is very important, because without a background the grid wouldn’t accept the input.</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/phone6_2.png"><img width="244" height="241" title="phone6" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px solid;" alt="phone6" src="http://www.silverlightshow.net/Storage/Users/wildfox/phone6_thumb.png" /></a>  <a href="http://www.silverlightshow.net/Storage/Users/wildfox/phone7_2.png"><img width="361" height="238" title="phone7" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px solid;" alt="phone7" src="http://www.silverlightshow.net/Storage/Users/wildfox/phone7_thumb.png" /></a></p> <p>The end result is an increased touch target, giving a much better experience on the phone:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/phone8_2.png"><img width="244" height="109" title="phone8" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px solid;" alt="phone8" src="http://www.silverlightshow.net/Storage/Users/wildfox/phone8_thumb.png" /></a></p> <p>For the cards I gave a margin to the image to keep the hosting grid bigger.</p> <h4>Adapting the UI to the phone – back button</h4> <p>Currently, pressing the Back button will exit the game immediately. Games on the phone usually pause on the first Back key, exit on second press. I’ve introduced a PauseState and to change the Back button’s behavior I had to override the OnBackKeyPress method:</p> <div id="codeSnippetWrapper" style="text-align: left; padding-bottom: 4px; line-height: 12pt; overflow-x: auto; overflow-y: auto; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 90.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; cursor: text; padding-top: 4px;border: silver 1px solid;"> <div id="codeSnippet" style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">override</span> <span style="color: #0000ff;">void</span> OnBackKeyPress(System.ComponentModel.CancelEventArgs e)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">if</span> (currentState == <span style="color: #006080;">"GameState"</span>)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> Messenger.Default.Send<<span style="color: #0000ff;">string</span>>(Constants.PauseMessage, Constants.PauseMessage);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> VisualStateManager.GoToState(<span style="color: #0000ff;">this</span>, PauseState.Name, <span style="color: #0000ff;">false</span>);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> e.Cancel = <span style="color: #0000ff;">true</span>;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">else</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">base</span>.OnBackKeyPress(e);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">}</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #0000ff;">void</span> VisualStateGroup_CurrentStateChanged(<span style="color: #0000ff;">object</span> sender, VisualStateChangedEventArgs e)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> currentState = e.NewState.Name;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">}</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #0000ff;">string</span> currentState = <span style="color: #006080;">""</span>;</pre> <!--CRLF--></div> </div> <p>If we are in game state, the Back key enters PauseState, otherwise it exists. Here’s the Pause state:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/phone9_2.png"><img width="197" height="311" title="phone9" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px solid;" alt="phone9" src="http://www.silverlightshow.net/Storage/Users/wildfox/phone9_thumb.png" /></a></p> <h3>Performance Optimizations</h3> <p>Originally I added the images to the project as “contents”. The game loaded fast, starting the game on easy level was okay, but on hard it was very slow, meaning a 3-4 seconds pause before the cards appeared. That seemed slow even on a pc, but on the phone it felt forever. On a touchscreen the interaction between the user and the software is more direct than on pc using a mouse, so the user expectation is that s/he would immediately see some result when pressing/tapping a button. But the CPU of the phone is of course much less powerful than on a desktop computer so we need to address optimizations.</p> <p><a href="http://blogs.claritycon.com/kevinmarshall/2010/10/27/wp7-development-tips-part-1/" target="_blank">Databindings and converters in lists are very costly</a> so I removed the converters from the cards’ bindings by adding the specific properties to the MemoryCard model. The Solved property became a Visibility type instead of boolean, and the Image property became an ImageSource instead of just a string pointing to the location of the image.</p> <p>Jpegs are rendered faster on the phone, but the images of the memory cards are using rounded corners which needs transparency so they remained pngs. Adding the images as content resulted a speedy start for the app but a longer pause when the images needed to load. Adding the images as resources means a slower start for the app – the images load with the application – but better loading times for the images themselves. The <a href="http://blogs.msdn.com/b/delay/archive/2010/09/02/keep-a-low-profile-lowprofileimageloader-helps-the-windows-phone-7-ui-thread-stay-responsive-by-loading-images-in-the-background.aspx" target="_blank">LowProfileImageLoader</a> gets the best of two worlds by loading/downloading images in the background without freezing the UI when they are on the web or added as content.</p> <p>I ended up not using the LowProfileImageLoader, just adding the images as resources to the project:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/phone10_2.png"><img width="290" height="171" title="phone10" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px solid;" alt="phone10" src="http://www.silverlightshow.net/Storage/Users/wildfox/phone10_thumb.png" /></a></p> <p>and creating the BitmapImages on startup:</p> <div id="codeSnippetWrapper"> <div id="codeSnippet" style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">_imageCache = <span style="color: #0000ff;">new</span> List<BitmapImage>();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = 0; i < 15; i++)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> var bi = <span style="color: #0000ff;">new</span> BitmapImage(); </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> bi.UriSource = <span style="color: #0000ff;">new</span> Uri(<span style="color: #0000ff;">string</span>.Format(<span style="color: #006080;">"Images/mm{0}.png"</span>, i), UriKind.Relative);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> _imageCache.Add(bi);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">}</pre> <!--CRLF--></div> </div> <p>This way and by eliminating the converters, the load-time on hard difficulty went down from 3-4 seconds to 1,5 seconds. </p> <p>The pause still felt long so I added some animation to make it look shorter. After opting out adding the images in a BackgroundWorker (in the end it needed much more time, and the images appeared in very unbalanced intervals), I simply added a ProgressBar. I‘ve used <a href="http://www.jeff.wilcox.name/2010/11/smooth-loading-performanceprogressbar/" target="_blank">Jeff Wilcox’s PerformanceProgressBar</a> which really made my life easy. His version of the progress bar works in a background thread, and lets the animation finish. That meant I didn’t have to worry about working in a background thread, just switching the progress bar on and off  would display the standard indeterminate progress animation:</p> <div id="codeSnippetWrapper"> <div id="codeSnippet" style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #0000ff;"><</span><span style="color: #800000;">extra:PerformanceProgressBar</span> <span style="color: #ff0000;">Width</span><span style="color: #0000ff;">="320"</span> <span style="color: #ff0000;">Canvas</span>.<span style="color: #ff0000;">Top</span><span style="color: #0000ff;">="710"</span> <span style="color: #ff0000;">Foreground</span><span style="color: #0000ff;">="White"</span> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #ff0000;">RenderTransformOrigin</span><span style="color: #0000ff;">="0.5,0.5"</span> <span style="color: #ff0000;">Canvas</span>.<span style="color: #ff0000;">Left</span><span style="color: #0000ff;">="80"</span> <span style="color: #ff0000;">IsLoading</span><span style="color: #0000ff;">="{Binding IsBusy}"</span> <span style="color: #0000ff;">></span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;"><</span><span style="color: #800000;">extra:PerformanceProgressBar.RenderTransform</span><span style="color: #0000ff;">></span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;"><</span><span style="color: #800000;">CompositeTransform</span> <span style="color: #ff0000;">ScaleX</span><span style="color: #0000ff;">="1.5"</span> <span style="color: #ff0000;">ScaleY</span><span style="color: #0000ff;">="1.5"</span><span style="color: #0000ff;">/></span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;"></</span><span style="color: #800000;">extra:PerformanceProgressBar.RenderTransform</span><span style="color: #0000ff;">></span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #0000ff;"></</span><span style="color: #800000;">extra:PerformanceProgressBar</span><span style="color: #0000ff;">></span></pre> <!--CRLF--></div> </div> <div id="codeSnippetWrapper"> <div id="codeSnippet" style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> Start()</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> _gameEnded = <span style="color: #0000ff;">false</span>;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> IsBusy = <span style="color: #0000ff;">true</span>;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> LoadCards();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> IsBusy = <span style="color: #0000ff;">false</span>;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> StartGame();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">}</pre> <!--CRLF--></div> </div> <p>Please note, that the PerformanceProgressBar too costs CPU time, however it wasn’t significant in this case.</p> <h3>Tombstoning</h3> <p>The last phone-specific feature I wanted to add is tombstoning. Basically it means that the game can resume from its last state when it is interrupted by something, e.g a phone call. Every application has to handle interruption itself. Well, it is not obligatory, but if you want to give your users a good experience you should handle it. I really hope it will change in the future, and the OS will give some basic tombstoning out of the box, because it is not easy to handle. It seems easy to implement, in some cases it is easy, but usually it’s not. You really need to plan ahead.</p> <p>We have four events to help us with implementing tombstoning:</p> <ul> <li>Application_Launching – occurs when the application starts </li> <li>Application_Activated – occurs when the application resumes </li> <li>Application_Deactivated – occurs when the application is sent to background (e.g. phone call) </li> <li>Application_Closing </li> </ul> <p>Storing the state we have two options:</p> <ul> <li>IsolatedStorage – will remain permanent </li> <li>PhoneApplicationService.Current.State – a dictionary which stays in memory for a time when the application is deactivated </li> </ul> <p>I’ve created an AppState class storing all the info of the game that it needs to resume and used <a href="http://whydoidoit.com/silverlight-serializer/" target="_blank">Mike Talbot’s SilverlightSerializer</a> to serialize and save it.</p> <p>Here’s the AppState class:</p> <div id="codeSnippetWrapper" style="text-align: left; padding-bottom: 4px; line-height: 12pt; overflow-x: auto; overflow-y: auto; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 90.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; cursor: text; padding-top: 4px;border: silver 1px solid;"> <div id="codeSnippet" style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 92.67%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; height: 78px; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">class</span> AppState</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">public</span> List<MemoryCard> MemoryCardList { get; set; }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">int</span> TimeCounter { get; set; }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">int</span> MoveCounter { get; set; }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">int</span> PairCounter { get; set; }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">int</span> CardSize { get; set; }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">public</span> Difficulties Difficulty { get; set; }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">public</span> AppState()</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">}</pre> <!--CRLF--></div> </div> <p>Nobody wants to continue a memory game several hours later, so I handled only the activation/deactivation events, and store the state in the temporary Dictionary. You can easily modify the code to do the state saving on the closing event too, and save it to IsolatedStorage instead of the State dictionary.</p> <div id="codeSnippetWrapper" style="text-align: left; padding-bottom: 4px; line-height: 12pt; overflow-x: auto; overflow-y: auto; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 90.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; cursor: text; padding-top: 4px;border: silver 1px solid;"> <div id="codeSnippet" style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #008000;">// Code to execute when the application is activated (brought to foreground)</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #008000;">// This code will not execute when the application is first launched</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">void</span> Application_Activated(<span style="color: #0000ff;">object</span> sender, ActivatedEventArgs e)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">try</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> var storage = PhoneApplicationService.Current.State;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">if</span> (storage.ContainsKey(AppStateKey))</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">byte</span>[] appStateBytes = storage[AppStateKey] <span style="color: #0000ff;">as</span> <span style="color: #0000ff;">byte</span>[];</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> AppState appState = SilverlightSerializer.Deserialize<AppState>(appStateBytes);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> MemoryViewModelLocator.SetAppState(appState); </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">catch</span> { }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">}</pre> <!--CRLF--></div> </div> <div id="codeSnippetWrapper" style="text-align: left; padding-bottom: 4px; line-height: 12pt; overflow-x: auto; overflow-y: auto; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 90.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; cursor: text; padding-top: 4px;border: silver 1px solid;"> <div id="codeSnippet" style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #008000;">// Code to execute when the application is deactivated (sent to background)</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #008000;">// This code will not execute when the application is closing</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">void</span> Application_Deactivated(<span style="color: #0000ff;">object</span> sender, DeactivatedEventArgs e)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> var appState = MemoryViewModelLocator.MainStatic.GetAppState();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">if</span> (appState != <span style="color: #0000ff;">null</span>)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> var storage = PhoneApplicationService.Current.State;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">byte</span>[] stateBytes = SilverlightSerializer.Serialize(appState);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">if</span> (storage.ContainsKey(AppStateKey))</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> storage[AppStateKey] = stateBytes;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">else</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> storage.Add(AppStateKey, stateBytes);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">}</pre> <!--CRLF--></div> </div> <p>We can tell SilverlightSerializer which properties not to serialize with the DoNotSerialize attribute, e.g. the MemoryCard’s Image property:</p> <div id="codeSnippetWrapper"> <div id="codeSnippet" style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">[DoNotSerialize]</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #0000ff;">public</span> ImageSource Image { get; set; }</pre> <!--CRLF--></div> </div> <p>You can try the tombstoning by pressing the windows-key during the game and then press the back-key to resume the game.</p> <h3>Summary</h3> <p>Porting the game to Windows Phone 7 was quite easy, Microsoft did a really great job. In this article we adopted the game to the phone in terms of UI and performance and we implemented tombstoning. With Windows Phone 7 Silverlight is becoming a real multi-platform framework, and I hope it is just the beginning. </p> <p>Please, don’t hesitate to drop a comment if you have questions, I hope you enjoyed this project at least half as much I did! </p> <h3>About the Author</h3> <p><img width="150" height="190" style="width: 100px; float: left; height: 134px; margin-right: 15px;" alt="Levente Mihaly" src="http://www.silverlightshow.net/Storage/levi.jpg" />My name is Levente Mihaly. I've entered the .NET world in 2006, and after graduating at Budapest University of Technology (Msc in Computer Science) I started working as a .NET software developer. After meeting technologies like WinForms and ASP.NET, Silverlight quickly became my favourite platform from the early Silverlight 2 beta stages. I was always interested in mobile development and now I'm very happy since with Windows Phone 7 I can easily expand my knowledge to the mobile world. Nowadays beside following Silverlight I'm focusing on WP7 in my free time. I'm also the runner-up of the 2010 Silverlight ecoContest. You can reach me on twitter (<a href="http://twitter.com/#!/leventemihaly" target="_blank">@leventemihaly</a>). <br />  </p> http://www.silverlightshow.net/items/A-classic-memory-game-Part-3-Porting-the-game-to-Windows-Phone-7.aspx editorial@silverlightshow.net (Levente Mihály ) http://www.silverlightshow.net/items/A-classic-memory-game-Part-3-Porting-the-game-to-Windows-Phone-7.aspx#comments http://www.silverlightshow.net/items/A-classic-memory-game-Part-3-Porting-the-game-to-Windows-Phone-7.aspx Fri, 04 Feb 2011 09:29:00 GMT XNA for Silverlight developers: Part 2 - Text rendering <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; padding-top: 5px; text-align: center;">Are you interested in <strong>creating games for Windows Phone 7</strong> but don't have XNA experience? <br /> <a href="http://www.silverlightshow.net/video/XNA-for-WP7-Webinar.aspx">Watch the recording</a> of the recent intro webinar delivered by Peter Kuhn '<strong>XNA for Windows Phone 7</strong>'.<a href="https://www1.gotomeeting.com/register/256344145" target="_blank"></a><br /> </div> <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; width: 196px; float: right; height: 401px; margin-left: 10px; margin-right: 5px; padding-top: 5px;"> <h3>Don't miss...</h3> <ul style="list-style-type: circle; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-left: 20px; font-size: 12px;"> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/WP7-What-is-Windows-Phone-7.aspx">What is Windows Phone series</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Exploring-Silverlight-XNA-integration-on-Windows-Phone-7.aspx">XNA article by Levente Mihály</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Windows-Phone-7-Part-1-Getting-Started.aspx">WP7 series by Andrea Boschin</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx">The XNA series is now available as a fully offline resource</a> </li> </ul> <p style="padding-bottom: 5px; text-align: center;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx"><img style="border:0px solid;" alt="XNA for Silverlight Developers Ebook" src="http://www.silverlightshow.net/storage/thumb_xna_ebook_cover_150_0.png" usemap="#rade_img_map_1291385581316" /></a></p> <p style="font-size: 12px;">     <a href="http://www.silverlightshow.net/ebooks.aspx">All SilverlightShow Ebooks</a> <img alt="" src="http://www.silverlightshow.net/Storage/arrow-content.jpg" /></p> </div> <p>This article is part 2 of the series "XNA for Silverlight developers".</p> <p><strong><strong>This article is compatible with Windows Phone "Mango". <em><br /> Latest update: Aug 1, 2011.</em></strong></strong></p> <p>Rendering text output in a game is not as fundamental as it is in other apps or business applications. However, it still is a very important part because you will need this everywhere: to render statistics like the current player score on the screen, to show messages or help text, and of course also for additional features and parts of your game that are not related to the game play itself, like menus or high score screens. Drawing text is another area with huge differences between XNA and Silverlight, which we will see and learn about in this part of the series. As always, you can download the full source code at the end of the article.</p> <h3>Silverlight</h3> <p>Text in Silverlight is so essential that you barely write it out directly. Instead you use the variety of controls that is available to do this on a higher level: text blocks and labels to show simple text, rich text controls for formatted text, and text boxes to offer the user the ability to edit text. And of course there's a whole set of additional controls that build upon these basic elements. The text rendering itself offers incredibly rich possibilities; for example, you can change fonts and colors on the fly. You can even use gradients and image brushes to create more interesting looks. And of course, if you want to get down to the lower levels of text rendering, there's also the Glyphs class that let's you manipulate advanced properties on character level. Except for the latter one, Silverlight's text controls also handle layout issues for you. Centering text or changing other aspects of the rendering is a simple matter of setting properties to different values. And so something simple as this:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span><span class="kwrd"><</span><span class="html">TextBlock</span> <span class="attr">Text</span><span class="kwrd">="Silverlight and Text"</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span> <span class="attr">FontFamily</span><span class="kwrd">="Arial Black"</span> </pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> </span> <span class="attr">FontStyle</span><span class="kwrd">="Italic"</span> </pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> </span> <span class="attr">FontSize</span><span class="kwrd">="29.333"</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> </span> <span class="kwrd"><</span><span class="html">TextBlock.Foreground</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> </span> <span class="kwrd"><</span><span class="html">LinearGradientBrush</span> <span class="attr">EndPoint</span><span class="kwrd">="0.5,1"</span> <span class="attr">StartPoint</span><span class="kwrd">="0.5,0"</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> </span> <span class="kwrd"><</span><span class="html">GradientStop</span> <span class="attr">Color</span><span class="kwrd">="Black"</span> <span class="attr">Offset</span><span class="kwrd">="0"</span><span class="kwrd">/></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> </span> <span class="kwrd"><</span><span class="html">GradientStop</span> <span class="attr">Color</span><span class="kwrd">="Red"</span> <span class="attr">Offset</span><span class="kwrd">="1"</span><span class="kwrd">/></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> </span> <span class="kwrd"></</span><span class="html">LinearGradientBrush</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> </span> <span class="kwrd"></</span><span class="html">TextBlock.Foreground</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> </span><span class="kwrd"></</span><span class="html">TextBlock</span><span class="kwrd">></span></pre> <!--CRLF--></div> </div> <p>Turns out to become a rendered text like this:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_image_2.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_image_thumb.png" width="244" height="35" /></a></p> <ul></ul> <h3>XNA</h3> <p>Silverlight's text rendering features are not only very sophisticated and comfortable, they're also expensive. In a game, where computing power is a critical resource, you don't want to waste that on calculating gradients and sub-pixel text rendering details. And of course XNA doesn't have a concept of controls with layout containers or a visual tree where text elements could integrate. So – I bet you already expected that – text rendering is more painful and limited. The good news however is that the required steps have been simplified a lot compared to "pure" Direct3D environments, so even if it is not as comfortable, drawing text in XNA still is straight-forward and not hard to accomplish – as long as you plan ahead.</p> <h4>Fonts basics</h4> <p>In the last part we've learned about the SpriteBatch class which I said is used for both text and sprite rendering. This already hints at the fact that in XNA, rendering text is actually done using bitmaps. This immediately becomes apparent when you try to do something where bitmaps are bad at. Take the following picture of a short text that was manipulated using a 500% scale transform in Silverlight, for example:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_image_10.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_image_thumb_4.png" width="431" height="142" /></a></p> <p>And here is how the same thing looks in XNA:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_image_8.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_image_thumb_3.png" width="426" height="139" /></a></p> <p>Where I come from, people like to use the term "suboptimal" for things like this :). Seriously, this is something you simply don't do in XNA. If you need to use fonts at different sizes, you don't use the built-in scaling capabilities, but ideally create separate so-called sprite fonts for each size (or at least start with a bigger size and only scale it down). For example, text rendered with the same sprite font but created at five times the previous size looks pretty nice:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_image_12.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_image_thumb_5.png" width="464" height="137" /></a></p> <h4>Creating sprite fonts</h4> <p>Now let's see how these sprite fonts are actually created, what they are and how they are used in practice. Like any other asset, your fonts go through the content pipeline too, so the content project of your game is the right place to start at. </p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_image_14.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_image_thumb_6.png" width="644" height="364" /></a></p> <p>When you choose to add a new item to a content project you get a set of specific types for XNA, and "Sprite Font" is one of them. After it is created, Visual Studio opens it automatically for you. Surprisingly, it is just an XML file (I've removed the default comments for clarity):</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span><span class="kwrd"><?</span><span class="html">xml</span> <span class="attr">version</span><span class="kwrd">="1.0"</span> <span class="attr">encoding</span><span class="kwrd">="utf-8"</span>?<span class="kwrd">></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span><span class="kwrd"><</span><span class="html">XnaContent</span> <span class="attr">xmlns:Graphics</span><span class="kwrd">="Microsoft.Xna.Framework.Content.Pipeline.Graphics"</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> </span> <span class="kwrd"><</span><span class="html">Asset</span> <span class="attr">Type</span><span class="kwrd">="Graphics:FontDescription"</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> </span> <span class="kwrd"><</span><span class="html">FontName</span><span class="kwrd">></span>Segoe UI Mono<span class="kwrd"></</span><span class="html">FontName</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> </span> <span class="kwrd"><</span><span class="html">Size</span><span class="kwrd">></span>14<span class="kwrd"></</span><span class="html">Size</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> </span> <span class="kwrd"><</span><span class="html">Spacing</span><span class="kwrd">></span>0<span class="kwrd"></</span><span class="html">Spacing</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> </span> <span class="kwrd"><</span><span class="html">UseKerning</span><span class="kwrd">></span>true<span class="kwrd"></</span><span class="html">UseKerning</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> </span> <span class="kwrd"><</span><span class="html">Style</span><span class="kwrd">></span>Regular<span class="kwrd"></</span><span class="html">Style</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> </span> <span class="rem"><!-- <DefaultCharacter>*</DefaultCharacter> --></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> </span> <span class="kwrd"><</span><span class="html">CharacterRegions</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> </span> <span class="kwrd"><</span><span class="html">CharacterRegion</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> </span> <span class="kwrd"><</span><span class="html">Start</span><span class="kwrd">></span>&#32;<span class="kwrd"></</span><span class="html">Start</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> </span> <span class="kwrd"><</span><span class="html">End</span><span class="kwrd">></span>&#126;<span class="kwrd"></</span><span class="html">End</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> </span> <span class="kwrd"></</span><span class="html">CharacterRegion</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> </span> <span class="kwrd"></</span><span class="html">CharacterRegions</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> </span> <span class="kwrd"></</span><span class="html">Asset</span><span class="kwrd">></span></pre> <!--CRLF--> <pre class="alt"><span id="lnum17" class="lnum"> </span><span class="kwrd"></</span><span class="html">XnaContent</span><span class="kwrd">></span></pre> <!--CRLF--></div> </div> <p>This file only describes the details of the sprite font that the content pipeline and in particular the sprite font importer and processor will create for you. The result of this import process is a texture that contains images of the individual characters, and when you render your text later, the sprite batch class will internally choose the right parts of this texture to re-create the text on the screen. All the detail information about the individual options you can use are listed in the <a href="http://msdn.microsoft.com/en-us/library/bb447759.aspx" target="_blank">XML schema reference for sprite fonts</a> on MSDN. I'll list the important parts here:</p> <ul> <li><strong>FontName</strong>: This is the friendly name of the font (as opposed to the file name) you can find in the fonts control panel item in your operating system. </li> <li><strong>Size</strong>: The size in points the font should be imported at. This is the setting you should tune until you can use the font in your application without using scaling. If you need the same font at different sizes, you should create multiple sprite fonts with this value set to the required values. </li> <li><strong>Style</strong>: Allows to use the common font styles like "Regular", "Bold", "Italic" or the combination "Bold Italic". </li> <li><strong>DefaultCharacter</strong>: The character to use if you try to render a character that is not contained in the generated sprite font. The meaning of this setting becomes clear after we've looked at the character regions settings. </li> <li><strong>CharacterRegions</strong>: see below. </li> </ul> <p>To understand the concept of character regions, you first need to understand two things: first of all, a font may contain <em>a lot</em> of characters. Especially Unicode fonts can easily contain several thousand characters. And second, each character takes up both storage space and memory when it is converted to and loaded as a texture, a lot more than in its original form. Imagine the waste and probably even problems you would run into if you would let XNA convert the whole font. To get an idea of what we are talking about here: Segoe UI, the font used in Windows Vista/7 and newer versions of Office, contains more than 2800 characters. Arial Unicode MS, a particular heavyweight distributed with earlier Office versions, apparently has almost 40,000 characters in it. For normal English text however, with both numbers and upper/lower case letters plus common special characters, you will barely need 100 of them. </p> <p>To fix this problem, a sprite font only includes those characters which you specify in one or more character region definitions. The "Start" and "End" values of a region depict the first and last Unicode character codes to include in the font. Either specify the characters themselves directly, or their decimal values using the "&#" prefix like the sample above demonstrates. The standard file created for a new sprite font includes the region &#32 to &#126. All numbers lower than 128 are identical in Unicode and ASCII, so this means all characters between " " (space) and "~" are included by default (MSDN has a <a href="http://msdn.microsoft.com/en-us/library/60ecse8t(v=VS.80).aspx" target="_blank">ASCII table</a> you can use to look that up):</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_image_16.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_image_thumb_7.png" width="176" height="127" /></a></p> <p>If you need to include Unicode characters, make sure the font you want to use has those, and take a look at the <a href="http://www.unicode.org/charts/" target="_blank">code charts provided on Unicode.org</a> to decide what regions you need. Please note that XNA's text rendering is somewhat limited in cases where characters are composed of multiple characters/glyphs, so you might run into problems with some languages.</p> <p><em>Important note on fonts: </em>One important thing to note is that you cannot use any font that is installed on your computer in a game this way. Most fonts require special licenses to be distributed in such a form, and for some such a license might be very expensive or even not be available at all. XNA comes with a set of built-in fonts you can use without getting into trouble. Please check the "redist.txt" file that comes with Game Studio which lists a number of included fonts. For all other fonts you want to use, make sure you have an appropriate license that allows this. The path to the "redist.txt" file should be:</p> <blockquote> <p><em>%programfiles%\Microsoft Visual Studio 10.0\Common7\IDE\VPDExpress\1033\redist.txt</em></p> </blockquote> <h4>Loading and using sprite fonts</h4> <h4></h4> <p>You load a sprite font like any other asset in your game by using a content manager. As we've learned in the last part, the game class already has a Content property (which actually is a content manager) we can use to do that. To try out the newly created font, change the LoadContent method of your game class in the following way. Also add a class field that holds the sprite font.</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span>SpriteFont sampleFont;</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span> </pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> </span>...</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> </span> </pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> </span><span class="kwrd">protected</span> <span class="kwrd">override</span> <span class="kwrd">void</span> LoadContent()</pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> </span>{</pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> </span> <span class="rem">// Create a new SpriteBatch, which can be used to draw textures.</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> </span> spriteBatch = <span class="kwrd">new</span> SpriteBatch(GraphicsDevice);</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> </span> </pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> </span> <span class="rem">// TODO: use this.Content to load your game content here</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> </span> sampleFont = Content.Load<SpriteFont>(<span class="str">"SampleFont"</span>);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> </span>}</pre> <!--CRLF--></div> </div> <p>Once loaded, it can easily be used in the draw method by utilizing the sprite batch. Remember that you have to call the Begin method first before you can start drawing with a sprite batch, and to call the End method when you're finished. </p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span><span class="kwrd">protected</span> <span class="kwrd">override</span> <span class="kwrd">void</span> Draw(GameTime gameTime)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span>{</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> </span> GraphicsDevice.Clear(Color.Black);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> </span> </pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> </span> <span class="rem">// TODO: Add your drawing code here</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> </span> spriteBatch.Begin();</pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> </span> </pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> </span> spriteBatch.DrawString(sampleFont, <span class="rem">// the sprite font to use for the rendering</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> </span> <span class="str">"Rendering text\r\nin XNA"</span>, <span class="rem">// the text to render, can contain line breaks</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> </span> Vector2.Zero, <span class="rem">// the position of the text (in relation to the origin)</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> </span> Color.Green, <span class="rem">// the text color</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> </span> 0.0f, <span class="rem">// the rotation angle (radians) </span></pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> </span> Vector2.Zero, <span class="rem">// the origin of the sprite</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> </span> 1.0f, <span class="rem">// the scale factor, try to keep at or close to 1</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> </span> SpriteEffects.None, <span class="rem">// allows flipping</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> </span> 0.0f); <span class="rem">// a depth value for depth sorting</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum17" class="lnum"> </span>  </pre> <!--CRLF--> <pre class="alteven"><span id="lnum18" class="lnum"> </span> spriteBatch.End();</pre> <!--CRLF--> <pre class="alt"><span id="lnum19" class="lnum"> </span> </pre> <!--CRLF--> <pre class="alteven"><span id="lnum20" class="lnum"> </span> <span class="kwrd">base</span>.Draw(gameTime);</pre> <!--CRLF--> <pre class="alt"><span id="lnum21" class="lnum"> </span>}</pre> <!--CRLF--></div> </div> <p>There are also alternate overloads with less arguments if you don't want or need to specify some of the values. The result is your first rendered text in XNA:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_18.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_thumb_8.png" width="173" height="61" /></a></p> <h4></h4> <h4>Measuring text size</h4> <p>One problem you will quickly run into is that you need to know the size some text will occupy on the screen. For example, you may want to render right-aligned or centered text, or your texts are dynamic with changing content (think player score, game time etc.) and you want to compensate for that. Luckily, the sprite font class has a method named "MeasureString" that exactly does that. In fact, it's the only method it implements which is not inherited from object. MeasureString takes a string or string builder as argument and returns the size of the rendered text. Since all parameters like font size and font style are static and have been set at compile time when the sprite font was created, they are not of interest or required here. With that, centering a text is a matter of computing the position dependent on the render size (another possibility would be to work with a combination of position and sprite origin of course):</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <div id="codeSnippet" class="csharpcode"> <pre class="alt"><span id="lnum1" class="lnum"> </span><span class="kwrd">protected</span> <span class="kwrd">override</span> <span class="kwrd">void</span> Draw(GameTime gameTime)</pre> <!--CRLF--> <pre class="alteven"><span id="lnum2" class="lnum"> </span>{</pre> <!--CRLF--> <pre class="alt"><span id="lnum3" class="lnum"> </span> GraphicsDevice.Clear(Color.Black);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum4" class="lnum"> </span> </pre> <!--CRLF--> <pre class="alt"><span id="lnum5" class="lnum"> </span> <span class="rem">// TODO: Add your drawing code here</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum6" class="lnum"> </span> </pre> <!--CRLF--> <pre class="alt"><span id="lnum7" class="lnum"> </span> <span class="rem">// calculate the text position</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum8" class="lnum"> </span> <span class="kwrd">string</span> myText = <span class="str">"Some centered text!"</span>;</pre> <!--CRLF--> <pre class="alt"><span id="lnum9" class="lnum"> </span> Vector2 myTextSize = sampleFont.MeasureString(myText);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum10" class="lnum"> </span> Vector2 myTextPosition = <span class="kwrd">new</span> Vector2(graphics.GraphicsDevice.Viewport.Width / 2.0f - myTextSize.X / 2.0f, </pre> <!--CRLF--> <pre class="alt"><span id="lnum11" class="lnum"> </span> graphics.GraphicsDevice.Viewport.Height / 2.0f - myTextSize.Y / 2.0f);</pre> <!--CRLF--> <pre class="alteven"><span id="lnum12" class="lnum"> </span> </pre> <!--CRLF--> <pre class="alt"><span id="lnum13" class="lnum"> </span> <span class="rem">// draw the text</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum14" class="lnum"> </span> spriteBatch.Begin();</pre> <!--CRLF--> <pre class="alt"><span id="lnum15" class="lnum"> </span> spriteBatch.DrawString(sampleFont, <span class="rem">// the sprite font to use for the rendering</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum16" class="lnum"> </span> myText, <span class="rem">// the text to render, can contain line breaks</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum17" class="lnum"> </span> myTextPosition, <span class="rem">// the position of the text (in relation to the origin)</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum18" class="lnum"> </span> Color.Green, <span class="rem">// the text color</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum19" class="lnum"> </span> 0.0f, <span class="rem">// the rotation angle (radians) </span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum20" class="lnum"> </span> Vector2.Zero, <span class="rem">// the origin of the sprite</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum21" class="lnum"> </span> 1.0f, <span class="rem">// the scale factor, try to keep at or close to 1</span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum22" class="lnum"> </span> SpriteEffects.None, <span class="rem">// allows flipping</span></pre> <!--CRLF--> <pre class="alt"><span id="lnum23" class="lnum"> </span> 0.0f); <span class="rem">// a depth value for depth sorting </span></pre> <!--CRLF--> <pre class="alteven"><span id="lnum24" class="lnum"> </span> spriteBatch.End();</pre> <!--CRLF--> <pre class="alt"><span id="lnum25" class="lnum"> </span> </pre> <!--CRLF--> <pre class="alteven"><span id="lnum26" class="lnum"> </span> <span class="kwrd">base</span>.Draw(gameTime);</pre> <!--CRLF--> <pre class="alt"><span id="lnum27" class="lnum"> </span>}</pre> <!--CRLF--></div> </div> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_20.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_thumb_9.png" width="644" height="346" /></a></p> <p>This is the basis for all text rendering you will do in XNA. As I said in the beginning, you use this not only for in-game text, but also for your menus, high score lists, about screens and instructions, simply put: whenever you need text. </p> <h4>Advanced font rendering/bitmap fonts</h4> <p>In the beginning, we have seen Silverlight text that uses gradients, and I've talked about other possibilities in Silverlight like using image brushes to create very unique text. In XNA, this is not supported when it comes to dynamically creating those effects. However, since characters are images in XNA, you can use all the possibilities you have with image manipulation and design to make your fonts more unique. However, this requires that you don't use the default import process of sprite fonts. Instead, you use a feature called bitmap fonts. </p> <p>Bitmap fonts are nothing but images of characters which you create yourself (as opposed to let them create automatically). Then, instead of using the sprite font importer and processor in your XNA content project, you import the image you've created by setting the importer to "Texture" (because it's really a texture you're importing) and the processor to "Sprite Font Texture". The result after the import process is the same however. You load a bitmap font as sprite font object and use it exactly like you would use a sprite font created the way we just did.</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_22.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_thumb_10.png" width="412" height="295" /></a></p> <p>To make a bitmap font work, your image needs to look like this (this image was taken and enhanced to actually work from the <a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.content.pipeline.processors.fonttextureprocessor.aspx" target="_blank">MSDN explanation of this feature</a>):</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/BitmapFont_2.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="BitmapFont" alt="BitmapFont" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/BitmapFont_thumb.png" width="524" height="244" /></a></p> <p>There are several conventions and restriction your image has to follow:</p> <ul> <li>The upper left character has to match the "First Character" property in the properties window above (in this case it's the space character which also is the default). </li> <li>From there, the runtime expects the following characters left to right and top to bottom, which means if you start with space, the next character has to be an exclamation mark, the third one quotes etc. You cannot skip characters. If you need to provide different ranges, you need to write a custom font processor for the content pipeline that matches indexes to characters as described on the above linked MSDN page. </li> <li>For monochrome fonts (where you add the tint in the draw method of the sprite batch like we did), you are supposed to work with grayscale images. Black means transparent and white full solid. If you want to use colors like the example above does for the digits, you need to provide transparency in the alpha channel of the image instead. </li> <li>You need to fill the space between the characters with a magenta color (ff00ff) or you will receive compilation errors, because the processor needs that color to distinguish the individual characters. Don't worry about wasting space, the characters will be packed as tightly as possible during the import process, so you can set the spacing to a value that allows comfortable working. </li> </ul> <p>When you're using bitmap fonts, you can design each individual character as you want. You can for example apply effects and distortions your graphic program offers, or even draw the characters by hand. Obviously though, creating such an image manually is tedious work. Most people therefore use some sort of generator that at least creates the basic image from an existing font and work from there. Microsoft has published <a href="http://create.msdn.com/en-US/education/catalog/utility/bitmap_font_maker" target="_blank">such a tool</a> too, but unfortunately it doesn't work correctly with XNA 4.0 yet.</p> <h4>A word on "Mango"</h4> <p>With the first major update of the Windows Phone 7 platform (code name "Mango") it is possible to mix Silverlight and XNA content not only in the same application, but also on the same page. If you make use of this new possibility, you are able to have the very advanced and comfortable Silverlight text rendering features in your XNA game too. With little effort you can use normal Silverlight text blocks with all their styling possibilities and other advantages mentioned in this article and display them together with your XNA rendered content. <a href="http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-12-Mango-1.aspx">Part 12</a> of this series shows how to do that, and even how to make use of Silverlight's animation features in combination with this.</p> <h4></h4> <h4>Summary</h4> <p>In this article we've learned the differences between text rendering in Silverlight and XNA. We've seen that importing a font into XNA and use it for text output is not that hard if you follow some basic rules, but it's also clear that if nicer fonts and design effects are required, things can quickly become time-consuming. </p> <p>You can download the source code of the sample here. It also includes the bitmap font part, but please note that I've used the above sample image taken from the MSDN article. It doesn't have an alpha channel, so the colored digits will only display as gray scale or tinted in one color.</p> <p><a href="http://www.silverlightshow.net/Storage/Sources/XNATextRendering.zip" target="_blank">Download source code</a></p> <p>As always, feel free to leave any comments and feedback, and of course also suggestions what can be improved or what you want to see in future episodes.</p> <h3>About the Author</h3> <p>Peter Kuhn aka "Mister Goodcat" has been working in the IT industry for more than ten years. After being a project lead and technical director for several years, developing database and device controlling applications in the field of medical software and bioinformatics as well as for knowledge management solutions, he founded his own business in 2010. Starting in 2001 he jumped onto the .NET wagon very early, and has also used Silverlight for business application development since version 2. Today he uses Silverlight, .NET and ASP.NET as his preferred development platforms and offers training and consulting around these technologies and general software design and development process questions.</p> <p>He created his first game at the age of 11 on the Commodore 64 of his father and has finished several hobby and also commercial game projects since then. His last commercial project was the development of Painkiller:Resurrection, a game published in late 2009, which he supervised and contributed to as technical director in his free time. You can find his tech blog here: <a href="http://www.pitorque.de/MisterGoodcat/">http://www.pitorque.de/MisterGoodcat/</a></p> http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-2-Text-rendering.aspx editorial@silverlightshow.net (Peter Kuhn ) http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-2-Text-rendering.aspx#comments http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-2-Text-rendering.aspx Wed, 26 Jan 2011 08:52:00 GMT A classic memory game: Part 2 - The game logic, connecting the ViewModel and the View <p><em><strong>This article is compatible with the latest version of Silverlight.</strong></em></p> <div style="padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; width: 200px; float: right; margin-left: 10px; padding-top: 5px;border: #dddddd 1px solid;"> <h3>Don't miss...</h3> <ul style="list-style-type: circle; margin: 0px; padding-left: 20px; font-size: 12px;"> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-1-Fundamentals.aspx">XNA for SL developers series</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/book/Pro-Game-Design-with-Silverlight-4.aspx">Pro Game Design with SL4 book: </a></li> </ul> <p style="padding-bottom: 5px;">         <a href="http://www.silverlightshow.net/book/Pro-Game-Design-with-Silverlight-4.aspx"><img alt="Beginning Windows Phone 7 Development" src="http://www.silverlightshow.net/Storage/GameDesign.jpg" /></a> </p> <p style="font-size: 12px;">             <a href="http://www.silverlightshow.net/Books.aspx">Show more books</a><img alt="" src="http://www.silverlightshow.net/Storage/arrow-content.jpg" /></p> </div> <p>This article is Part 2 of “A classic memory game”.</p> <p>The series is about building the following classic memory game in Silverlight, and porting it to Windows Phone 7. In the first article we started a new MVVM Light project, created the controls and designed the game-states. Now it’s time to put some code behind the game, and finish it.</p> <p>Here’s the game itself and you can <a href="http://www.silverlightshow.net/Storage/Sources/memoria_2.zip">download the source code here</a>.</p> <p style="text-align: center;"><iframe width="570" height="570" src="http://www.silverlightshow.net/Storage/demos/Memoria/MemoriaPart1.html"></iframe></p> <h3>The Game Logic</h3> <h4>MVVM Light template</h4> <h4></h4> <p>The MVVM Light project template contains a ViewModelLocator and a MainViewModel in the ViewModel folder. The ViewModelLocator’s job is to hold a reference of the ViewModels and to create them when needed. In the App.xaml there’s an instance of the ViewModelLocator, so the Views can bind to the ViewModel via this instance. </p> <p>App.xaml:</p> <div id="codeSnippetWrapper"> <div id="codeSnippet" style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 60.53%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; height: 99px; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #0000ff;"><</span><span style="color: #800000;">Application.Resources</span><span style="color: #0000ff;">></span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;"><</span><span style="color: #800000;">ResourceDictionary</span><span style="color: #0000ff;">></span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;"><</span><span style="color: #800000;">vm:MemoryViewModelLocator</span> <span style="color: #ff0000;">x:Key</span><span style="color: #0000ff;">="Locator"</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #ff0000;">d:IsDataSource</span><span style="color: #0000ff;">="True"</span> <span style="color: #0000ff;">/></span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;"></</span><span style="color: #800000;">ResourceDictionary</span><span style="color: #0000ff;">></span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #0000ff;"></</span><span style="color: #800000;">Application.Resources</span><span style="color: #0000ff;">></span></pre> <!--CRLF--></div> </div> <p>MainPage.xaml:</p> <div id="codeSnippetWrapper"> <div id="codeSnippet" style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 60.68%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; height: 49px; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #0000ff;"><</span><span style="color: #800000;">UserControl.DataContext</span><span style="color: #0000ff;">></span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 59.72%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; height: 16px; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;"><</span><span style="color: #800000;">Binding</span> <span style="color: #ff0000;">Path</span><span style="color: #0000ff;">="Main"</span> <span style="color: #ff0000;">Source</span><span style="color: #0000ff;">="{StaticResource Locator}"</span><span style="color: #0000ff;">/></span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 99.81%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; height: 16px; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #0000ff;"></</span><span style="color: #800000;">UserControl.DataContext</span><span style="color: #0000ff;">></span></pre> <!--CRLF--></div> </div> <h4>Model</h4> <p>To represent the cards, we have a MemoryCard class with the following properties:</p> <ul> <li>the <strong>ID</strong> specifies which image is on the card </li> <li>the <strong>Upside</strong> property indicates if a card is showing it’s face or back </li> <li>the <strong>Solved</strong> property is set true once the card and its pair are found </li> </ul> <p>The MemoryCard class implements the <strong>INotifyPropertyChanged</strong> interface. Classes that implement this interface must have an event delegate PropertyChangedEventHandler. Raising this event enables the View (that’s the MainPage.xaml) to track changes. So we need to raise this event when setting the Upside and the Solved properties. The ID won’t change in a MemoryCard’s lifetime so we don’t need to do anything there.</p> <p>Here’s the Upside property and the raisePropertyChanged method.</p> <div id="codeSnippetWrapper" style="text-align: left; padding-bottom: 4px; line-height: 12pt; overflow-x: auto; overflow-y: auto; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 90.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; cursor: text; padding-top: 4px;border: silver 1px solid;"> <div id="codeSnippet" style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #008000;">/// <summary></span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #008000;">/// The <see cref="Upside" /> property's name.</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #008000;">/// </summary></span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">string</span> UpsidePropertyName = <span style="color: #006080;">"Upside"</span>;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">bool</span> _upside = <span style="color: #0000ff;">false</span>;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #008000;">/// <summary></span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #008000;">/// Gets the Upside property.</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #008000;">/// Changes to that property's value raise the PropertyChanged event. </span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #008000;">/// </summary></span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">bool</span> Upside</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> get</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">return</span> _upside;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> set</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">if</span> (_upside == <span style="color: #0000ff;">value</span>)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">return</span>;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> _upside = <span style="color: #0000ff;">value</span>;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #008000;">// Update bindings, no broadcast</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> raisePropertyChanged(UpsidePropertyName);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">}</pre> <!--CRLF--></div> </div> <div id="codeSnippetWrapper"> <div id="codeSnippet" style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 92.1%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; height: 150px; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">event</span> PropertyChangedEventHandler PropertyChanged;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">void</span> raisePropertyChanged(<span style="color: #0000ff;">string</span> propertyName)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">if</span> (PropertyChanged != <span style="color: #0000ff;">null</span>)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">this</span>.PropertyChanged(<span style="color: #0000ff;">this</span>, <span style="color: #0000ff;">new</span> PropertyChangedEventArgs(propertyName));</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">}</pre> <!--CRLF--></div> </div> <div> </div> <h4>The game logic</h4> <p>The logic is simple. In the MainViewModel’s constructor we’re setting up two timers (DispatcherTimers), one to turn back cards after a while and one for tracking the elapsed time. In the Start method we load the selected number of cards (based on the difficulty) and reset the score and start the elapsed timer.</p> <p>We have a few properties to store the state of the game. The most important one is the list of the cards (MemoryCardList). The MainViewModel derives from BaseViewModel that means we don’t have to explicitly implement the INotifyPropertyChanged interface because it’s already implemented in the base class as well as the RaisePropertyChanged method.</p> <p>Here’s the LoadCards method:</p> <div id="codeSnippetWrapper" style="text-align: left; padding-bottom: 4px; line-height: 12pt; overflow-x: auto; overflow-y: auto; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 90.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; cursor: text; padding-top: 4px;border: silver 1px solid;"> <div id="codeSnippet" style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> LoadCards()</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">int</span> cardCount = 0;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">switch</span> (Difficulty)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">case</span> Difficulties.Easy:</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> cardCount = 8;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> CardSize = 100;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">break</span>;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">case</span> Difficulties.Normal:</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> cardCount = 10;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> CardSize = 78;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">break</span>;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">case</span> Difficulties.Hard:</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> cardCount = 15;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> CardSize = 62;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">break</span>; </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> } </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #008000;">// clear</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> MemoryCardList.Clear();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #008000;">// add cards</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = 0; i < cardCount; i++)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> MemoryCardList.Add(<span style="color: #0000ff;">new</span> MemoryCard(i)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> Upside = <span style="color: #0000ff;">false</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> });</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> MemoryCardList.Add(<span style="color: #0000ff;">new</span> MemoryCard(i)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> Upside = <span style="color: #0000ff;">false</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> });</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #008000;">// shuffle them</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> var rand = <span style="color: #0000ff;">new</span> Random();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = MemoryCardList.Count - 1; i > 0; i--)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">int</span> n = rand.Next(i+1);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> var temp = MemoryCardList[i];</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> MemoryCardList[i] = MemoryCardList[n];</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> MemoryCardList[n] = temp;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> IsGameEnded = <span style="color: #0000ff;">false</span>;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">}</pre> <!--CRLF--></div> </div> <p>Note that we are adding two cards with the same ID at a time in the loop so we can pair them. After adding the cards we shuffle them. One last thing to explain: the CardSize specifies the actual size (width and height) of the cards in the View as we have smaller cards when there more. Keeping the CardSize in the ViewModel is not very elegant, it should belong to the View, I’ll probably refactor it.</p> <p>The heart of the game is the <strong>OnSelectionChanged</strong> method. First we count the upside cards, if it’s two (which is actually one plus the newly flipped) we compare the IDs. If there’s a match they are solved. We have to check if there are any unsolved cards there to track the end of the game. If two cards are already upside showing their front, the newly selected would be the third card to show it’s front, so we have to flip back everything. Here’s the code:</p> <div id="codeSnippetWrapper" style="text-align: left; padding-bottom: 4px; line-height: 12pt; overflow-x: auto; overflow-y: auto; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 90.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; cursor: text; padding-top: 4px;border: silver 1px solid;"> <div id="codeSnippet" style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> OnSelectionChanged(MemoryCard lastCard)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">int</span> upsideNo = MemoryCardList.Count((m) => m.Upside); </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">if</span> (upsideNo == 1)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #008000;">// one is upside + lastCard is 2, check IDs</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> MoveCounter++;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> var upsideCard = MemoryCardList.First((m) => m.Upside);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">if</span> (upsideCard.ID == lastCard.ID)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">if</span> (MemoryCardList.Count((m) => !m.Solved) == 2)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> _elapsedTimer.Stop();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> EndGame();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> } </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> upsideCard.SetSolved();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> lastCard.SetSolved();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> PairCounter++; </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">return</span>;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">if</span> (!_timer.IsEnabled)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> _timer.Start();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> } </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span> (upsideNo == 2)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #008000;">// two is already upside, hide them, lastCard will be the only upside</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> _timer.Stop();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> hideCards();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> } </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">}</pre> <!--CRLF--></div> </div> <h3> </h3> <h3>Connecting the ViewModel and the View</h3> <p>After building the project the MainViewModel properties should show up in Blend’s databinding windows. We bind the MemoryCardList to the ListBox’s ItemSource. Click on the little square next to the ItemSource, choose DataBinding and select the MemoryCardList in the DataContext tab.</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/binding0_thumb_2.png"><img width="804" height="124" title="binding0_thumb" style="background-image: none; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px solid;" alt="binding0_thumb" src="http://www.silverlightshow.net/Storage/Users/wildfox/binding0_thumb_thumb.png" /></a></p> <p>Now the ListBox will always display the cards in the ViewModel’s MemoryCardList property.</p> <h4>Converters</h4> <p>Blend’s databinding window shows only the compatible types by default. That means for example an IsVisible property type of bool won’t show up (by default) when you try to bind it to any element’s Visibility property, because the Visibility is type of a Visibility enum. Here enters the IValueConverter interface. When you build a class implementing this interface, you can pass it to the binding to have the data converted to compatible type.</p> <p>A classic example is the VisiblityConverter for the Solved property (so it’s working the opposite way), note that only one way is implemented, we won’t need the ConvertBack:</p> <div id="codeSnippetWrapper" style="text-align: left; padding-bottom: 4px; line-height: 12pt; overflow-x: auto; overflow-y: auto; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 90.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; cursor: text; padding-top: 4px;border: silver 1px solid;"> <div id="codeSnippet" style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">class</span> VisibilityConverter : IValueConverter</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #cc6633;">#region</span> IValueConverter Members</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">object</span> Convert(<span style="color: #0000ff;">object</span> <span style="color: #0000ff;">value</span>, Type targetType, <span style="color: #0000ff;">object</span> parameter, System.Globalization.CultureInfo culture)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">if</span> (<span style="color: #0000ff;">value</span> == <span style="color: #0000ff;">null</span>) <span style="color: #0000ff;">return</span> Visibility.Visible;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">if</span> ((<span style="color: #0000ff;">bool</span>)<span style="color: #0000ff;">value</span>) <span style="color: #0000ff;">return</span> Visibility.Collapsed;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">else</span> <span style="color: #0000ff;">return</span> Visibility.Visible;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">object</span> ConvertBack(<span style="color: #0000ff;">object</span> <span style="color: #0000ff;">value</span>, Type targetType, <span style="color: #0000ff;">object</span> parameter, System.Globalization.CultureInfo culture)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span> NotImplementedException();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #cc6633;">#endregion</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">}</pre> <!--CRLF--></div> </div> <p>After build, you can set it up in Blend for the MemoryCard’s Solved property. Edit the CardList’s ItemTemplate, select the ToggleButton and bind the Visibility property:</p> <p>Switch to all properties, select the Solved property, open the dropdown at the bottom of the window, and select the VisibilityConverter as Value converter.</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/binding2_thumb3_2.png"><img width="426" height="508" title="binding2_thumb3" style="background-image: none; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px solid;" alt="binding2_thumb3" src="http://www.silverlightshow.net/Storage/Users/wildfox/binding2_thumb3_thumb.png" /></a></p> <p>You can bind the Checked property to the Upside property without a converter, because both of them are a type of bool.</p> <p>Sometimes we need the ValueConverter in both ways, and sometimes we need additional parameters. To set the difficulty I needed both. There is a RadioButton for each difficulty, and we have one Difficulty property in the ViewModel type of an enum. So to make it work I needed to pass which RadioButton is the target. Here’s the code for the DifficultyConverter:</p> <div id="codeSnippetWrapper" style="text-align: left; padding-bottom: 4px; line-height: 12pt; overflow-x: auto; overflow-y: auto; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 90.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; cursor: text; padding-top: 4px;border: silver 1px solid;"> <div id="codeSnippet" style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">object</span> Convert(<span style="color: #0000ff;">object</span> <span style="color: #0000ff;">value</span>, Type targetType, <span style="color: #0000ff;">object</span> parameter, System.Globalization.CultureInfo culture)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">if</span> (System.Convert.ToString(<span style="color: #0000ff;">value</span>).Equals(System.Convert.ToString(parameter)))</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">true</span>;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">false</span>;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">}</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">object</span> ConvertBack(<span style="color: #0000ff;">object</span> <span style="color: #0000ff;">value</span>, Type targetType, <span style="color: #0000ff;">object</span> parameter, System.Globalization.CultureInfo culture)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">if</span> (System.Convert.ToBoolean(<span style="color: #0000ff;">value</span>))</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">return</span> Enum.Parse(<span style="color: #0000ff;">typeof</span>(Difficulties), System.Convert.ToString(parameter), <span style="color: #0000ff;">true</span>);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">null</span>;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">}</pre> <!--CRLF--></div> </div> <p>And here’s the Binding for the Easy difficulty to the button’s Checked property:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/binding3_thumb2_2.png"><img width="413" height="493" title="binding3_thumb2" style="background-image: none; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px solid;" alt="binding3_thumb2" src="http://www.silverlightshow.net/Storage/Users/wildfox/binding3_thumb2_thumb.png" /></a></p> <p>Another converter was needed for displaying the images based on the ID. The <strong>ImageConverter</strong> takes the ID and converts it to an ImageSource by resolving a naming convention.</p> <h4>Formatting text</h4> <p>Converters are often used to format text, for example converting a number to string in a desired format. Silverlight 4 however introduced the StringFormat extension. Unfortunately I couldn’t find any Blend support here, so we have to dive into code to make it working. The following code formats the move counter to a 2-digit style:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/formatting_thumb1_2.png"><img width="475" height="67" title="formatting_thumb1" style="background-image: none; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px solid;" alt="formatting_thumb1" src="http://www.silverlightshow.net/Storage/Users/wildfox/formatting_thumb1_thumb.png" /></a></p> <p>You can find a good summary about StringFormat on <a href="http://www.designersilverlight.com/2010/05/28/silverlight-4-binding-and-stringformat-in-xaml/">DesignerSilverlight</a>.</p> <h4>CallDatacontextMethodAction</h4> <p>We have to connect not just the data in the ViewModel to the View, but user-actions too. The standard way for the View to notify the ViewModel that something is happened is by commands. A simpler and more designer friendly approach is the CallDatacontextMethodAction behavior by <a href="http://dotneteers.net/blogs/vbandi/">András Velvárt</a>. You might have already read the article about it: <a href="http://www.silverlightshow.net/items/A-Designer-friendly-Approach-to-MVVM-Part-I.aspx">A Designer-friendly Approach to MVVM</a>.</p> <p>After adding it to the project and building it we can add it to our Start button to call the Start method on the ViewModel.</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/calldatacontextmethod0_thumb1_2.png"><img width="247" height="418" title="calldatacontextmethod0_thumb1" style="background-image: none; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px solid;" alt="calldatacontextmethod0_thumb1" src="http://www.silverlightshow.net/Storage/Users/wildfox/calldatacontextmethod0_thumb1_thumb.png" /></a>    <a href="http://www.silverlightshow.net/Storage/Users/wildfox/calldatacontextmethod1_thumb1_2.png"><img width="484" height="280" title="calldatacontextmethod1_thumb1" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px solid;" alt="calldatacontextmethod1_thumb1" src="http://www.silverlightshow.net/Storage/Users/wildfox/calldatacontextmethod1_thumb1_thumb.png" /></a></p> <h4></h4> <h4>Delaying an action</h4> <p>For the MemoryCard I had to delay the effect of setting the Solved property to true. What happened is by setting the card to solved it immediately disappeared, even before the control would arrive to the Checked state. This was clearly not okay, the player couldn’t see the card before it was marked as solved and disappeared.  That’s why I introduced the SetSolved method that starts a timer and only after 1.5 seconds sets the Solved property to true thus making the card disappear.</p> <h4>The Messenger class</h4> <p>The ViewModel sends a signal to the View once all of the cards are collected by using <a href="http://blog.galasoft.ch/archive/2009/09/27/mvvm-light-toolkit-messenger-v2-beta.aspx">MVVM Light Messenger class</a>. The View then moves into the EndState:</p> <div id="codeSnippetWrapper"> <div id="codeSnippet" style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">Messenger.Default.Register<<span style="color: #0000ff;">string</span>>(<span style="color: #0000ff;">this</span>, (msg) =></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> <span style="color: #0000ff;">if</span> (Constants.EndGameMessage == msg)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> { </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> VisualStateManager.GoToState(<span style="color: #0000ff;">this</span>, EndState.Name, <span style="color: #0000ff;">true</span>);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;">});</pre> <!--CRLF--></div> </div> <h3> </h3> <p>The Messenger is used in the MemoryCard class as well. When the card is flipped it notifies the MainViewModel and that is when the OnSelectionChanged method runs, which is the heart of the game.</p> <h3>Summary</h3> <p>In this article we inspected the development aspects of a game leveraging on MVVM using bindings, visual states, transitions and behaviors. Expression Blend turned out to be a really powerful tool as for the whole game we needed only little coding.</p> <p>In the final article of the series I will port this game to Windows Phone 7, exploring performance optimizations, phone-specific UI, and tombstoning. Don’t hesitate to drop a comment if you have questions or want to see something specific in the next article.</p> <h3>About the Author</h3> <p><img width="150" height="190" style="width: 100px; float: left; height: 134px; margin-right: 15px;" alt="Levente Mihaly" src="http://www.silverlightshow.net/Storage/levi.jpg" />My name is Levente Mihaly. I've entered the .NET world in 2006, and after graduating at Budapest University of Technology (Msc in Computer Science) I started working as a .NET software developer. After meeting technologies like WinForms and ASP.NET, Silverlight quickly became my favourite platform from the early Silverlight 2 beta stages. I was always interested in mobile development and now I'm very happy since with Windows Phone 7 I can easily expand my knowledge to the mobile world. Nowadays beside following Silverlight I'm focusing on WP7 in my free time. I'm also the runner-up of the 2010 Silverlight ecoContest. You can reach me on twitter (<a href="http://twitter.com/#!/leventemihaly" target="_blank">@leventemihaly</a>). <br />  </p> http://www.silverlightshow.net/items/A-classic-memory-game-Part-2-The-game-logic-connecting-the-ViewModel-and-the-View.aspx editorial@silverlightshow.net (Levente Mihály ) http://www.silverlightshow.net/items/A-classic-memory-game-Part-2-The-game-logic-connecting-the-ViewModel-and-the-View.aspx#comments http://www.silverlightshow.net/items/A-classic-memory-game-Part-2-The-game-logic-connecting-the-ViewModel-and-the-View.aspx Fri, 21 Jan 2011 09:54:00 GMT A classic memory game: Part 1 - Designing the game <p><em><strong>This article is compatible with the latest version of Silverlight.</strong></em></p> <div style="padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; width: 200px; float: right; margin-left: 10px; padding-top: 5px;border: #dddddd 1px solid;"> <h3>Don't miss...</h3> <ul style="list-style-type: circle; margin: 0px; padding-left: 20px; font-size: 12px;"> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-1-Fundamentals.aspx">XNA for SL developers series</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/book/Pro-Game-Design-with-Silverlight-4.aspx">Pro Game Design with SL4 book: </a></li> </ul> <p style="padding-bottom: 5px;">         <a href="http://www.silverlightshow.net/book/Pro-Game-Design-with-Silverlight-4.aspx"><img alt="Beginning Windows Phone 7 Development" src="http://www.silverlightshow.net/Storage/GameDesign.jpg" /></a> </p> <p style="font-size: 12px;">             <a href="http://www.silverlightshow.net/Books.aspx">Show more books</a><img alt="" src="http://www.silverlightshow.net/Storage/arrow-content.jpg" /></p> </div> <p>This article is Part 1 of “A classic memory game”.</p> <h3>Introduction</h3> <p>My wife has started learning Silverlight and because of my Expression Blend skills are still lacking, we have decided to build the following classic memory game so both of us can learn something new. We kept things simple but we respected basic Silverlight principles. That’s why we used MVVM which might sound an overkill for a simple game but you’ll see that it fits together perfectly. </p> <p>Nowadays everything revolves around Windows Phone 7 in the Silverlight world and I am not an exception either. Once I got my phone I quickly ported the game over to WP7 to see that with a few modifications it’s running nicely. </p> <p>In this first article I will go around the game’s concepts, designing the controls and the main game-states. In the second article I’ll finish building the game by adding the necessary code and connecting it with the View. The third part will be about porting the Silverlight game to Windows Phone 7.</p> <p>First things first, here’s the game itself and <a href="http://www.silverlightshow.net/Storage/Sources/memoria_2.zip">download link for the source code</a>.</p> <p style="text-align: center;"><iframe width="570" height="570" src="http://www.silverlightshow.net/Storage/demos/Memoria/MemoriaPart1.html"></iframe></p> <p> </p> <h3>Step 0. – creating a new MVVM Light project</h3> <p>For the project we have used my personal favorite and probably the most popular lightweight MVVM framework out there: Laurent  Bugnion’s MVVM Light toolkit. If you haven’t already installed it, you can do it from here: <a href="http://www.galasoft.ch/mvvm/installing/manually/" title="http://www.galasoft.ch/mvvm/installing/manually/">http://www.galasoft.ch/mvvm/installing/manually/</a></p> <p>After installing and copying the Blend template in its place, two new ‘New Project’ templates should appear in the Silverlight department:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/mem_0_thumb1_2.png"><img width="333" height="177" title="mem_0_thumb1" style="background-image: none; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px;" alt="mem_0_thumb1" src="http://www.silverlightshow.net/Storage/Users/wildfox/mem_0_thumb1_thumb.png" /></a></p> <p>Of course you can add the MVVM Light toolkit bits later to the project as well, but it is very nice to have a template for an easy start. It’s also important to note that the MVVM Light toolkit is available for Silverlight 3, <strong>Silverlight 4</strong>, WPF3.5, WPF4 and <strong>Windows Phone 7</strong>.</p> <h3>Creating a custom ToggleButton Style – the Card</h3> <p>After drawing the different cards, the back of the cards,  and saving them as images, our first step was to create a custom control for the cards. </p> <p>In Blend put a Grid in the LayoutRoot and put two Images inside this Grid. One will represent the back of the card, the other will be the front. Right click on the Grid and choose Make into Control..</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/mem_cardctrl1_thumb1_2.png"><img width="277" height="252" title="mem_cardctrl1_thumb1" style="background-image: none; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px;" alt="mem_cardctrl1_thumb1" src="http://www.silverlightshow.net/Storage/Users/wildfox/mem_cardctrl1_thumb1_thumb.png" /></a></p> <p>In the upcoming panel find the ToggleButton control and add the name CardToggleButtonStyle. Select a separate resource file (you may have to create a new one) to define the style in to keep the MainPage.xaml clean.</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/mem_cardctrl2_thumb3_2.png"><img width="273" height="339" title="mem_cardctrl2_thumb3" style="background-image: none; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px;" alt="mem_cardctrl2_thumb3" src="http://www.silverlightshow.net/Storage/Users/wildfox/mem_cardctrl2_thumb3_thumb.png" /></a></p> <p>Delete the ContentPresenter, so you will end up something like this:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/mem_cardctrl3_thumb1_2.png"><img width="272" height="358" title="mem_cardctrl3_thumb1" style="background-image: none; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px;" alt="mem_cardctrl3_thumb1" src="http://www.silverlightshow.net/Storage/Users/wildfox/mem_cardctrl3_thumb1_thumb.png" /></a></p> <p>Notice the States panel, especially the CheckStates state-group. Every ToggleButton has a checked and unchecked state and we will use these two states to switch between our card’s front and back. So select the Unchecked state and change the front image’s Visibility to Collapsed. Then select the Checked state and do the same with the back image.</p> <p>Now if you run the application you should be able to switch between the back and front of the card by clicking on it. Let’s add the turning-animation to the switching to make it look better! </p> <p>Select the Unchecked state and add a transition to Checked state (1). A good length for the animation is 0.3 secs (2). By selecting the newly created transition we can edit its animation. Forward to 0.2 sec in the timeline, select the front image (3) and click the Record Keyframe button (4). Do this for the back image too (3,4). Set up a 90 degrees Y rotation in the Projection panel for the front image, and a –90 for the back (5). (Don’t worry when Blend automatically overrides the 90 degrees to 89...) Change the Visibility of the images, the front was Collapsed, change it to Visible. The back was Visible, change it to Collapsed (6). Now forward to the 0.3 sec mark and change the front images Y rotation to 0 (7). Now when you press the play button, you should see the card turning from one side to the other.</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/mem_ctrl_anim_thumb1_2.png"><img width="455" height="402" title="mem_ctrl_anim_thumb1" style="background-image: none; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px;" alt="mem_ctrl_anim_thumb1" src="http://www.silverlightshow.net/Storage/Users/wildfox/mem_ctrl_anim_thumb1_thumb.png" /></a></p> <p>You’ll have to create the opposite of this animation in the Checked->Unchecked transition.</p> <p>The cards will be flipped back when a certain time elapsed or a third card is about to turn. That means the logic is responsible for this task not the player. So in the Checked state set the <strong>IsHitTestVisible</strong> to false. At first I didn’t do this and had this bug where the user could turn back and forth a card without the MoveCounter increasing.</p> <h3>Designing the layout – Creating a WrapListBox</h3> <p>So we have the card control, now we need to place them. The memory game shouts for a WrapPanel, you’ll find that in the <a href="http://silverlight.codeplex.com/">Silverlight toolkit</a>. But we wanted a more dynamic solution, having different number of cards for each difficulty. That’s why we used a WrapListBox, a ListBox with WrapPanel’s layout. Here’s how to do it:</p> <p>Drop a ListBox in the Layout Grid, right click on it and choose Edit Additional Templates –> Edit Layout of Items (ItemsPanel) –> Create Empty..</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/mem_wrap_thumb2_2.png"><img width="462" height="289" title="mem_wrap_thumb2" style="background-image: none; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px;" alt="mem_wrap_thumb2" src="http://www.silverlightshow.net/Storage/Users/wildfox/mem_wrap_thumb2_thumb.png" /></a></p> <p>Give it a name (WrapItemsTemplate) and again place the template in the separate resource dictionary to keep the main xaml clean. The ItemsPanel is responsible for the layout of the ListBox’s items. Here you can override the ListBox’s default layout (which is a StackPanel) and do anything you want. Just check out <a href="http://bea.stollnitz.com/blog/?p=40">Bea Stollnitz’s famous solar system example</a>.</p> <p>We’ll need only a simple WrapPanel now. Delete the default StackPanel and replace it with the WrapPanel (you’ll need the <a href="http://silverlight.codeplex.com/">Silverlight toolkit</a>).</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/mem_wrap2_thumb1_2.png"><img width="347" height="97" title="mem_wrap2_thumb1" style="background-image: none; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px;" alt="mem_wrap2_thumb1" src="http://www.silverlightshow.net/Storage/Users/wildfox/mem_wrap2_thumb1_thumb.png" /></a></p> <p>Trying it by placing a few Cards inside you’ll see that it doesn’t do exactly what we wanted. The problem is the ScrollViewer.</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/mem_wrap3_thumb_2.png"><img width="243" height="244" title="mem_wrap3_thumb" style="background-image: none; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px;" alt="mem_wrap3_thumb" src="http://www.silverlightshow.net/Storage/Users/wildfox/mem_wrap3_thumb_thumb.png" /></a></p> <p>The quickest solution is to remove it from the ListBox’s template (we wouldn’t want the user to scroll for flipping cards).</p> <p>Right click on the ListBox, select Edit Template and Edit a Copy.. After giving it a name (MemoryListBoxStyle) and placing it in the separate resource dictionary, move up the ItemsPresenter into the Grid, and delete the unneeded parts: the Border, the ScrollViewer and the ValidationErrorElement. The ItemsPresenter defines where the ItemsPanel (that’s our WrapPanel) is added inside the ListBox template. So we will need just that.</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/mem_wrap6_thumb1_2.png"><img width="388" height="154" title="mem_wrap6_thumb1" style="background-image: none; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px;" alt="mem_wrap6_thumb1" src="http://www.silverlightshow.net/Storage/Users/wildfox/mem_wrap6_thumb1_thumb.png" /></a></p> <p>Much better:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/mem_wrap7_thumb1_2.png"><img width="381" height="287" title="mem_wrap7_thumb1" style="background-image: none; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px;" alt="mem_wrap7_thumb1" src="http://www.silverlightshow.net/Storage/Users/wildfox/mem_wrap7_thumb1_thumb.png" /></a></p> <h3>Designing the layout – Menu and VisualStates</h3> <p>The game has three main stages: choosing a difficulty (start), playing, score (end). VisualStates are useful not just in (Custom) Control level, but in UserControl level as well. I’ve added three VisualStates to the LayoutRoot after building the menu items.</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/state0_thumb1_2.png"><img width="256" height="256" title="state0_thumb1" style="background-image: none; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px;" alt="state0_thumb1" src="http://www.silverlightshow.net/Storage/Users/wildfox/state0_thumb1_thumb.png" /></a><a href="http://www.silverlightshow.net/Storage/Users/wildfox/state1_thumb5_2.png"><img width="256" height="256" title="state1_thumb5" style="background-image: none; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px;" alt="state1_thumb5" src="http://www.silverlightshow.net/Storage/Users/wildfox/state1_thumb5_thumb.png" /></a><a href="http://www.silverlightshow.net/Storage/Users/wildfox/state2_thumb2_2.png"><img width="257" height="256" title="state2_thumb2" style="background-image: none; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px;" alt="state2_thumb2" src="http://www.silverlightshow.net/Storage/Users/wildfox/state2_thumb2_thumb.png" /></a></p> <p>All the menus are Grids with a rounded Rectangle inside them. The Buttons use a custom style containing a rounded Rectangle and the ContentPresenter. </p> <p>The template of the RadioButtons are the ContentPresenter and two Rectangles. One Rectangle behaves as a highlight border, because it doesn’t fill and is visible only when in Checked state.</p> <p>For the states first we need to add a VisualStateGroup, select the LayoutRoot, switch to the States panel, and add a VisualStateGroup. Now we can add our three states and of course we won’t miss adding transitions as well. The result looks like this:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/mainstates_thumb4_2.png"><img width="395" height="276" title="mainstates_thumb4" style="background-image: none; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px;" alt="mainstates_thumb4" src="http://www.silverlightshow.net/Storage/Users/wildfox/mainstates_thumb4_thumb.png" /></a></p> <p>Note that you can add Easing to transitions (highlighted too) to make a more realistic animation. For example in my case I have an ElasticInOut Easing on the MenuState –> GameState transition.</p> <h4>Switching States using the GoToStateAction behavior</h4> <p>A great and straightforward way to change between states is the GoToStateAction behavior. With this behavior we can easily set up the initial state, the GameState, and the switching from EndState to MenuState. What we can’t do with it, is the switch from GameState to EndState because it’s not trivial, it’s decided by the game logic. So let’s do the trivial ones:</p> <p>Switch to the Assets panel and find the GoToStateAction behavior in the Behaviors. Put the behavior inside the LayoutRoot and select Loaded for EventName and MenuState for State. Don’t forget to check the UseTransitions box.</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/gotostate0_thumb1_2.png"><img width="255" height="286" title="gotostate0_thumb1" style="background-image: none; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px;" alt="gotostate0_thumb1" src="http://www.silverlightshow.net/Storage/Users/wildfox/gotostate0_thumb1_thumb.png" /></a>  <a href="http://www.silverlightshow.net/Storage/Users/wildfox/gotostate01_thumb6_2.png"><img width="468" height="286" title="gotostate01_thumb6" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border: 0px;" alt="gotostate01_thumb6" src="http://www.silverlightshow.net/Storage/Users/wildfox/gotostate01_thumb6_thumb.png" /></a></p> <p>To switch from MenuState to GameState place another GoToStateAction behavior inside the start button, EventName is Click, StateName is GameState. Put the final behavior inside the new game button to make the switch from EndState to MenuState again (StateName is MenuState).</p> <h3>Summary</h3> <p>In this article we started designing a game, our main tool was Expression Blend. We’ve created a custom control for the Cards, a WrapListBox for laying out them, and we set up the three main states of the game. All of these parts are real, working elements.</p> <p>In the next part we will put some logic behind the game and connect it with the View, using converters, the CallDatacontextMethodAction behavior and others. We will again rely heavily on Blend.</p> <h3>About the Author</h3> <p><img width="150" height="190" style="width: 100px; float: left; height: 134px; margin-right: 15px;" alt="Levente Mihaly" src="http://www.silverlightshow.net/Storage/levi.jpg" />My name is Levente Mihaly. I've entered the .NET world in 2006, and after graduating at Budapest University of Technology (Msc in Computer Science) I started working as a .NET software developer. After meeting technologies like WinForms and ASP.NET, Silverlight quickly became my favourite platform from the early Silverlight 2 beta stages. I was always interested in mobile development and now I'm very happy since with Windows Phone 7 I can easily expand my knowledge to the mobile world. Nowadays beside following Silverlight I'm focusing on WP7 in my free time. I'm also the runner-up of the 2010 Silverlight ecoContest. You can reach me on twitter (<a href="http://twitter.com/#!/leventemihaly" target="_blank">@leventemihaly</a>). <br />  </p> http://www.silverlightshow.net/items/A-classic-memory-game-Part-1-Designing-the-game.aspx editorial@silverlightshow.net (Levente Mihály ) http://www.silverlightshow.net/items/A-classic-memory-game-Part-1-Designing-the-game.aspx#comments http://www.silverlightshow.net/items/A-classic-memory-game-Part-1-Designing-the-game.aspx Thu, 20 Jan 2011 10:00:00 GMT XNA for Silverlight developers: Part 1 - Fundamentals <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; padding-top: 5px; text-align: center;">Are you interested in <strong>creating games for Windows Phone 7</strong> but don't have XNA experience? <br /> <a href="http://www.silverlightshow.net/video/XNA-for-WP7-Webinar.aspx">Watch the recording</a> of the recent intro webinar delivered by Peter Kuhn '<strong>XNA for Windows Phone 7</strong>'.<a href="https://www1.gotomeeting.com/register/256344145" target="_blank"></a><br /> </div> <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; width: 196px; float: right; height: 401px; margin-left: 10px; margin-right: 5px; padding-top: 5px;"> <h3>Don't miss...</h3> <ul style="list-style-type: circle; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-left: 20px; font-size: 12px;"> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/WP7-What-is-Windows-Phone-7.aspx">What is Windows Phone series</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Exploring-Silverlight-XNA-integration-on-Windows-Phone-7.aspx">XNA article by Levente Mihály</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Windows-Phone-7-Part-1-Getting-Started.aspx">WP7 series by Andrea Boschin</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx">The XNA series is now available as a fully offline resource</a> </li> </ul> <p style="padding-bottom: 5px; text-align: center;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx"><img style="border:0px solid;" alt="XNA for Silverlight Developers Ebook" src="http://www.silverlightshow.net/storage/thumb_xna_ebook_cover_150_0.png" usemap="#rade_img_map_1291385581316" /></a></p> <p style="font-size: 12px;">     <a href="http://www.silverlightshow.net/ebooks.aspx">All SilverlightShow Ebooks</a> <img alt="" src="http://www.silverlightshow.net/Storage/arrow-content.jpg" /></p> </div> <p>This article is Part 1 of the series “XNA for Silverlight developers”. </p> <p><strong><strong>This article is compatible with Windows Phone "Mango". <em><br /> Latest update: Aug 1, 2011.</em></strong></strong></p> <p>For Silverlight developers who want to delve into XNA, the hard part is not learning the new set of classes in the library, but the fundamental difference in programming style for some parts of XNA compared to Silverlight. Especially when you have no experience in game programming, you might find some things confusing or even illogical. In the first part of this article, I want to give an overview and explanation of the biggest differences. You will run across all these topics again in the following articles in more detail. </p> <p>Later on, we finally start with some actual code and analyze the structure of an XNA project. We will learn about the concept of content and how to add it to your project. By the end of the article, we will have built a very simple "Hallo World" game that renders some moving content onto the screen. The source code for the sample project can be found at the end of the article.</p> <h3></h3> <h3></h3> <h3>Some Theory</h3> <h4>Event-driven vs. Polling</h4> <p>In Silverlight, when you are interested in any kind of data or information, the common way to get it is to subscribe to some sort of event (I also count callbacks and similar constructs to that). No matter if it is user actions you want to be notified about, the arrival of data from a returning asynchronous operation, or other situations that require transportation of information: the runtime, third party components, or even objects you have written yourself send you a notification whenever the data you have asked for is available. That also works the other way round. If you want to notify someone else that something interesting has happened, for example signal to a UI component that your data has changed, you raise events. We've all gotten used to interfaces like INotifyPropertyChanged, do we?</p> <p>This picture is quite different in XNA. A lot of work there is done using polling. For example, instead of being notified by an event that the user has touched the screen, you actively keep asking the screen if something interesting happened since your last call. Like on that long trip in the car, when your kids in the back seat keep asking "are we there yet"; with XNA, you are the kids, and you ask 30 times a second.</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_12.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_thumb_5.png" width="545" height="480" /></a></p> <p>You may ask yourself why one would voluntarily use polling when there's so much comfort in using events. The answer is simple: to have full control about the processing of that information and data. One drawback of the event-driven model is that events may happen any time. This is a problem in Silverlight applications too, but we have learned to live with it and work around it. For example, let's say you have a button that kicks off a long-running operation. Nobody stops the user from hitting that button twice or three times in a row. If you want to prevent multiple operations from being started, then what you do is ignore subsequent events, or disable the involved UI elements so they do not generate those events anymore. With a polling based model, you simply wouldn't ask anymore. Also, more reasons for and benefits of that approach will become apparent once you learn more details about XNA. Especially user actions could corrupt your data if they happened e.g. between update and drawing operations. </p> <p>I don't say XNA doesn't have events, and I also don't say XNA doesn't <em>use</em> events. When you create an empty project and inspect the involved classes, as we will in just a few minutes, you'll realize that events are in fact used, for example to cope with system notifications (e.g. when the user has hit a hardware button and your game is suspended or similar things). In a lot of situations, you might want to use events in your own programming logic and game structure too, simply because the alternative often means to clutter your code with loads of flags and statements that check those flags (think complexity of a state machine). I'll try to point out those situations as we go. </p> <h4>Drawing Content (aka "The Groundhog Day Effect")</h4> <p>When you want to make something visible on the screen in Silverlight, you simply add it to the visual tree in a "fire and forget" manner. That means that once added, the runtime will take care of it and draw it for you whenever it is necessary. You don't have to do anything for that, and the element stays visible until you remove it or a parent element from the visual tree (or explicitly set its visibility so it is not drawn, of course).</p> <p>With XNA, this is very different. A good way is to imagine it as a flip-book. You can draw something nice on the current frame, but as soon as you turn the page to the next frame, you have to start all over again, with a blank piece of paper. In XNA, there is no automatism that tracks and re-draws objects on the screen for you. In every single render frame you have to draw the whole content that should be visible on the screen from scratch, you always start with that "blank piece of paper". In Silverlight there is also a lot of optimization going on to determine which parts of the screen actually need to be re-drawn. You can easily use the <a href="http://msdn.microsoft.com/en-us/library/system.windows.interop.settings.enableredrawregions(v=vs.95).aspx" target="_blank">EnableRedrawRegions property</a> to turn on a visual demonstration of that concept. In XNA, there's nothing like that. You simply draw the whole screen all the time, 30 times a second (30 is the default target frames per second setting on the Windows Phone platform).</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_14.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_thumb_6.png" width="495" height="382" /></a></p> <h4>The Game Loop</h4> <p>The central part of each game, the heartbeat if you will, is the game loop. To many modern programmers who have spent a lot of time to adopt the principles of object-oriented programming, having an uber-loop that drives the whole application from a central place seems like an awkward idea. Of course those of you with deeper knowledge or a background of Windows programming know the truth, which manifests itself in what is known as the message pump; but this is way beyond the scope of this article. Let's just agree on the fact that as a Silverlight developer, you won't <em>see</em> anything comparable to the game loop of XNA in your applications, ever. The simplest form of a game loop in pseudo code looks like this:</p> <div style="border:1px solid silver;padding-bottom: 4px; line-height: 12pt; overflow-x: auto; overflow-y: auto; background-color: #f4f4f4; margin-top: 20px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; cursor: text; padding-top: 4px; text-align: left;" id="codeSnippetWrapper"> <div style="padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px; text-align: left;border-style: none;" id="codeSnippet"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum1" style="color: #606060;"> </span><span style="color: #0000ff;">while</span> (IsRunning)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum2" style="color: #606060;"> </span>{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum3" style="color: #606060;"> </span> Update();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum4" style="color: #606060;"> </span> Draw();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum5" style="color: #606060;"> </span>}</pre> <!--CRLF--></div> </div> <p>In fact, this is what actually happens. XNA only adds more functionality to that to make the whole concept more robust (like accommodate for timing and/or performance problems) and to provide the developer with additional information and data. We'll take a closer look at it shortly. </p> <p>Having such a game loop often requires a different style of thinking in your programming, which is a problem for a lot of developers who start with game programming. As an application developer, you are used to write methods that complete a task by sequentially performing all the required steps. For example, when you want to copy some files, you would create a method that iterates over all the provided file names and call File.Copy() on each one of them. If you follow good practices and want to avoid that these operations freeze your UI, you move them to a background thread, but your code still would execute all the steps sequentially. In XNA, a lot of operations are not performed like that. In each iteration of the game loop, you only perform a tiny step that works towards the final result and then you stop until the next update. In the mentioned sample, that would mean that you would only copy a small part of one file, and then stop and return control to the runtime. In the next iteration, you'd resume your operation, copy another small part, and stop again etc.</p> <p>Of course you won't find yourself copying a lot of files in XNA. A better example for this approach are animations, something we'll closely look at in a later part of the series. I've often seen that beginners create a "loop within the loop" and try to perform the logic of the whole animation within a single frame – which of course results in no visual effect at all. Instead, you need to learn these "baby steps" that require only tiny progress updates for the current frame, but sum up to the overall result you want to achieve in the long run. An interesting detail is that Silverlight's built-in animation system has to do exactly the same behind the scenes. Of course those details are hidden from the developer and the runtime does all that logic in this case; you only control those animations by simply setting some properties.</p> <h3></h3> <h3>Your First XNA Project</h3> <p>After all this theoretical introduction you're probably eager to start with some code, so we'll finally dive into the real stuff now. After you've downloaded and installed the required tools for developing games for Windows Phone 7 (you can find them as web installer <a href="http://go.microsoft.com/?linkid=9713250" target="_blank">here</a>) you can create your first game project in Visual Studio/Game Studio:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_2.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border-width: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_thumb.png" width="644" height="413" /></a></p> <p>As you can see there are only two templates for Windows Phone, one for a game, and a second one for a class library. For now, select the game template, enter a name and hit the OK button. The following screenshot shows the structure of your solution after Visual Studio has prepared everything:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_image_4.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_image_thumb_1.png" width="443" height="600" /></a></p> <p>The first thing to note is that actually two projects have been created. The game itself is the first one in solution explorer, but below that there's a second entry for the content project. As you can see the game project references the content project using a "Content Reference". Visual Studio has also added some default references to common assemblies, like parts of the XNA library used for rendering and input in the game project, and various importers in the content project.</p> <h3>The Content Project</h3> <p>A full discussion of the XNA content pipeline is far beyond the scope of this article; however, you need to understand some of the basics and reasons how and why content in XNA games is separated from the actual game. "Content" is everything in your game project that is not executable code. Among game developers, the more common term for this kind of content probably is "assets". Your game assets include all graphics and related things (like textures, but also effects, 3D models and even fonts), audio (music, sound effects) and also other accompanying content, for example level data files or similar.</p> <p>So instead of adding these assets to your game project, you add them to the content project. Let's just do that and add an existing image. Right-click the content project and select "Add/Existing item...". Browse to some image file on your hard disk and import it. You can also safe the following image file to your hard disk and use it, or download the source code at the end of the article:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Block_2.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="Block" alt="Block" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/Block_thumb.png" width="40" height="40" /></a></p> <p>For a better understanding of what happens with this imported image, select it and take a look at the properties window of Visual Studio:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_image_6.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/_image_thumb_2.png" width="416" height="238" /></a></p> <p>Your assets are handled in a two-stage process. First they are read by an importer that knows how to treat that particular input format and transform it into a unified intermediate format. That means that no matter if e.g. your input image is a JPG or PNG, after the import process it will be available in the content pipeline as the same representation for further processing. The second step is a content processor that transforms the imported asset into the final, compiled format that is later used in the game. This separation adds a lot of flexibility to the overall process. For example, a sound file can be both processed into a sound effect as well as a song (we'll learn the difference between those two in a later part). Importing the file always is the same and can be done by the same importer in both cases. However, the final transformation requires two different processors.</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_16.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border-width: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_thumb_7.png" width="640" height="119" /></a></p> <p>For most file types, the required helpers for the content processing are detected automatically, but you can always override these. Of course you can also extend the content pipeline with your own importers and processors, to add the ability to use non-standard formats, or to create custom formats which you can use directly in your game later. We will briefly see how that works in a later part.</p> <h4>Reasons for the Content Pipeline</h4> <p>Why do we have this separation in place for XNA games, when we simply could add the images as resources or content to the game project itself? In fact, even a newly created game project (see above) already has some images added to it for the background and the game thumbnail! The main reason the assets are processed separately is (as often) improved performance. An important detail of the content pipeline is that the assets are imported and transformed <em>at compile time</em>. This means that when you build your solution, all your assets are processed and transformed into managed objects. Loading and handling of these objects at runtime then can be performed very fast, as they will always have the same format. Imagine the game needed to load different image formats at runtime. Not only may importing some formats (that use a more sophisticated compression, for example) be much slower than others, there might also be several sub-formats or encoding differences that add more complexity to the process even when you apparently use the same format for all resources.</p> <p>Of course another reason is that you have more flexibility what programs you can use to generate your assets. The content pipeline supports the most common formats out of the box; for example, it doesn't matter if your images are BMPs, JPGs, PNGs, TGAs or in DDS format. All of those can be imported, which spares you the manual conversion or looking for suitable exporters for your graphics software. Some of these formats could not be used in the game project natively. The content pipeline also allows to perform additional steps automatically during the import, like creating multiple versions of a texture of different sizes ("mipmaps"), which otherwise you'd also have to do manually in an extra tool.</p> <p>There have also been discussions about drawbacks of the content pipeline though. For example, by default XNA uses an uncompressed format for textures, which increases the size of your images significantly when the source files actually are compressed images like JPGs or PNG.s In turn, loading times may actually suffer. The texture content processor has the ability to compress images using the DXT format which can be activated in the properties window of an image. But then again this requires that your texture size is a power of two, which might not be the case and can result in additional (manual) work you need to perform to prepare your images, which kind of contradicts the idea of a smooth and comfortable import process.</p> <h3>The Game Project</h3> <p>After you've created the solution, the game project is already fully functional. You can simply hit F5 and it will start running and show an empty screen in cornflower blue. To this end Visual Studio uses the emulator that ships with the developer tools for Windows Phone:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_8.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_thumb_3.png" width="132" height="244" /></a></p> <p>This means the template already initializes everything in a way that there's already some visual experience, and theoretically you could start adding your own content and game logic right away. However, the initialization is not really complete. For example, when you take a closer look at the emulator you will realize that the game (which really only is a blue rectangle at the moment) doesn't occupy the whole screen; there's a black border around it. That is why we should take a look at some of the initialization options before we actually start adding our logic. </p> <p><em><strong>An important note on the emulator:</strong></em> The emulator for Windows Phone 7 is great. It's not perfect, has its flaws, is missing some possibilities, and you might see some stuttering with games and similar glitches, but compared to previous emulators for Windows CE/Mobile my experience is that it's much better and faster in most areas, especially at startup and deployment of your projects. And it allows you to test your games easily without access to an actual device. However, <em>please never publish a game to the market place without testing it on a real device first!</em> The emulator actually is faster than real devices, and there are even some special cases with really huge differences. Some of these things will be emulated more accurately in the first major update of the platform ("Mango") and the corresponding development tools, for example the time it takes to check for trial mode. But there will still be huge differences regarding rendering performance even in this updated version, so please take this advice seriously. And of course it's always a good idea to use a real device to make sure your concepts (like input and the intended way of controlling your game) really work and feel right on actual hardware.</p> <h4>The Basic Setup </h4> <p>Take a look at the source code of the Game1.cs file. Each XNA game derives from a built-in Game class that provides much of the required basic logic. One can say that with XNA, Microsoft has managed to neatly hide most of the difficult details required to set up a DirectX based game. When you're using DirectX in C++, or even in former attempts to bring DirectX to the managed world (Managed DirectX), you had and have to care about pesky details like acquiring and resetting lost devices and surfaces and other things, all of which is now handled for you behind the scenes. It really never has been easier to create games using this technology.</p> <p>When you take a look at the constructor of the class, you see some of the initialization code:</p> <div style="border:1px solid silver;padding-bottom: 4px; line-height: 12pt; overflow-x: auto; overflow-y: auto; background-color: #f4f4f4; margin-top: 20px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; cursor: text; padding-top: 4px; text-align: left;" id="codeSnippetWrapper"> <div style="padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px; text-align: left;border-style: none;" id="codeSnippet"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum1" style="color: #606060;"> </span><span style="color: #0000ff;">public</span> Game1()</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum2" style="color: #606060;"> </span>{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum3" style="color: #606060;"> </span> graphics = <span style="color: #0000ff;">new</span> GraphicsDeviceManager(<span style="color: #0000ff;">this</span>);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum4" style="color: #606060;"> </span> Content.RootDirectory = <span style="color: #006080;">"Content"</span>;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum5" style="color: #606060;"> </span> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum6" style="color: #606060;"> </span> <span style="color: #008000;">// Frame rate is 30 fps by default for Windows Phone.</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum7" style="color: #606060;"> </span> TargetElapsedTime = TimeSpan.FromTicks(333333); </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum8" style="color: #606060;"> </span>}</pre> <!--CRLF--></div> </div> <p>The graphics device manager does exactly what its name says, it manages the graphics device for you, and allows to manipulate various properties of that device through simple property values and methods. Three things you most likely want to configure here are full-screen mode, the supported screen orientation(s) and resolution. </p> <p>To support full-screen mode, you only need to set the corresponding property accordingly. The same is true for the supported screen orientations. This is a bit flag enum, which means you can combine multiple values to indicate that your game supports multiple screen orientations. In most cases though, implementing support for different orientations is complex, and sometimes doesn't even work without changing game mechanics, so it's totally valid to only support one orientation.</p> <p>You should also set the back buffer dimensions, depending on the screen orientation. Think of the back buffer as the canvas you are drawing on; it is a direct representation of the screen, so usually its dimensions will be those of the device screen's native resolution (480x800 in portrait mode, or 800x480 in landscape mode). You can also render your game at smaller resolutions and make use of the built-in hardware scaler to zoom it to full screen size (it does that automatically). This will improve performance dramatically (at the cost of reduced visual quality), but it's usually not necessary, especially for simple games. Note the naming of these properties ("Preferred...") which indicates that your settings will automatically be discarded when you set it to non-supported values (e.g. values larger than what is supported by the device).</p> <div style="border:1px solid silver;padding-bottom: 4px; line-height: 12pt; overflow-x: auto; overflow-y: auto; background-color: #f4f4f4; margin-top: 20px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; cursor: text; padding-top: 4px; text-align: left;" id="codeSnippetWrapper"> <div style="padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px; text-align: left;border-style: none;" id="codeSnippet"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum1" style="color: #606060;"> </span><span style="color: #0000ff;">public</span> Game1()</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum2" style="color: #606060;"> </span>{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum3" style="color: #606060;"> </span> graphics = <span style="color: #0000ff;">new</span> GraphicsDeviceManager(<span style="color: #0000ff;">this</span>);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum4" style="color: #606060;"> </span> graphics.IsFullScreen = <span style="color: #0000ff;">true</span>;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum5" style="color: #606060;"> </span> graphics.SupportedOrientations = DisplayOrientation.Portrait;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum6" style="color: #606060;"> </span> graphics.PreferredBackBufferWidth = 480;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum7" style="color: #606060;"> </span> graphics.PreferredBackBufferHeight = 800;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum8" style="color: #606060;"> </span> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum9" style="color: #606060;"> </span> Content.RootDirectory = <span style="color: #006080;">"Content"</span>;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum10" style="color: #606060;"> </span> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum11" style="color: #606060;"> </span> <span style="color: #008000;">// Frame rate is 30 fps by default for Windows Phone.</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum12" style="color: #606060;"> </span> TargetElapsedTime = TimeSpan.FromTicks(333333); </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum13" style="color: #606060;"> </span>}</pre> <!--CRLF--></div> </div> <p>The Content property of the game class is a content manager instance that allows you to access the assets you have added in your content project. It's set up for you automatically (you can also use multiple instances of content managers, but in the beginning this will not be necessary), all you have to do is set the RootDirectory property value. You can structure your assets in the content project by creating folders (for example for the different asset types, or to group assets by level or similar). All content however is located below a (virtual) root directory. The name of that root directory is something that you can change in the properties of the content project. By default it is set to "Content" both in the content project and here in the constructor of the game class, so if you just leave it at that it will work without any additional changes. </p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/content%20root%20dir_2.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;border-width: 0px;" title="content root dir" alt="content root dir" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/content%20root%20dir_thumb.png" width="616" height="332" /></a></p> <p>The last line of code in the constructor indirectly sets the desired frame rate you want to achieve by setting the target elapsed time. This is part of the sophistication I have talked about before, which is added to the game loop by XNA. What you basically say is that your game logic should be updated 30 times a second. XNA will try to adhere to this settings as good as possible. If your game is not very demanding (as in finishing an iteration of the game loop is faster than the target time), it will idle and wait for the next update to be triggered. On the other hand, if your game is complex or poorly optimized, the game loop may fall behind and not be able to provide 30 frames per second. In that case, the game will try to keep up by skipping drawing calls, and let your game logic update multiple times in succession (as drawing is way less important than updating your logic, which may break things if it doesn't happen as expected).</p> <p>When your update and draw methods are called, XNA will provide you with some values, like the elapsed time since the last call. These values also include a property you can monitor to determine whether the game loop takes too long and XNA needs to skip frames to keep up (<a href="http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.gametime.isrunningslowly.aspx" target="_blank">GameTime.IsRunningSlowly</a>). You can fine-tune all these settings, but you should just keep them at default values for now.</p> <h4>The Game Methods</h4> <p>The rest of your game class contains the main methods you need to create a full game. The comments that have been added by the project template actually are pretty comprehensive and should give you an idea of the particular details.</p> <ul> <li><strong>Initialize</strong>: Here you can add any initialization logic required by your game which is not related to graphics or your content. </li> <li><strong>LoadContent/UnloadContent</strong>: The LoadContent method is called whenever it is necessary to load your game assets. In these methods you make use of the content manager provided by your game to load textures, sounds and other required assets which you have added to your content project before. In the UnloadContent counterpart you can do some clean-up if necessary. Please note that the content manager cleans up any content managed through it automatically, so you don't need to take care of that separately. </li> <li><strong>Update</strong>: This is the place where you update all game logic. For example, it is here where you will take care of user input, performing animations, triggering sounds and handling general things like checking if the player has won or lost the game or updating your AI. By default, this method contains a check for the back button press which per certification requirement needs to exit the game if you're on the main screen. </li> <li><strong>Draw</strong>: Within this method, you draw all content that should be visible on the screen. This includes not only sprites like the player and enemies, or effects, but also things like background graphics, text, menu or message box overlays and similar things. </li> </ul> <h4></h4> <h3>Hallo World!</h3> <p>Let's use all we have learned so far to show the single texture we have imported into the content project on the screen. This will conclude our "Hallo World" project. </p> <h4>Loading Content</h4> <p>First of all, we will use the LoadContent method to actually load the texture. To store it permanently, we'll also add a class field for it:</p> <div style="border:1px solid silver;padding-bottom: 4px; line-height: 12pt; overflow-x: auto; overflow-y: auto; background-color: #f4f4f4; margin-top: 20px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; cursor: text; padding-top: 4px; text-align: left;" id="codeSnippetWrapper"> <div style="padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px; text-align: left;border-style: none;" id="codeSnippet"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum1" style="color: #606060;"> </span>Texture2D block;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum2" style="color: #606060;"> </span> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum3" style="color: #606060;"> </span>...</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum4" style="color: #606060;"> </span> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum5" style="color: #606060;"> </span><span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">override</span> <span style="color: #0000ff;">void</span> LoadContent()</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum6" style="color: #606060;"> </span>{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum7" style="color: #606060;"> </span> <span style="color: #008000;">// Create a new SpriteBatch, which can be used to draw textures.</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum8" style="color: #606060;"> </span> spriteBatch = <span style="color: #0000ff;">new</span> SpriteBatch(GraphicsDevice);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum9" style="color: #606060;"> </span> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum10" style="color: #606060;"> </span> <span style="color: #008000;">// TODO: use this.Content to load your game content here</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum11" style="color: #606060;"> </span> block = Content.Load<Texture2D>(<span style="color: #006080;">"Block"</span>);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum12" style="color: #606060;"> </span>}</pre> <!--CRLF--></div> </div> <p>Simple, is it? You can use the Load method of a content manager to load assets from your compiled content. Simply pass in the name (including the relative path from the root directory, if you've chosen to put your assets into sub folders) and set the required generic type to the type of asset you want to load. In this case, we need to use Texture2D, as we want to load a normal two-dimensional image.</p> <p>You can also see that in the LoadContent method, a SpriteBatch object is created, something we haven't talked about so far. The sprite batch is a helper class that is used to draw two-dimensional images and text on the screen. As the name implies it does that in a batch, which improves performance a lot. Will see how it works in a moment.</p> <h4>Updating the Logic</h4> <p>But first let's add some movement logic to our Hallo World game to make it a bit more interesting. For this, we use the Update method of the game class:</p> <div style="border:1px solid silver;padding-bottom: 4px; line-height: 12pt; overflow-x: auto; overflow-y: auto; background-color: #f4f4f4; margin-top: 20px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; cursor: text; padding-top: 4px; text-align: left;" id="codeSnippetWrapper"> <div style="padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px; text-align: left;border-style: none;" id="codeSnippet"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum1" style="color: #606060;"> </span>Vector2 position;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum2" style="color: #606060;"> </span><span style="color: #0000ff;">double</span> angle;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum3" style="color: #606060;"> </span> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum4" style="color: #606060;"> </span>...</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum5" style="color: #606060;"> </span> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum6" style="color: #606060;"> </span><span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">override</span> <span style="color: #0000ff;">void</span> Update(GameTime gameTime)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum7" style="color: #606060;"> </span>{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum8" style="color: #606060;"> </span> <span style="color: #008000;">// Allows the game to exit</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum9" style="color: #606060;"> </span> <span style="color: #0000ff;">if</span> (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum10" style="color: #606060;"> </span> <span style="color: #0000ff;">this</span>.Exit();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum11" style="color: #606060;"> </span> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum12" style="color: #606060;"> </span> <span style="color: #008000;">// TODO: Add your update logic here</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum13" style="color: #606060;"> </span> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum14" style="color: #606060;"> </span> <span style="color: #008000;">// take the screen width</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum15" style="color: #606060;"> </span> var screenWidth = graphics.GraphicsDevice.Viewport.Width;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum16" style="color: #606060;"> </span> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum17" style="color: #606060;"> </span> <span style="color: #008000;">// calculate the new rotation agle</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum18" style="color: #606060;"> </span> angle -= gameTime.ElapsedGameTime.TotalSeconds;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum19" style="color: #606060;"> </span> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum20" style="color: #606060;"> </span> <span style="color: #008000;">// update the x and y coordinates</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum21" style="color: #606060;"> </span> position.X = (<span style="color: #0000ff;">float</span>)(screenWidth / 2.0 - block.Width / 2.0 + 200 * Math.Cos(angle));</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum22" style="color: #606060;"> </span> position.Y = (<span style="color: #0000ff;">float</span>)(screenWidth / 2.0 - block.Height / 2.0 + 200 * Math.Sin(angle));</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum23" style="color: #606060;"> </span>  </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum24" style="color: #606060;"> </span> <span style="color: #0000ff;">base</span>.Update(gameTime);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum25" style="color: #606060;"> </span>}</pre> <!--CRLF--></div> </div> <p>Positions are stored as vectors in XNA, which is why we need to add a class field of type Vector2 (a two-dimensional vector) for the position of the sprite. We also store the current rotation angle. In the Update method, we first fetch the actual screen width from the graphics device (more specifically from its view port). This value is identical to the value we have set as preferred back buffer width in the constructor of the game class.</p> <p>The following lines update the rotation angle depending on the elapsed game time since the last call of the Update method and then calculate the x and y coordinates for a circular motion. This is the moment to remember the "baby steps" philosophy I have talked about in the beginning of the article. The Update method is called 30 times a second, which means that the gameTime.ElapsedGameTime property will be a very small time span (ideally 33.3 milliseconds). We need to adjust the motion values by similar very small values to create a smooth movement that is not too fast. To this end, we make the calculation dependent on that elapsed time value passed into the Update method. You will always see this approach for calculations, and over time you will become used to it. It's pretty easy to create the desired velocity for a movement with this. For example, let's say you want to move an object by 200 pixels a second, then you simply multiply that reference value (200 pixels/second) by the elapsed seconds since the last update (e.g. 0.033 seconds) and get the number of pixels you need to move the object in the current frame (6.67 pixels). In our example, we increase the angle by the elapsed seconds. Since a full rotation requires 2 * PI ~= 6.3 radians, this is the number of seconds the sprite needs to complete a circle.</p> <p><em>Tip: It's a great idea to agree on one unit for all these calculations across your whole application. For example, if you use seconds in one part and milliseconds in other parts, things can easily become confusing when you need to connect both parts at a later point. I prefer to use seconds.</em></p> <h4></h4> <h4>Output to the Screen</h4> <p>On to the Draw method! If you look at the draw method, you can see that it contains a single call to GraphicsDevice.Clear(), which takes a color as argument. That is where the cornflower blue comes from. Of course you can change that to anything you like. In addition, add the following code:</p> <div style="border:1px solid silver;padding-bottom: 4px; line-height: 12pt; overflow-x: auto; overflow-y: auto; background-color: #f4f4f4; margin-top: 20px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; cursor: text; padding-top: 4px; text-align: left;" id="codeSnippetWrapper"> <div style="padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px; text-align: left;border-style: none;" id="codeSnippet"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum1" style="color: #606060;"> </span><span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">override</span> <span style="color: #0000ff;">void</span> Draw(GameTime gameTime)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum2" style="color: #606060;"> </span>{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum3" style="color: #606060;"> </span> GraphicsDevice.Clear(Color.DarkGoldenrod);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum4" style="color: #606060;"> </span> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum5" style="color: #606060;"> </span> <span style="color: #008000;">// TODO: Add your drawing code here</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum6" style="color: #606060;"> </span> spriteBatch.Begin();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum7" style="color: #606060;"> </span> spriteBatch.Draw(block, position, Color.Red);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum8" style="color: #606060;"> </span> spriteBatch.End();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum9" style="color: #606060;"> </span> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum10" style="color: #606060;"> </span> <span style="color: #0000ff;">base</span>.Draw(gameTime);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum11" style="color: #606060;"> </span>}</pre> <!--CRLF--></div> </div> <p>That is how you use the sprite batch. You tell it to begin a batch, queue up all drawing operations, and in the end call the End() method to have it process all the operations you have just added. For our simple project, we only have one thing to do: draw the block texture. The Draw method of the sprite batch has several overloads that let you resize or rotate the texture that should be drawn. You can also add a tint, which I did by setting the color argument to red. This is especially handy when you have several similar objects in your game that only are different in their color. You can then use a grey scale version of the texture and add the color here.</p> <p>If you compile and run the project, you shouldn't receive any errors, and the result looks like this:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_10.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_thumb_4.png" width="261" height="484" /></a></p> <p>There you go, your first XNA project. I strongly encourage you to play with the different features we have learned today. Change the supported orientations, the back buffer resolution, the movement of the sprite, and make use of some of the overloads of the sprite batch draw method.</p> <h3>A word on "Mango"</h3> <p>What this article demonstrates stays valid for the first platform update (code name "Mango") without exceptions, if you are creating an XNA game. However, Mango offers new project types that mix XNA and Silverlight in the same application; this feature changes the basic setup and the way the update and draw logic is executed. If you are interested in these new possibilities and how to use them, please read <a href="http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-12-Mango-1.aspx">part 12</a> of this series to learn more about it.</p> <h3>Summary</h3> <p>In this article we've learned some of the major differences in programming style between XNA and Silverlight. We have seen a few of those differences in the sample project, and finished by creating a small game application that already moves some content around the screen. You should also have developed an understanding for the concept of content and how content management in XNA works. You can download the full source code of the sample project here:</p> <a href="http://www.silverlightshow.net/Storage/Sources/MyFirstXNAProject.zip">Download Source Code</a> <p>This article was quite lengthy and covered a lot of the details we needed to finally get some tangible results; Hopefully it still was an interesting read for you and has whet some appetite for the things that will follow. Now that we have created a basis we can use to build upon, I will try to keep the next parts shorter so they can be digested a bit easier and make more of a lighter read. Feel free to leave any comments and feedback, and of course also suggestions what can be improved or what you want to see in future episodes.</p> <h3>About the Author</h3> <p>Peter Kuhn aka "Mister Goodcat" has been working in the IT industry for more than ten years. After being a project lead and technical director for several years, developing database and device controlling applications in the field of medical software and bioinformatics as well as for knowledge management solutions, he founded his own business in 2010. Starting in 2001 he jumped onto the .NET wagon very early, and has also used Silverlight for business application development since version 2. Today he uses Silverlight, .NET and ASP.NET as his preferred development platforms and offers training and consulting around these technologies and general software design and development process questions.</p> <p>He created his first game at the age of 11 on the Commodore 64 of his father and has finished several hobby and also commercial game projects since then. His last commercial project was the development of Painkiller:Resurrection, a game published in late 2009, which he supervised and contributed to as technical director in his free time. You can find his tech blog here: <a href="http://www.pitorque.de/MisterGoodcat/">http://www.pitorque.de/MisterGoodcat/</a></p> http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-1-Fundamentals.aspx editorial@silverlightshow.net (Peter Kuhn ) http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-1-Fundamentals.aspx#comments http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-1-Fundamentals.aspx Tue, 18 Jan 2011 02:12:00 GMT XNA for Silverlight developers: Part 0 - Why should I care? <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; padding-top: 5px; text-align: center;">Are you interested in <strong>creating games for Windows Phone 7</strong> but don't have XNA experience? <br /> <a href="http://www.silverlightshow.net/video/XNA-for-WP7-Webinar.aspx">Watch the recording</a> of the recent intro webinar delivered by Peter Kuhn '<strong>XNA for Windows Phone 7</strong>'.<a href="https://www1.gotomeeting.com/register/256344145" target="_blank"></a><br /> </div> <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; width: 196px; float: right; height: 401px; margin-left: 10px; margin-right: 5px; padding-top: 5px;"> <h3>Don't miss...</h3> <ul style="list-style-type: circle; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-left: 20px; font-size: 12px;"> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/WP7-What-is-Windows-Phone-7.aspx">What is Windows Phone series</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Exploring-Silverlight-XNA-integration-on-Windows-Phone-7.aspx">XNA article by Levente Mihály</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Windows-Phone-7-Part-1-Getting-Started.aspx">WP7 series by Andrea Boschin</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx">The XNA series is now available as a fully offline resource</a> </li> </ul> <p style="padding-bottom: 5px; text-align: center;"><a href="http://www.silverlightshow.net/ebooks/ebook_xna.aspx"><img style="border:0px solid;" alt="XNA for Silverlight Developers Ebook" src="http://www.silverlightshow.net/storage/thumb_xna_ebook_cover_150_0.png" usemap="#rade_img_map_1291385581316" /></a></p> <p style="font-size: 12px;">     <a href="http://www.silverlightshow.net/ebooks.aspx">All SilverlightShow Ebooks</a> <img alt="" src="http://www.silverlightshow.net/Storage/arrow-content.jpg" /></p> </div> <p>This article is Part 0 of the series “XNA for Silverlight developers”.</p> <p><strong><strong>This article is compatible with Windows Phone "Mango". <em><br /> Latest update: Aug 1, 2011.</em></strong></strong></p> <h3>Introduction</h3> <p>Around the time the first Windows Phone 7 devices were released to the market, one popular sentence you heard was "every Silverlight developer is a Windows Phone 7 developer" – and that's true. Silverlight is Microsoft's main platform to do Windows Phone 7 development, and every desktop Silverlight programmer will feel comfortable in the new mobile programming environment instantly. Sure there are differences and libraries specific to the devices, but you won't have to learn a new programming language or new ways to define your UI, and you can use the same development environment you've been using for normal Silverlight development all the time. That's great!</p> <p>However, Silverlight is not the only possibility to create software for Windows Phone 7 devices, there's also XNA. Most people that are interested in developing for the new mobile platform probably have already read about it, mostly in the context of game programming. This article tries to give a quick overview of XNA on the mobile platform for Silverlight developers who have not dealt with XNA in detail before. It explains the possibilities of interaction between Silverlight and XNA, and why you as a Silverlight developer should care about XNA even if you don't want to develop games.</p> <p>In successive parts of this series, we'll go into the details of programming 2D games for Windows Phone 7 using XNA, with special focus on those parts that require a change of thinking when you are coming from a Silverlight background. I strongly encourage you to comment on each part and request more information about topics you are interested in. If possible, your feedback will directly result in the design of future articles.</p> <h3>The Windows Phone 7 Platform</h3> <p>The base of all development on the Windows Phone 7 platform is a .NET Framework managed runtime code sandbox based on the Compact Framework know from previous mobile platforms (Windows CE/Mobile). This means that all application development for the phone devices is done in managed code and in a protected sandbox. On top of that, the following structure is established:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_4.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_thumb_1.png" width="585" height="350" /></a></p> <ul></ul> <p>As you can see, both Silverlight and XNA share a great amount of the available functionality. The base class library provides all the basic plumbing needed in most software and is similar to what you know from other platforms like desktop Silverlight. Neither this base class library nor most of the provided services specific to the Windows Phone 7 platform are tied to Silverlight or XNA per se. In fact, it's even possible for great parts of Silverlight and XNA to interact with each other and to be used side-by-side. Let's see how that works.</p> <h3>XNA and Silverlight Interop</h3> <p>When a .NET or Silverlight developer reads "interop", they often think of painstaking work ahead. For Win32 interop, you need to import operating system functions, map data types and marshal between the managed and unmanaged world. For COM interop in Silverlight, you need to know the API by heart or look up every detail in its documentation, as you don't have any built-in help like Intellisense in Visual Studio which could guide you through the process of interacting with the unmanaged component. In this situation however, things are different.</p> <p>Since all development for Windows Phone 7 happens to be managed code in C# or Visual Basic .NET, and both Silverlight and XNA manifest themselves as libraries which can be referenced and accessed natively in managed code, the borders between both are easily blurred. The transition between both worlds in fact is so seamless that developers often don't even realize when they cross these borders. It's likely that even if you as a Silverlight developer have never consciously used XNA, an application of yours did, behind the scenes, without you even noticing.</p> <h4>A concealed example</h4> <p>A very prominent example for this is the Gesture Service and Gesture Listener that can be found in the Silverlight for Windows Phone Toolkit. These classes provide an easy way to access gestures like flicks or pinching in your Silverlight application. When you look at the <a href="http://silverlight.codeplex.com/SourceControl/changeset/view/57505#1325103" target="_blank">source code</a> however, you will realize that this construct is only a shallow wrapper around the Input.Touch namespace functionality from the XNA framework. It adds some logic to recognize gestures and to translate from XNA's polling-based model to the event-driven nature of Silverlight. To many people this comes as a surprise, and they're not aware it's actually XNA when they're using these features.</p> <h4>Limitations of Silverlight</h4> <p>In some areas we also have some limitations in Silverlight when it comes to making full use of the phone device features. In these cases, it's a good idea to take a quick look at XNA, because sometimes it offers superior possibilities in the same areas, which simply are not available in Silverlight. A very simple example of this is the message box. Silverlight's API for this is very limited:</p> <div style="border:1px solid silver;padding-bottom: 4px; line-height: 12pt; overflow-x: auto; overflow-y: auto; background-color: #f4f4f4; margin-top: 20px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; cursor: text; padding-top: 4px; text-align: left;" id="codeSnippetWrapper"> <div style="padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px; text-align: left;border-style: none;" id="codeSnippet"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum1" style="color: #606060;"> </span>MessageBox.Show(<span style="color: #006080;">"Hallo World"</span>,</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum2" style="color: #606060;"> </span> <span style="color: #006080;">"This is a caption"</span>,</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum3" style="color: #606060;"> </span> MessageBoxButton.OK);</pre> <!--CRLF--></div> </div> <p>This is about all you get. You can show a text with a caption, and decide whether you want to show the OK button or both OK and Cancel buttons. In XNA though, there is the Guide helper class, which let's you do something like that:</p> <div style="border:1px solid silver;padding-bottom: 4px; line-height: 12pt; overflow-x: auto; overflow-y: auto; background-color: #f4f4f4; margin-top: 20px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; cursor: text; padding-top: 4px; text-align: left;" id="codeSnippetWrapper"> <div style="padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px; text-align: left;border-style: none;" id="codeSnippet"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum1" style="color: #606060;"> </span>Guide.BeginShowMessageBox(<span style="color: #006080;">"This is a caption"</span>,</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum2" style="color: #606060;"> </span> <span style="color: #006080;">"Hallo World"</span>,</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum3" style="color: #606060;"> </span> <span style="color: #0000ff;">new</span> <span style="color: #0000ff;">string</span>[] { <span style="color: #006080;">"First button"</span>, <span style="color: #006080;">"Second button"</span> },</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum4" style="color: #606060;"> </span> 1,</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum5" style="color: #606060;"> </span> MessageBoxIcon.Alert,</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum6" style="color: #606060;"> </span> <span style="color: #0000ff;">null</span>,</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; overflow-x: visible; overflow-y: visible; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; padding-top: 0px;border-style: none;"><span id="lnum7" style="color: #606060;"> </span> <span style="color: #0000ff;">null</span>);</pre> <!--CRLF--></div> </div> <p>You can specify the number of and labels for the buttons yourself, and when you pass in different values for the MessageBoxIcon argument, you will get different behaviors for your message box (e.g. different sounds). Another thing to note is that this message box is non-blocking by default, which is also not possible in Silverlight; but it also means you have to put extra effort into your code if you want to make it blocking.</p> <p>Other areas where XNA offers features that are not available in Silverlight are:</p> <ul> <li>Certain audio features like playing multi-channel sounds. </li> <li>Advanced sound effects, for example changing parameters like pitch and similar on the fly while the sound is playing. </li> <li>Recording audio using the phone's microphone. </li> <li>Interaction with the media library to load songs, videos and pictures (without using the PhotoChooserTask) and also to save pictures back to the media library. </li> <li>The previously mentioned input gestures, which are now also available by using the Toolkit wrapper. </li> </ul> <h4>Limitations of interop</h4> <p>Although you can use great parts of both worlds in the same application, there are some restrictions to the possibilities of interop between XNA and Silverlight. To be more specific, the official certification requirements for the RTM release published by Microsoft contain a paragraph that says:</p> <blockquote> <p><em><strong>4.2.5</strong> The application must not call any APIs in the Microsoft.Xna.Framework.Game <br /> assembly or the Microsoft.Xna.Framework.Graphics assembly when using any methods from <br /> the System.Windows.Controls namespace.</em></p> </blockquote> <p>That's a heavy one. A lot of developers would love to pick the best of two worlds and mix visual elements from XNA and Silverlight in their applications. XNA does not have a real concept of controls, and hence does not ship with a set of built-in UI controls, so it would be great to be able to use Silverlight controls for your game's menu system, for example. And the other way round, a great benefit for Silverlight developers would be to use XNA in parts of their application, for example when they need the superior rendering capabilities and performance, or native 3D support. Unfortunately, with the above restriction in place, none of this is possible in the RTM release.</p> <p>The good news however is that this limitation is eased in the first major update of the platform (code name "Mango"). Starting with this version of Windows Phone, developers will be able to mix XNA and Silverlight in the same application. There still are restrictions in place (for example, you still won't be able to use anything from the Game assembly), and doing this requires to choose a certain solution/project setup where Silverlight takes the leading role; however, it allows you to use the best from Silverlight and still have XNA rendering parts of your game or application, or even mix elements of both technologies on the same page. We will see how this works in more detail in <a href="http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-12-Mango-1.aspx">part 12</a> of this series.</p> <h3>Windows Phone 7 as a Gaming Platform</h3> <p>From the beginning, Windows Phone 7 has aggressively been promoted as a gaming platform. Big players in the industry like Electronic Arts are developing games for this platform, and you will find a lot of well-known brands in the market place, from The Sims™ to Assassin's Creed™ to Guitar Hero 5 and a lot more. In 2011's CES keynote from Steve Ballmer, a big part of the Windows Phone 7 presentation was the show case of current and future games. Obviously gaming is a major part of Microsoft's strategy for these devices.</p> <p>For smaller companies or hobby developers, the mobile platform offers the opportunity to develop casual games people can play wherever they are. Some very remarkable games are already available and prove that an addictive idea does not necessarily need a big publisher or developer team to make a successful appearance on the phone. Game development apparently is an area developers for Windows Phone 7 are very interested in. At the moment of writing this, the market place offers around 5900 apps for download, and out of this 1340 are games. This makes games by far the category with the most entries:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_6.png"><img style="border:0px solid; background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px;" title="image" alt="image" src="http://www.silverlightshow.net/Storage/Users/MisterGoodcat/image_thumb_2.png" width="483" height="291" /></a></p> <p>In the following parts, I'll go into the details of what considerations you need to make when you want to develop games for Windows Phone 7.</p> <h3>Choosing between Silverlight and XNA</h3> <p>Because XNA is a dedicated framework for game development, a lot of people perceive this as the <em>only</em> possibility to create games for Windows Phone 7; some even think it is a certification requirement to use XNA when you want to publish applications to the game section of the market place. This could not be more far from the truth. You are free to also choose Silverlight as your development tool for game creation. In fact, I know of several games in the market place that are made with Silverlight, and your decision on what technology to use should be made carefully to optimize your development process in terms of efficiency, depending on your requirements.</p> <h4>When to choose XNA</h4> <p><strong>3D support – </strong>This is a major argument for XNA. If you need that extra dimension, it's almost certain that XNA which is built on Direct3D will be the better choice. You have excellent support for 3D graphics with it, and if you have worked with 3D technology before (not necessarily XNA, but also native Direct3D or even OpenGL) the involved concepts and theory will be very familiar to you and make the transition easy. Although it has some limitations (for example in terms of shader support), XNA on the phone basically has the same features as Direct3D 9. Silverlight on the other hand has no real support for 3D, and it would require a lot of work to even get some of the basic things done.</p> <p><strong>Performance – </strong>When you are about to create a visually complex game, probably with a lot of effects and moving elements, it's advisable to use XNA. It has been optimized for performance and GPU rendering. Silverlight's layouting system is more comfortable, but that comes at the cost of lower performance in these situations, and when you have to do a lot of blending and work with transparency a lot (particle effects and the like), performance may suffer with Silverlight.</p> <p><strong>Compatibility with XBox –</strong> If you are planning on developing a game for both the Phone and XBox or even the Windows Desktop, and to share code between those implementations, Silverlight wouldn't be an optimal choice as it is not available on the XBox. XNA is not only a supported framework on the Phone, but also runs on the XBox and Windows desktop systems, which makes it considerably easier to share code for projects targeted at those platforms.</p> <p><strong>Misc –</strong> There are some more smaller arguments for XNA. Using the built-in content pipeline to import assets provides a more powerful mechanism to organize your game content, for example. It supports a variety of formats for graphics and sounds that can be imported and converted without additional work, and you can even write your own importers for custom formats that integrate seamlessly into your developer experience if you like.</p> <h4></h4> <h4>When to choose Silverlight</h4> <p><strong>Comfort –</strong> Silverlight's development experience, especially compared to XNA, is a pleasure in a lot of situations. The design-time support of tools like Expression Blend alone can be a huge boost in productivity. That story continues when it comes to the rich set of built-in controls you can use in Silverlight (buttons, text controls, items controls etc.); you have to create those all manually in XNA, from scratch, if you need something comparable. Then there's the layout system and controls that do all the heavy lifting for you when it comes to arranging content automatically – in XNA you'll find yourself calculating positions and coordinates for these things a lot. So if your game depends a lot on user interaction with standard controls, or you can make good use of the built-in Silverlight features or use the available tools to cut your development time in half, Silverlight should definitely be a consideration for you.</p> <p><strong>Compatibility with the web browser – </strong>We have talked about how XNA is great when you want to develop for both the phone and XBox. The same is true for Silverlight if you aim to create a game for both the phone and the web. It is unlikely that you can reuse your UI for a desktop Silverlight application. But again, if you plan on sharing code between the two platforms and plan ahead, Silverlight might be the better choice, as XNA's programming model is so different from Silverlight's and hence harder to port to a web application.</p> <p><strong>Vector graphics –</strong> Silverlight's rendering system is vector based, even the text. That means that you get some benefits from that for free, for example lossless scaling capabilities. XNA has little to no support for this (text rendering is also bitmap based). So if your game heavily depends on zooming of elements that can be designed as non-bitmaps in Silverlight, you might want to make use of this. </p> <h4></h4> <p><strong>Misc –</strong> As with XNA, there are also minor arguments for Silverlight for specific scenarios. For example, if you want to work with video content, then Silverlight offers great flexibility with the media element. XNA on the other hand only supports full-screen video playback, which means you cannot embed video nicely into your application.</p> <h4></h4> <h3></h3> <h4>When to choose both</h4> <p>As mentioned above, starting with Windows Phone "Mango", you will be able to mix both technologies in the same application. This is an attractive alternative for a lot of scenarios, and it allows you to easily make use of the best from XNA (like 3D support, the better performance, the content pipeline and more) and Silverlight (the comfort of having a layout and animation engine, pre-built controls, vector graphics and other things) at the same time.</p> <p>You may ask yourself why you shouldn't <em>always</em> use this kind of integrated application. One major argument against this is the worse portability of your code. XNA is a technology that supports not only Windows Phone, but has been established for years on other platforms like the PC and XBox. If you want to target those other platforms with your game too ("three screens"), then one of your goals of course is to reuse as much code as possible. Once you have decided to make use of the Silverlight-XNA-integration of Mango though, your code will need some adjustments (like not relying on the Game class anymore and others), and that will likely make it harder to achieve this goal.</p> <p>Another point of course is that this feature requires your end users to have Windows Phone "Mango" on their devices. The rollout of this new version only starts in the near future, and it will surely take months from now for the majority of users to transition to it. Until then, if you target Mango features like this exclusively, you are limiting the reach of your application and the number of potential customers. Of course this is only a temporary issue that will be solved over time, but depending on your release schedule it might be a big issue.</p> <h3>Summary</h3> <p>In this article we have learned that Silverlight and XNA, although very different in their programming models, are not that far apart on Windows Phone 7. Both are based on a common foundation, and parts of the libraries can be and already are shared between those two worlds easily.</p> <p>We have also seen that game development for the new mobile platform is not something exclusive to XNA, but can be accomplished using Silverlight too. In fact, both technologies have their advantages when it comes to effectively programming games for Windows Phone 7.</p> <p>I hope that by giving a glimpse of XNA, I've sparked some interest for those who had no motivation to delve into it so far. In the following parts of the series, we will shed light on several aspects of programming games with XNA and also focus on the differences to Silverlight's programming model to ease the learning curve for those who didn't engage in XNA development yet.</p> <p>Please feel free to add your comments and thoughts, or contact me directly with any questions you have.</p> <h3>About the Author</h3> <p>Peter Kuhn aka "Mister Goodcat" has been working in the IT industry for more than ten years. After being a project lead and technical director for several years, developing database and device controlling applications in the field of medical software and bioinformatics as well as for knowledge management solutions, he founded his own business in 2010. Starting in 2001 he jumped onto the .NET wagon very early, and has also used Silverlight for business application development since version 2. Today he uses Silverlight, .NET and ASP.NET as his preferred development platforms. </p> <p>He created his first game at the age of 11 on the Commodore 64 of his father and has finished several hobby and also commercial game projects since then. His last commercial project was the development of Painkiller:Resurrection, a game published in late 2009, which he supervised and contributed to as technical director in his free time. You can find his tech blog here: <a href="http://www.pitorque.de/MisterGoodcat/">http://www.pitorque.de/MisterGoodcat/</a></p> http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-0-Why-should-I-care.aspx editorial@silverlightshow.net (Peter Kuhn ) http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-0-Why-should-I-care.aspx#comments http://www.silverlightshow.net/items/XNA-for-Silverlight-developers-Part-0-Why-should-I-care.aspx Mon, 10 Jan 2011 10:29:00 GMT Exploring Silverlight-XNA integration on Windows Phone 7 <p><em><strong>This article is compatible with the latest version of Silverlight for Windows Phone 7.</strong></em></p> <div style="border:1px solid #dddddd;padding-bottom: 5px; background-color: #f3f3f3; margin-top: 5px; padding-left: 10px; width: 200px; float: right; margin-left: 10px; padding-top: 5px;"> <h3>More on this topic..</h3> <ul style="list-style-type: circle; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-left: 20px; font-size: 12px;"> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/WP7-Stock-Quoting-Demo-Series-Part1.aspx">WP7 Stock Quoting Demo series</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/WP7-What-is-Windows-Phone-7.aspx">What is Windows Phone series</a> </li> <li style="padding-bottom: 5px;"><a href="http://www.silverlightshow.net/items/Building-a-DataGrid-Control-for-Silverlight-for-Windows-Phone-Part-1.aspx">DataGrid Control for WP7 series</a> </li> </ul> </div> <p>Windows Phone 7 platform provides two different frameworks for developing applications: Silverlight and XNA. XNA is a game-specific framework. If you want to write a 3D game, or a loop-based game with a lot of effects, textures and motion, you should choose XNA. So why should a SilverlightShow reader care about XNA? Because the two platforms are more-or-less compatible with each other, meaning you can use XNA assemblies in your Windows Phone 7 Silverlight applications. Here’s a brief introduction to the Silverlight-XNA integration.</p> <p>First of all, you can download source codes for </p> <p>- <a href="http://www.silverlightshow.net/Storage/Sources/MicDemo.zip">MicDemo</a> – recording with microphone</p> <p>- <a href="http://www.silverlightshow.net/Storage/Sources/SoundEffectDemo.zip">SoundEffectDemo</a> – <a href="http://msdn.microsoft.com/en-us/library/bb447687.aspx">MSDN code</a> modified for Silverlight: playing and applying 3D positioning effects to a SoundEffect</p> <p>- <a href="http://www.silverlightshow.net/Storage/Sources/SongDemo.zip">SongDemo</a> – playing back a song</p> <p>- <a href="http://www.silverlightshow.net/Storage/Sources/PictureDemo.zip">PictureDemo</a> – loading and saving pictures with MediaLibrary</p> <h3>XNA assemblies, limitations</h3> <p>In a Silverlight WP7 application we can use all the XNA assemblies except Microsoft.Xna.Framework.Game and Microsoft.Xna.Framework.Graphics. Sadly, this kind of kills the concept of a game with Silverlight menus or a Silverlight application with some 3D XNA graphic.</p> <h3>Enabling XNA</h3> <p>When you first run your Silverlight application that uses the XNA Framework, you might get an exception like this:</p> <p><a href="http://www.silverlightshow.net/Storage/Users/wildfox/FrameworkDispatcher_2.png"><img width="437" height="92" title="FrameworkDispatcher" style="border:0px solid; display: inline;" alt="FrameworkDispatcher" src="http://www.silverlightshow.net/Storage/Users/wildfox/FrameworkDispatcher_thumb.png" /></a> </p> <p>The FrameworkDispatcher.Update method triggers event processing in the XNA Framework. It is automatically called whenever Game.Update is called. In Silverlight though, we don’t have a Game.Update, thus we have to call the FrameworkDispatcher.Update method manually.</p> <p>The easiest way is to create a class like this:</p> <div id="codeSnippetWrapper" style="border:1px solid silver;padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin-top: 20px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-left: 4px; width: 90.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow-x: auto; overflow-y: auto; cursor: text; padding-top: 4px; text-align: left;"> <div id="codeSnippet" style="padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px; text-align: left;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum1" style="color: #606060;"> </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">class</span> XNAAsyncDispatcher : IApplicationService</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum2" style="color: #606060;"> </span> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum3" style="color: #606060;"> </span> <span style="color: #0000ff;">private</span> DispatcherTimer frameworkDispatcherTimer;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum4" style="color: #606060;"> </span> <span style="color: #0000ff;">public</span> XNAAsyncDispatcher(TimeSpan dispatchInterval)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum5" style="color: #606060;"> </span> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum6" style="color: #606060;"> </span> <span style="color: #0000ff;">this</span>.frameworkDispatcherTimer = <span style="color: #0000ff;">new</span> DispatcherTimer();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum7" style="color: #606060;"> </span> <span style="color: #0000ff;">this</span>.frameworkDispatcherTimer.Tick += <span style="color: #0000ff;">new</span> EventHandler(frameworkDispatcherTimer_Tick);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum8" style="color: #606060;"> </span> <span style="color: #0000ff;">this</span>.frameworkDispatcherTimer.Interval = dispatchInterval;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum9" style="color: #606060;"> </span> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum10" style="color: #606060;"> </span> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum11" style="color: #606060;"> </span> <span style="color: #0000ff;">void</span> IApplicationService.StartService(ApplicationServiceContext context)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum12" style="color: #606060;"> </span> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum13" style="color: #606060;"> </span> <span style="color: #0000ff;">this</span>.frameworkDispatcherTimer.Start();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum14" style="color: #606060;"> </span> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum15" style="color: #606060;"> </span> <span style="color: #0000ff;">void</span> IApplicationService.StopService()</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum16" style="color: #606060;"> </span> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum17" style="color: #606060;"> </span> <span style="color: #0000ff;">this</span>.frameworkDispatcherTimer.Stop();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum18" style="color: #606060;"> </span> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum19" style="color: #606060;"> </span> <span style="color: #0000ff;">void</span> frameworkDispatcherTimer_Tick(<span style="color: #0000ff;">object</span> sender, EventArgs e)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum20" style="color: #606060;"> </span> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum21" style="color: #606060;"> </span> FrameworkDispatcher.Update();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum22" style="color: #606060;"> </span> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum23" style="color: #606060;"> </span> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum24" style="color: #606060;"> </span> }</pre> <!--CRLF--></div> </div> <p>and add it to ApplicationLifetimeObjects collection:</p> <div id="codeSnippetWrapper" style="border:1px solid silver;padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin-top: 20px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-left: 4px; width: 90.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; height: 136px; max-height: 200px; font-size: 8pt; overflow-x: auto; overflow-y: auto; cursor: text; padding-top: 4px; text-align: left;"> <div id="codeSnippet" style="padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px; text-align: left;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum1" style="color: #606060;"> </span><span style="color: #008000;">// Constructor</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum2" style="color: #606060;"> </span><span style="color: #0000ff;">public</span> App()</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum3" style="color: #606060;"> </span>{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum4" style="color: #606060;"> </span> ...</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum5" style="color: #606060;"> </span> <span style="color: #0000ff;">this</span>.ApplicationLifetimeObjects.Add(</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum6" style="color: #606060;"> </span> <span style="color: #0000ff;">new</span> XNAAsyncDispatcher(TimeSpan.FromTicks(333333))</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum7" style="color: #606060;"> </span> );</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum8" style="color: #606060;"> </span>}</pre> <!--CRLF--></div> </div> <p>The standard rendering rate for XNA is 30fps so we configure the DispatcherTimer according to that.</p> <h3>Audio and Media</h3> <p>The Microsoft.Xna.Framework.Audio and Media namespaces offer  a lot of options. Playing back sound effects, dynamic sound effects, recording audio with the microphone, applying 3D effects to audio, and the list goes on…</p> <h4></h4> <h4>SoundEffect</h4> <p>The SoundEffect class can be useful in a Silverlight game where you have a lot of sounds, often playing at the same time. Using the SoundEffect class to play a wave sound should be better performance-wise than using Silverlight’s MediaElement. Here’s how to load and play a sound file in a loop with XNA:</p> <div id="codeSnippetWrapper" style="border:1px solid silver;padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin-top: 20px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-left: 4px; width: 90.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; height: 67px; max-height: 200px; font-size: 8pt; overflow-x: auto; overflow-y: auto; cursor: text; padding-top: 4px; text-align: left;"> <div id="codeSnippet" style="padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px; text-align: left;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum1" style="color: #606060;"> </span>SoundEffect se = SoundEffect.FromStream(TitleContainer.OpenStream(<span style="color: #006080;">"hand-clap-1.wav"</span>));</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum2" style="color: #606060;"> </span>soundEffectInstance = se.CreateInstance();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum3" style="color: #606060;"> </span>soundEffectInstance.IsLooped = <span style="color: #0000ff;">true</span>;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum4" style="color: #606060;"> </span>soundEffectInstance.Play();</pre> <!--CRLF--></div> </div> <p>The TitleContainer static class provides filestream access in XNA. In the Play method we can also pass volume, pitch and panning values.</p> <p>It’s very easy to apply positional effects to the sound, by calling the Apply3D method on the SoundEffectInstance, and passing one or more AudioListener instances and an AudioEmitter.</p> <h4>Microphone</h4> <p>Windows Phone 7 provides access to the microphone via the Microsoft.Xna.Audio.Microphone class. The quality is 16-bit Mono.</p> <div id="codeSnippetWrapper" style="border:1px solid silver;padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin-top: 20px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-left: 4px; width: 90.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow-x: auto; overflow-y: auto; cursor: text; padding-top: 4px; text-align: left;"> <div id="codeSnippet" style="padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px; text-align: left;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum1" style="color: #606060;"> </span>Microphone mic = Microphone.Default;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum2" style="color: #606060;"> </span>MemoryStream micStream = <span style="color: #0000ff;">new</span> MemoryStream();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum3" style="color: #606060;"> </span><span style="color: #0000ff;">byte</span>[] buffer;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum4" style="color: #606060;"> </span>SoundEffect recordedSE;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum5" style="color: #606060;"> </span> </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum6" style="color: #606060;"> </span><span style="color: #008000;">// Constructor</span></pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum7" style="color: #606060;"> </span><span style="color: #0000ff;">public</span> MainPage()</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum8" style="color: #606060;"> </span>{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum9" style="color: #606060;"> </span> InitializeComponent();</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum10" style="color: #606060;"> </span> mic.BufferReady += <span style="color: #0000ff;">new</span> EventHandler<EventArgs>(mic_BufferReady);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum11" style="color: #606060;"> </span> mic.BufferDuration = TimeSpan.FromMilliseconds(1000);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum12" style="color: #606060;"> </span> buffer = <span style="color: #0000ff;">new</span> <span style="color: #0000ff;">byte</span>[mic.GetSampleSizeInBytes(mic.BufferDuration)]; </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum13" style="color: #606060;"> </span>}</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum14" style="color: #606060;"> </span>  </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum15" style="color: #606060;"> </span><span style="color: #0000ff;">void</span> mic_BufferReady(<span style="color: #0000ff;">object</span> sender, EventArgs e)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum16" style="color: #606060;"> </span>{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum17" style="color: #606060;"> </span> mic.GetData(buffer);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum18" style="color: #606060;"> </span> <span style="color: #0000ff;">if</span> (isRecording)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum19" style="color: #606060;"> </span> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum20" style="color: #606060;"> </span> micStream.Write(buffer, 0, buffer.Length);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum21" style="color: #606060;"> </span> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum22" style="color: #606060;"> </span>}</pre> <!--CRLF--></div> </div> <p>Now we can start recording by calling the mic.Start() method, and stop recording with mic.Stop(). </p> <p>We can play it back:</p> <div id="codeSnippetWrapper" style="border:1px solid silver;padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin-top: 20px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-left: 4px; width: 90.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow-x: auto; overflow-y: auto; cursor: text; padding-top: 4px; text-align: left;"> <div id="codeSnippet" style="padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; height: 20px; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px; text-align: left;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum1" style="color: #606060;"> </span>SoundEffect recSE = <span style="color: #0000ff;">new</span> SoundEffect(micStream.ToArray(), mic.SampleRate, AudioChannels.Mono);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum2" style="color: #606060;"> </span>recSE.Play();</pre> <!--CRLF--></div> </div> <p>Or save it to the application’s isolated storage:</p> <div id="codeSnippetWrapper" style="border:1px solid silver;padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin-top: 20px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-left: 4px; width: 90.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow-x: auto; overflow-y: auto; cursor: text; padding-top: 4px; text-align: left;"> <div id="codeSnippet" style="padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px; text-align: left;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum1" style="color: #606060;"> </span><span style="color: #0000ff;">using</span> (var isf = IsolatedStorageFile.GetUserStoreForApplication())</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum2" style="color: #606060;"> </span>{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum3" style="color: #606060;"> </span> <span style="color: #0000ff;">using</span> (var isfstream = <span style="color: #0000ff;">new</span> IsolatedStorageFileStream(<span style="color: #006080;">"record.pcm"</span>, FileMode.Create, isf))</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum4" style="color: #606060;"> </span> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum5" style="color: #606060;"> </span> isfstream.Write(micStream.ToArray(), 0, (<span style="color: #0000ff;">int</span>)micStream.Length);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum6" style="color: #606060;"> </span> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum7" style="color: #606060;"> </span>}</pre> <!--CRLF--></div> </div> <h4>Media</h4> <p>The Microsoft.Xna.Framework.Media namespace provides access to the Media Library. With the MediaLibrary class we can play back songs and load pictures from the user’s collection on the device. Saving pictures to the MediaLibrary is also allowed, but this is not true for songs. Another limitation is that we can’t get access to DRM protected music. </p> <p>To load a picture from the media library, we must select the device’s library source. In the following code snippet I’m loading the first picture of the user’s library:</p> <div id="codeSnippetWrapper" style="border:1px solid silver;padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin-top: 20px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-left: 4px; width: 90.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow-x: auto; overflow-y: auto; cursor: text; padding-top: 4px; text-align: left;"> <div id="codeSnippet" style="padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px; text-align: left;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum1" style="color: #606060;"> </span><span style="color: #0000ff;">foreach</span> (var source <span style="color: #0000ff;">in</span> MediaSource.GetAvailableMediaSources())</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum2" style="color: #606060;"> </span>{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum3" style="color: #606060;"> </span> <span style="color: #0000ff;">if</span> (source.MediaSourceType == MediaSourceType.LocalDevice)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum4" style="color: #606060;"> </span> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum5" style="color: #606060;"> </span> ml = <span style="color: #0000ff;">new</span> MediaLibrary(source);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum6" style="color: #606060;"> </span> <span style="color: #0000ff;">if</span> (ml.Pictures.Count > 0)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum7" style="color: #606060;"> </span> {</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum8" style="color: #606060;"> </span> var bitmap = <span style="color: #0000ff;">new</span> BitmapImage(); </pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum9" style="color: #606060;"> </span> bitmap.SetSource(ml.Pictures[0].GetImage());</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum10" style="color: #606060;"> </span> img.Source = bitmap;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum11" style="color: #606060;"> </span> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum12" style="color: #606060;"> </span> <span style="color: #0000ff;">break</span>;</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum13" style="color: #606060;"> </span> }</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum14" style="color: #606060;"> </span>}</pre> <!--CRLF--></div> </div> <p>And we can write to the library using the MediaLibrary.SavePicture method:</p> <div id="codeSnippetWrapper" style="border:1px solid silver;padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin-top: 20px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-left: 4px; width: 90.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; height: 19px; max-height: 200px; font-size: 8pt; overflow-x: auto; overflow-y: auto; cursor: text; padding-top: 4px; text-align: left;"> <div id="codeSnippet" style="padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; height: 26px; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px; text-align: left;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum1" style="color: #606060;"> </span>ml.SavePicture(<span style="color: #006080;">"TestPic"</span>, TitleContainer.OpenStream(<span style="color: #006080;">"Chrysanthemum.jpg"</span>));</pre> <!--CRLF--></div> </div> <p>Regarding the emulator’s Songs library,  it is empty, unlike the Pictures, which contains some sample images. To make things worse, even on the unlocked emulator I couldn’t find any way to upload some mp3s to the emulator’s library.</p> <p>Still, the Song class is useful, we can load our files (or download, save to isolated storage and then load), and play them as background music. The Song class has also properties for artist, rating, playcount etc. They can be played using the MediaPlayer static class. </p> <div id="codeSnippetWrapper" style="border:1px solid silver;padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin-top: 20px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-left: 4px; width: 90.5%; padding-right: 4px; font-family: 'courier new', courier, monospace; direction: ltr; height: 87px; max-height: 200px; font-size: 8pt; overflow-x: auto; overflow-y: auto; cursor: text; padding-top: 4px; text-align: left;"> <div id="codeSnippet" style="padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px; text-align: left;border-style: none;"> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum1" style="color: #606060;"> </span>Song song = Song.FromUri(<span style="color: #006080;">"I Will Not Bow"</span>, <span style="color: #0000ff;">new</span> Uri(<span style="color: #006080;">"02.I Will Not Bow.mp3"</span>, UriKind.Relative));</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum2" style="color: #606060;"> </span><span style="color: #0000ff;">if</span> (MediaPlayer.GameHasControl)</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum3" style="color: #606060;"> </span>{</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum4" style="color: #606060;"> </span> MediaPlayer.Play(song);</pre> <!--CRLF--> <pre style="text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin-top: 0em; margin-right: 0em; margin-bottom: 0em; margin-left: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'courier new', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; padding-top: 0px;border-style: none;"><span id="lnum5" style="color: #606060;"> </span>}</pre> <!--CRLF--></div> </div> <p>In XNA for Windows or Xbox, MediaPlayer can provide visualization data, so you can display audio visualization easily (sound waves, bars etc) via the MediaPlayer.GetVisualizationData method. In Windows Phone 7 it is not available (it won’t throw an exception if we call it, but will only return a bunch of zeros), so you will have to find your own way to give graphical feedback.</p> <h3>MathHelper, Vectors</h3> <p>XNA contains a lot of classes required by game developing including a Vector class with 2,3 or 4 components, Rectangle class with intersect checking and a MathHelper static class. This gives somewhat an out-of-the-box solution for checking collisions, playground etc.</p> <h3>GameServices</h3> <p>The GameServices assembly gives you access to Xbox Live parts, but i’m not sure if it’s usable in a Silverlight based game. Even if you are developing an XNA game, you will need to be a registered Xbox Live developer to gain access to the GameServices.AchievementCollection for example.</p> <h3>Summary</h3> <p>Well, I think I covered the most promising scenarios, but I’m also sure, that a lot of new ideas will come about. On the audio level the integration is good, and apart from a few missing things, it’s definitely usable. The absence of the Game and Graphics assemblies really hurts, with them the Silverlight-XNA integration would reach a whole new level. We don’t have to go too far to imagine an XNA game with Silverlight menus, scoreboard, etc. or a Silverlight application with 3D models loaded with XNA. I hope in the next versions of Windows Phone 7 this level will be reached, and until then we can use the non-graphical components in our Silverlight applications.</p> http://www.silverlightshow.net/items/Exploring-Silverlight-XNA-integration-on-Windows-Phone-7.aspx editorial@silverlightshow.net (Levente Mihály ) http://www.silverlightshow.net/items/Exploring-Silverlight-XNA-integration-on-Windows-Phone-7.aspx#comments http://www.silverlightshow.net/items/Exploring-Silverlight-XNA-integration-on-Windows-Phone-7.aspx Mon, 30 Aug 2010 00:33:00 GMT