Square Away: A Windows Phone 7 Game using nRoute

Posted by Rishi on 19-Jul-10 7:17 AM - Comments (9)

Continuing with my efforts to showcase how to create end-to-end applications using nRoute, this time we have a Windows Phone 7 (WP7) based board game called Square Away. It is actually one game out of a multi-game application I'm creating for WP7, and is based on the "Bubble Breaker" theme. To create the game we'll use the MVVM pattern, creating all the three triads and then use nRoute to bring together everything into one squarely game.

Squares

A word of caution, this is not a super optimized application and is clearly designed to showcase what nRoute has to offer rather than creating an end-user application. 

Quick Start

NewProject

To get started we'll use nRoute's "Windows Phone 7 Application" template, which quickly gets you started with all the bootstrapping and dependencies in place. To get this and more temples (like the ones shown above) you can install nRoute Templates from Visual Studio Gallery - it is really snap quick, and offers a zero-impact installation using VS 2010's new VSIX packaging.

For this sample application, we going to make use of the "application" rather than the "navigation application" template - which actually means that we're making use of nRoute's Toolkit rather than the full framework. The toolkit version is a complete subset of the full version sans the navigation, routing, and site-map features. Though it is lightweight (< 70KB xap'ed), it is quite feature rich and includes things like Modules, Services, ViewServices, ViewModel, ViewModel driven Views, plus it also includes Dependency Injection (DI), Inversion of Control (IoC), Brokered Messaging (Channels), Commanding, Extended Binding (including for Behaviours), Value-Triggers, Weak-Eventing, Relays, Remote Resourcing, and other components. Additionally its got tons of bindable Blend-based triggers, actions, and behaviours that make MVVM style development so much easier. You can get more details from the following posts:

- Introducing nRoute.Toolkit for Silverlight (Part I)
- Introducing nRoute.Toolkit for Silverlight (Part II)
- nRoute: Now, More Wholesome

The Model

To kick things off, we first create the Model representing the game-play, and it is really quite simple: 
Model Our working unit is a "Square", and it specifies four simple properties:

  • Column and Row properties representing a Square's position,
  • a Colour property which obviously indicates the colour from a selection of five choices, as specified in the "SquareColor" enumeration,
  • and a State property representing the current state of a Square per the "SquareState" enumeration

Next we have a Engine type that "hosts" a game; a game is represents by a configurable number of Columns and Rows, which together determines the provisioned Squares, and lastly the Engine also keeps track of the Score of any ongoing game. Further, with the Engine you can do four specific things:

  • Start a New Game, by specifying the number of columns and rows you want, along with a callback which is used to signal when a game is completed,
  • You can Highlight or UnHighlight a group of interconnected Squares,
  • and you can Hide a group of interconnected Square

Apart from the inputs the Engine takes care of everything - that is, it manages its logical state, and the visualization of the game is bound to this logical state. And quiet importantly, the visual and logical states are completely independent in line with the spirit of MVVM. So, basically the four functions above are like the inputs using which you alter the logical state - and in our case these inputs will be funnelled via the ViewModel which will also host the Engine. Now, to expose the engine for DI consumption, we use nRoute's MapResource attribute which essentially "exports" the engine if you understand MEF talk with a lifetime option set to create a new instance per request.

   1: [MapResource(InstanceLifetime.PerInstance)]
   2: public class Engine
   3: {
   4:     // ...
   5: }

Keen eyed domain-modellers might suggest that in the model above one could carve out a "Game" entity by itself - that is true, however here it only exists in a "logical state" as form of optimization. Creating and tearing a Game object's visuals is a very costly exercise on a limited resource device (a multi-seconds operation in our case), and by only having it as a "logical" concept we can cache the visual representing objects (i.e. the Squares) so that we only need to create them once in the lifetime of the application.

The ViewModel

Because this sample is relatively simple we can fit the entire game into just one pair of View and ViewModel - we'll first start with out ViewModel. Our MainPageViewModel, broadly speaking, provides for two sets of functionality, one it exposes our Model's properties and functions, and two, it layers in a "play state" model that enables one to pause, resume and/or start a game.

ViewModel   
As I mentioned earlier, the first set of grouping above is just a facade to the functionality provided by the Engine, which is perfectly alright as we doing so in a decidedly View-friendly manner and plus we're layering above it our "play state" functionality. The "play state" model is simply defined by PlayState enumeration, and we allow the View to change it via the "TogglePlayState" command. The toggle command works by pausing the game if it was already started, resuming it if it was paused, or if the game was earlier completed then it starts a new game. Lastly, we use the Message property to beam out user-friendly information such as what was your game score etc - this could have been done in the View, but given our limited output we're just formatting the messages from the ViewModel itself.

In terms of exposing and resourcing our ViewModel (as you can see below) we decorate our ViewModel class with a MapViewModel attribute specifying that this class is the ViewModel for View of type MainPage. Secondly, we also decorate our constructor with a ResolveConstructor attribute which tells nRoute to resolve the two dependencies asked for in the constructor. As a general theme, you'll notice nRoute tries to keep the "infrastructure code" out of your "custom code", which is purposefully so as it ensures minimal impact whilst giving you a sort of operational flexibility for future or extended use.

   1: [MapViewModel(typeof(MainPage))]
   2: public class MainPageViewModel : ViewModelBase
   3: {
   4:     [ResolveConstructor]
   5:     public MainPageViewModel(Engine engine, IHighScoreService scoreService) { ... }
   6: }

Lastly, I'll just point out that the ViewModelBase class is nothing but a helper class that only implements the INotifyPropertyChanged interface - or put otherwise, its use is optional.

BridgeViewModelThe View

Firstly, in the View you will see that we have a "BridgeViewModel" behaviour attached to the Page - this actually corresponds to our mapping of ViewModel above, and it basically resolves and injects the paired ViewModel at runtime.

Next we create a VSM based state-group that will PlayStateGroupmirror our the "play states" from our ViewModel, however owing to how we intent to visualize, we will have only two corresponding states - a "Non-Playing State" (the default/start state) and a "Playing State". Further, since the "play states" are entirely directed/managed from our ViewModel, we have to pair them up with VSM based states - we do so using nRoute's ValueSwitchTrigger behaviour:

   1: <i:Interaction.Triggers>
   2:     <nTriggers:ValueSwitchTrigger SourceBinding="{Binding PlayState}">
   3:         <ic:GoToStateAction nTriggers:ValueSwitchTrigger.CaseValue="Started" 
   4:             StateName="PlayingState"/>
   5:         <ic:GoToStateAction nTriggers:ValueSwitchTrigger.CaseValue="Paused" 
   6:             StateName="NonPlayingState"/>
   7:         <ic:GoToStateAction nTriggers:ValueSwitchTrigger.CaseValue="Completed" 
   8:             StateName="NonPlayingState"/>
   9:     </nTriggers:ValueSwitchTrigger>
  10: </i:Interaction.Triggers>

The value-switch trigger here acts quite like its namesake in the programming world, it allows selective execution of one or more possibilities given a match. Here, these possibilities take the shape of "GoToState" trigger actions, wherein each action is earmarked using with a "CaseValue" attached property. If the Case Value matches the Source Value, the appropriate action or actions are executed - and this happens every time the Source value changes, which in this case means whenever the PlayState changes. Note the use of the "Binding" postfixed Source property, this is required as Silverlight 3 doesn't allow direct inline binding to dependency properties, however with nRoute's extended binding infrastructure this limitation can be bypassed.

Corresponding to our two view-states we have two "Playing" and "NonPlaying" panels - and as you could have guessed one panel shows the game, and the other is a menu overlay. Now, the UI implementation is fairly simplistic, some of the notable things include our use of FluidMove behaviour to handle all our square's items animations. The PanelTemplate for squares looks like:

   1: <!-- SQUARE's CANVAS PANEL TEMPLATE -->
   2: <ItemsPanelTemplate x:Key="SquaresPanelTemplate">
   3:     <Canvas CacheMode="BitmapCache">
   4:         <i:Interaction.Behaviors>
   5:             <il:FluidMoveBehavior AppliesTo="Children" Duration="0:0:0.65">
   6:                 <il:FluidMoveBehavior.EaseY>
   7:                     <PowerEase EasingMode="EaseOut" Power="7"/>
   8:                 </il:FluidMoveBehavior.EaseY>
   9:                 <il:FluidMoveBehavior.EaseX>
  10:                     <PowerEase EasingMode="EaseIn" Power="9"/>
  11:                 </il:FluidMoveBehavior.EaseX>
  12:             </il:FluidMoveBehavior>
  13:         </i:Interaction.Behaviors>
  14:     </Canvas>
  15: </ItemsPanelTemplate>

With the above you can easily change the type of easing and timing associated with the moving of the squares - here we've set it to ensure that it moves first along the Y axis then on the X. Another related notable is that we've used a custom bindable behaviour, that helps a square position itself per the specified Column and Row position. Actually more correctly we position the container not the template control, and the Xaml for the same is:

   1: <DataTemplate x:Key="SquareTemplate">
   2:     <Canvas .. >
   3:         <i:Interaction.Behaviors>
   4:             <local:SetContainerPositionBehavior RowBinding="{Binding Row, Mode=OneWay}"
   5:                         ColumnBinding="{Binding Column, Mode=OneWay}" />
   6:         </i:Interaction.Behaviors>
   7:     </Canvas>
   8: </DataTemplate>

Further, because ViewModel based commands are not directly usable in DataTemplates (static v/s instance differences), we use something called CommandRelays. Command relays in nRoute basically allow you to source (i.e. relay) your command's implementation from elsewhere, and this fact allows us to declared them as static resource yet have their implementation sourced from our ViewModel. So for example, below we've declared three command relays just as any normal static resource with a key:

   1: <phone:PhoneApplicationPage.Resources>    
   2:     <!-- STATIC COMMAND RELAYS - FOR USE IN DATA TEMPLATES -->
   3:     <nComponents:CommandRelay x:Key="HighlightCommandRelay" />
   4:     <nComponents:CommandRelay x:Key="UnHighlightCommandRelay" />
   5:     <nComponents:CommandRelay x:Key="HideCommandRelay" />
   6: ..
   7: </phone:PhoneApplicationPage.Resources>

Next, we "bridge" them to the actual implementation provided in our ViewModel, using a "BridgeCommand" behaviour:

   1: <!-- BRIDGE STATIC COMMAND RELAYS TO OUR VIEWMODEL -->
   2: <nBehaviors:BridgeCommandBehavior CommandRelay="{StaticResource HighlightCommandRelay}"
   3:     CommandSourceBinding="{Binding HighlightCommand}"/>
   4: ...

Now, within our DataTemplate each square can execute the ViewModel sourced command by binding to statically declared command relays:

   1: <DataTemplate>
   2:     ...
   3:     <i:EventTrigger EventName="MouseEnter">
   4:         <nBehaviors:ExecuteCommandAction Command="{StaticResource HighlightCommandRelay}" 
   5:             ParameterBinding="{Binding}"/>
   6:     </i:EventTrigger>
   7:     ...
   8: </DataTemplate>

Above we're using a "MouseEnter" event-trigger to execute a command fronted by the "HighlightCommandRelay". If you see the HighlightCommand declaration in our ViewModel, you'll see it takes in a "Square" type parameter, which is exactly what we're passing by binding to the command's parameter.

Another notable thing with the Square templates is that rather than using VSM based storyboards for managing the visual state I've opted to use nRoute based Value-triggers as they are quite lightweight and easier to use with Blend's drag-and-drop experience. Consider, the following is the xaml for when a Square is highlighted:

   1: <nTriggers:ValueMatchTrigger SourceBinding="{Binding State}" Value="Highlighted">                 
   2:     <nBehaviors:SetPropertyAction PropertyName="Opacity" Value="0.8" />
   3: </nTriggers:ValueMatchTrigger>

Above in line 1 we're basically saying that when the source (which we're bound to a Square's State property) matches the value "Highlighted" execute the SetProperty action. Elsewhere but likewise, we use nRoute's Value-triggers to play the sound when hiding squares, consider:

   1: <!-- SOUND MEDIA ELEMENT -->
   2: <MediaElement x:Name="HideSoundMedia" Source="Resources/sound.wma" .. />
   3:  
   4: <!-- PLAY SOUND -->
   5: <nTriggers:ValueChangedTrigger SourceBinding="{Binding Score}">
   6:     <local:PlayMediaElementAction TargetName="HideSoundMedia"/>
   7: </nTriggers:ValueChangedTrigger>

Above in line 2 we've declare our MediaElement that points to a sound bit, then in line 5 we've declared a ValueChanged trigger that says whenever the "Score" changes play the "HideSoundMedia" element. Lastly, we also use behaviours to handle the back-press key, consider:

   1: <i:EventTrigger EventName="BackKeyPress">
   2:     <nBehaviors:ExecuteCommandAction CommandBinding="{Binding TogglePlayStateCommand}"
   3:          IsEnabledBinding="{Binding PlayState, Converter={StaticResource StartedStateConverterRelay}}"/>
   4:     <local:PlayStateCancelHandledAction PlayStateBinding="{Binding PlayState}"/>                
   5: </i:EventTrigger>

So above when then "BackKeyPress" event occurs we toggle our play state, which is, via the IsEnabled property, only allowed when the a game is ongoing else the CommandAction is disabled. However, per the WP7 rules if we don't specifically cancel the "BackKeyPress" event we'll exit the application, so using our custom "PlayStateCancelHandled" action we only cancel event when pausing a game else it allows returning to the OS. This is in keeping with somewhat strange but expected use of the BackKey in WP7.

GamePaused

A Service & A Channel

If  you look at the MainPageViewModel other than the Engine type, it also asks for an IHighScoreService - which is really a simple service that persists the highest ever score recorded. Normally you'll have a more elaborate score-keeping implementation, but for this demo the interface looks like:

HighScoreService
Further, as shown below, using nRoute's MapService attribute we register the decorated class as a singleton implementation of IHighScoreService service, and this implementation inturn gets internally served to the ViewModel. Like the interface defination, the implementation is also very simple, it makes use of the ApplicationSettings facility in Silverlight's IsolatedStorage to keep our highest score figure on disk.

   1: [MapService(typeof(IHighScoreService), Lifetime=InstanceLifetime.Singleton)]
   2: public class HighScoreService : IHighScoreService
   3: {
   4:    [ResolveConstructor]
   5:    public HighScoreService(IChannel<ApplicationStateInfo> stateInfoChannel)
   6:    {
   7:         ...
   8:    }
   9: }

Now, if you look at the line 5, you'll see that this class has a dependency on an IChannel<ApplicationStateInfo> type. IChannels or Channels in nRoute are observable messaging brokers against which you can both publish and subscribe messages, kind of the equivalent of Event-Aggregators in Prism but with much simpler API coupled with significantly more capabilities. So above the HighScoreService class is asking for a Channel of type ApplicationStateInfo, which is one of the in-built channels to nRoute that publishes the running application's lifetime state information - such as when it is starting or existing. And so here we use it to save changes to our ApplicationSettings just before the application exits - the saving of ApplicationSettings is mandatory else the settings are not persisted, and this way we only need to do it once per the application's lifetime.

   1: private IDisposable _subscriptionToken;
   2:  
   3: // usage
   4: _subscriptionToken = stateInfoChannel.Subscribe((s) =>
   5: {
   6:     if (s.CurrentState == ApplicationState.Exiting)
   7:     {
   8:         IsolatedStorageSettings.ApplicationSettings.Save();
   9:         _subscriptionToken.Dispose();
  10:     }
  11: });
 
Squaring It Up

So there you have it, an end-to-end WP7 application - I've tried to show as many features as I could in terms of using nRoute. However, there is a ton more you can do with nRoute in WP7, look up some of the earlier examples of nRoute's use on the desktop/browser, which fortunately are very much usable in approach if not just directly. 

Get the code from Codeplex

Comments

pingback
topsy.com
on 19-Jul-10 10:16 AM
Pingback from topsy.com

Twitter Trackbacks for
        
        Square away - A Windows Phone 7 Game using nRoute   #wp7dev
        [orktane.com]
        on Topsy.com

pingback
phone7.wordpress.com
on 19-Jul-10 2:58 PM
Pingback from phone7.wordpress.com

Square Away Game for WP7 (with code) « Phone7

pingback
1800pocketpc.com
on 19-Jul-10 5:24 PM
Pingback from 1800pocketpc.com

wp7 games : Square Away puzzle Game â?" windows phone 7 apps / WP7 games / Windows Mobile Software

pingback
w7phone.ru
on 19-Jul-10 6:33 PM
Pingback from w7phone.ru

Ð~гÑ?а Square Away с исÑ.одниками - Windows Phone 7

trackback
DotNetKicks.com
on 19-Jul-10 10:58 PM
Square Away: A Windows Phone 7 Game using nRoute

You've been kicked (a good thing) - Trackback from DotNetKicks.com

trackback
DotNetShoutout
on 20-Jul-10 12:10 AM
Square Away: A Windows Phone 7 Game using nRoute

Thank you for submitting this cool story - Trackback from DotNetShoutout

pingback
freepocketpcsolutions.info
on 20-Jul-10 8:26 AM
Pingback from freepocketpcsolutions.info

wp7 games : Square Away puzzle Game | Free Pocket PC Software

pingback
windows-smartphones.de
on 22-Jul-10 5:04 AM
Pingback from windows-smartphones.de

Tutorial Square Away, eine Windows Phone 7 App programmieren | Smartphones mit Windows Phone 7

pingback
techblog.ginktage.com
on 24-Jul-10 7:25 PM
Pingback from techblog.ginktage.com

Interesting .NET Links - July 25 , 2010 | Tech Blog

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading