Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

Creating a Pinball Game in Silverlight: Using the Physics Helper Library + Farseer Physics

In this tutorial, we'll create a Silverlight pinball game using Behaviors, a new addition to Expression Blend 3 & Silverlight that allows you to create interactivity with little or no coding.

Below you'll find a video and step-by-step walkthrough.

Videos:

Setup and Prerequisites

First you have to setup Physics Helper controls into Expression Blend. Follow these steps:

  1. Download the Physics Helper ZIP file from http://physicshelper.codeplex.com/Release/ProjectReleases.aspx
  2. Extract the ZIP file contents and dump them into a folder.
  3. Run install.bat. (Note: this copies a few required assemblies to a folder in Blend 3.)

Getting Started & Making the Ball

Next, you create the controller and the pinball. Here's how.

  1. Open Expression Blend 3 and create a new “Silverlight 3 Application + Website” named PinballGame.
  2. Change your default layout container to a Canvas. You can do this in the Objects and Timeline panel by right-clicking LayoutRoot and selecting Change Layout Type/Canvas. Canvas layout containers are better for games because they allow for positioning of elements at absolute (x,y) coordinates.
  3. Give the application a bigger default size. Select the UserControl element in the Objects and Timeline panel. Then, in the Properties panel, change the Width to 800 and the Height to 600.
  4. Add a Physics Controller Behavior to our main game Canvas. From the Asset Panel, find the PhysicsController Behavior and drag it to the Objects and Timeline Panel. Then, drop it on the LayoutRoot Canvas.

  5. Change a few of the PhysicsController's properties, to make it easier to use. In the Objects and Timeline Panel, select the PhysicsControllerBehavior assigned to LayoutRoot. Then, set the highlighted properties as follows in the Properties Window:
    1.)  Decrease the vertical gravity to 250
    2.)  Increase the iterations to 200. This will help with collision detection in a fast-action game.
    3.)  Set MousePickEnabled to true. This will allow us to manipulate objects with the mouse during development and testing.
  6. Create a "playing field" for the pinball. To do this, draw out a Rectangle onto the artboard, just off the bottom of the user control bounds. Set the properties of the Rectangle as follows:

    Name: rectPlatform
    Fill: select a color of your choosing (light blue is selected below).
  7. To make “rectPlatform” a physics element, we need to add a PhysicsObjectBehavior to it. Find the PhysicsObjectBehavior in the Assets panel and drag/drop it onto rectPlatform on the Artboard.
  8. In the Objects and Timeline Panel, select the PhysicsObjectBehavior belonging to rectPlatform. Then in the Properties Panel, set the IsStatic property to True so that the platform will stay in place.
  9. Next we'll create the ball. Draw out an Ellipse near the very top of the UserControl and set the following properties:

    Name:
    ellBall
    Height
    : 50
    Width: 50
    Fill: A color of your choosing.
  10. To make the ball a Physics Object, we need to add a PhysicsObjectBehavior to it. From the Asset Library, drag and drop a PhysicsObjectBehavior onto ellBall.
  11. Run the project by clicking F5. The ball should fall and hit the platform. Use the mouse to manipulate the ball.

Making the Flippers

In a pinball game, the Flippers are controlled by the player and are used to move the ball up the playfield. Standard pinball games have one right and one left flipper, but many games have three or more flippers located in various locations on the playfield. We'll create two user controls to represent the flippers – a right flipper and a left flipper – that way we can easily add as many flippers as we want to our main playfield.

  1. In the Projects Panel, right-click the PinballGame (Silverlight) project and select Add New Item. Select UserControl and name the control LeftFlipper.xaml.
  2. In the Objects and Timeline Panel, right-click LayoutRoot and select Change Layout Type/Canvas. This is required for the Physics Helper to work with nested User Controls.
  3. In the Objects and Timeline Panel, select the UserControl element and set its Width and Height to 200 x 200 pixels in the Properties Panel.
  4. Draw out a Rectangle that is about 200 x 50 pixels big, centered in the user control. Round the corners of the Rectangle.
  5. From the main Blend menu, select Object/Path/Convert to Path. This will create a new Path element from the Rectangle primitive. Then select the Direct Selection tool from the Toolbox, which will allow you to manipulate the individual points on the Path.
  6. Select the top, left two points on the Path and move them down so that you end up with a more “flipper like” shape:
  7. In the Toolbox, select the Selection arrow and rotate the flipper 45 degrees so that it looks similar to this (be sure the flipper is still within the bounds of the user control):
  8. Right-click the flipper Path and select Group Into/Canvas. Name the Canvas cnvLeftFlipper and resize it so that it surrounds the entire flipper Path. Note that you could “embellish” the cnvLeftFlipper with additional elements if you wish.
  9. Next we'll create a Joint so that the Flipper can rotate on an axis. We'll need something to anchor the joint to, so first let's add a small Static object to hold the Joint in place. Draw out a Rectangle at the upper left of the flipper and name the Rectangle rectHolder.
  10. To add a Joint, we will need a UI element to visually represent where we want the joint created. You can use any element you want for this, but an Ellipse works pretty well. Draw out an Ellipse on top of the rectHolder.
  11. Now we can add all of the required Physics Behaviors to the User Control.
    1. From the Asset Library, drag a PhysicsObjectBehavior to the rectHolder Rectangle. Set its IsStatic property to True. Since we don't really want to see the rectHolder in our UI, just send it to the back of the elements (Right-click, then select Order/Send to Back).
    2. Drag a PhysicsObjectBehavior to the cnvLeftFlipper Canvas. Set the RestitutionCoefficent property to “0.1”. (This makes the flipper a bit more “bouncy” on collisions)
    3. Drag a PhysicsJointBehavior to the Ellipse. Set the Body1 property to “cnvLeftFlipper” and the Body2 property to “rectHolder”. Set the CollisionGroup to “1” and the AngleLimitLower and AngleLimitUpper to 0 and 50 respectively. Also enable the AngleSpring:
    4. Drag a PhysicsApplyTorqueBehavior to cnvLeftFlipper. We want to apply Torque when the user presses the Left arrow key, so we'll create a Trigger for this. Select the Behavior and in the Properties Panel, click the New Trigger button.
       

      Select the PhysicsKeyTrigger type from the popup.

      Set the Key property for the Trigger to “Left” and the TorqueValue to -100000
  12. Build the Project so that the LeftFlipper user control is available.
  13. Open MainPage.xaml and drag an instance of LeftFlipper from the Asset Library onto the Artboard. Position it near the bottom of the user control and slightly to the Left.
  14. Run the project and try the flipper with the Left arrow key.
  15. In this step, we'll create a copy of “LeftFlipper” to make  a “RightFlipper”. In the Projects Panel, right-click LeftFlipper.xaml and select Copy. Then right-click the PinBallGame Project and select Paste.
    You now have a new User Control named “Copy of LeftFlipper.xaml”. Rename this to RightFlipper.xaml. Then in the code-behind file, make sure the Class and Constructor are also renamed to “RightFlipper”.
    Change to XAML view for RightFlipper.xaml and change the x:Class attribute so that it inherits from RightFlipper: 
    <UserControl 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    xmlns:i=&amp;quotclr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    xmlns:pb="
    clr-namespace:Spritehand.PhysicsBehaviors;assembly=Spritehand.PhysicsBehaviors"
    x:Class="
    PinballGame.RightFlipper"
    d:DesignWidth="
    640" d:DesignHeight="480" Height="200" Width="200">

    Open RightFlipper.xaml and find the pathLeftFlipper element. Rename this to pathRightFlipper. Then, rename “cnvLeftFlipper” to “cnvRightFlipper”. Rename “rectHolder” to “rectRightHolder” and position the rectHolder and joint ellipse so that they are in the correct location for the flipper:

    Modify the PhysicsJointBehavior for the joint in the Objects and Timeline Panel. Change BodyOne to “cnvRightFlipper”, BodyTwo to “rectRightHolder” and change the Angle limits so they are appropriate for the opposite flipper:

    Select the PhysicsApplyTorqueBehavior for cnvRightFlipper in the Objects and Timeline Panel. Change the Key property for the trigger to “Right” so that it will initiate on the Right Arrow key down. Also change the TorqueValue so that it will apply torque clockwise:

  16. Add an instance of RightFlipper onto the MainPage.xaml and position it to the right of LeftFlipper.
  17. Test the project by clicking F5. You can now click left and right arrows keys to manipulate the paddles.

Creating the Playfield + Scrolling

Now let's create more of a playfield for our Pinball game and add a Camera control so that the game will scroll and always follow the pinball. Feel free to tweak the design as you wish.

  1. Open MainPage.xaml in Expression Blend and zoom out by using Ctrl plus minus (Ctrl -).
  2. Draw out a Path using the Pen tool for the right border. Note that you can extend above the upper bounds of the User Control. Group this into a Canvas named “cnvLeftBorder” (right click the Path and select Group Into/Canvas).
  3. Drag a PhysicsObjectBehavior from the Asset Library onto cnvLeftBorder and set the IsStatic property to True. Also set the CollisionGroup property to “1” so that collisions will not occur between the border and the Flippers.
  4. Repeat Steps 2 and 3 above for the right border, naming that cnvRightBorder. You should have something similar to the following:
  5. Continue adding border controls and applying physics behaviors until you are happy with your playfield. Make the playfield completely closed in so that the ball cannot escape (except by the bottom trap of course!)
  6. You can also add more LeftFlipper and RightFlipper controls so that the player can more easily move the ball up the playfield. Here is one example playfield design:
  7. To enable scrolling, we can add a Camera Controller to the ball, causing the camera to follow the ball around the screen. Drag a PhysicsCameraBehavior from the Asset Library onto the ellBall element.

Creating a Kicking Target

Pinball games have lots of different types of targets – some are simple sensors which give points on contact, others are little toys that gobble up the ball for a few seconds. In this step, we'll create a Kicking Target, which gives points when hit by the ball but also kicks the ball back in the opposite direction.

  1. Create a new User Control named KickingTarget.
  2. Change the LayoutRoot container to a Canvas by right-clicking and selecting Change Layout Type/Canvas.
  3. Select the [UserControl] in the Objects and Timeline Panel and set its Width and Height to 100 x 100.
  4. Draw out an Ellipse that is 90x90 pixels, and positioned at Left, Top 5,5 (so that it is centered in the control).
  5. Draw out a second Ellipse that is 70x70 pixels and positioned and Left, Top 15, 15.
  6. Group the two Ellipses into a Canvas by selecting them both in the Objects and Timeline Panel and selecting Group Into/Canvas. Name the Canvas “cnvKicker”.
  7. Drag a PhysicsObjectBehavior from the Asset Library onto cnvKicker. Set the IsStatic and RestitutionCoefficient properties as shown below. Setting the RestitutionCoefficient above a value of 1 will make the object “kick” things back.
  8. Build the project so that the new user control is available. Then open MainPage.xaml and add a few instance of “KickingTarget” to the page.
  9. Run the project and try out the Kicking Targets.

Optimizing Performance

Our pinball game is looking up, but the performance could be a lot better. The startup time for the game is taking quite awhile because the Physics Helper Library is determining the outline of all of the shapes. Also, the frame rate is too low.

By default, Silverlight has a target frame rate of 60 frames per second. This is great for a lot of casual games, but Pinball requires a bit more speed. Additionally, Silverlight 3 introduces GPU Acceleration which can greatly increase the performance of our game by offloading graphics operations to the Video Card.

  1. Open the Default.html page in the website project. This page hosts the Silverlight control and exposes the plugin parameters. Add the following parameters to the plug in to increase the default frame rate and enable GPU Acceleration for our game.
    <object data="data:application/x-silverlight," type="application/x-silverlight-2" width="800" height="600"> 
    <param name="source" value="ClientBin/PinballGame.xap"/>
    <param name="onerror" value="onSilverlightError" />
    <param name="background" value="#010141" />
    <param name="minRuntimeVersion" value="3.0.40624.0" />
    <param name="MaxFrameRate" value="160" />
    <param name="EnableGPUAcceleration" value="true" />
    <param name="EnableCacheVisualization" value="false" />
    <param name="autoUpgrade" value="true" />
    <a href="http://go.microsoft.com/fwlink/?LinkID=149156&ampv=3.0.40624.0" style="text-decoration: none;">
    <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none"/>
    </a>
    </object>

  2. Apply the CacheMode attribute to all elements in the game that are static. Since our pinball game's elements are not animated, we can apply the CacheMode to each of them. (If the objects contained animations within them, then the Cache would be invalidated during animation).
    <Rectangle x:Name="rectPlatform" CacheMode="BitmapCache" Fill="#FF8AD0C9" Height="70" Width="800" Canvas.Left="-14" Canvas.Top="2172" Opacity="0"> 
    <i:Interaction.Behaviors>
    <pb:PhysicsObjectBehavior IsStatic="True"/>
    </i:Interaction.Behaviors>
    </Rectangle>

  3. Now we'll speed up the startup time by adding in a pre-calculated Point Cache. The Physics Helper Library echoes out the calculated points at runtime. Open the project in Visual Studio, and run with debugging on. In the Output Window, Find the ReadBoundaryCache method that was echoed out and Copy the entire method.
  4. Note that the copied code can contain multiple rows of the same points. You can trim out any rows that are duplicated, for example cnvRightFlipper_1 is a copy of cnvRightFlipper, so you can remove that from the list of points.
  5. In order to load the list of boundary outlines into the Physics Controller, we need to get a reference to the controller through code. Open up MainPage.xaml.cs and add the highlighted code:
    public partial class MainPage : UserControl 
    {
    PhysicsControllerMain _physicsController;
    public MainPage()
    {
    // Required to initialize variables
    InitializeComponent();
    this.Loaded += new RoutedEventHandler(MainPage_Loaded);
    }
    void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
    _physicsController = LayoutRoot.GetValue( PhysicsControllerMain.PhysicsControllerProperty) as PhysicsControllerMain;
    BoundaryCache.ReadBoundaryCache(_physicsController);
    }
    }

  6. Run the project and test the performance. Note that items that have a tint applied to them (Red, Blue, etc.) are NOT being cached and GPU accelerated.

Scoring

In this section, we'll embellish our game with a Score.

  1. We'll create a Canvas that overlays our main Canvas to contain a High Score TextBlock. This is so that the Score TextBlock will not scroll with the rest of the Playfield. Open MainPage.xaml and the select LayoutRoot in the Objects and Timeline Panel. Right-click LayoutRoot and select Group Into/Canvas.
  2. Add a new StackPanel into this outside Canvas that contains two TextBlock aligned Horizontally. The first TextBlock should contain the Text “Score:” and the second TextBlock should contain the Text “0”. Set the Font Size to a larger value, around 12 pt. Position the TextBlock at the Top, Left portion of the Canvas.
  3. Next we'll handle the Collision event for the Physics Controller so we can add to the score. Open MainPage.xaml.cs and add in a Property wrapper for the current  score:
    public int Score 
    {
    get
    {
    return Convert.ToInt32(txtScore.Text);
    }
    set
    {
    txtScore.Text = value.ToString();
    }
    }

  4. Handle the Collision event for the Physics Controller and increment the Score.
    void MainPage_Loaded(object sender, RoutedEventArgs e) 
    {
    _physicsController = LayoutRoot.GetValue(PhysicsControllerMain.PhysicsControllerProperty) as PhysicsControllerMain;
    BoundaryCache.ReadBoundaryCache(_physicsController);
    _physicsController.Collision += new PhysicsControllerMain.CollisionHandler(_physicsController_Collision);
    }

    void _physicsController_Collision(string sprite1, string sprite2)
    {
    if (sprite1 == "ellBall" && sprite2.StartsWith("ellKicker"))
    Score += 10;
    }

Tracking Lives

When the ball collides with the “rectPlatform” obstacle at the bottom, the ball has been lost and we should launch a new ball.

  1. Add a new ChildWindow to the project named “LostTheBall.xaml”.
  2. Set the “Cancel” button's Visibility to Collapsed.
  3. Add a TextBlock and set its Text to a message for the user stating the ball was lost.
  4. Add code in MainPage.xaml.cs to display the dialog and reset the ball.
    LostTheBall _lostTheBall;
    void _physicsController_Collision(string sprite1, string sprite2) 
    {
    if (sprite1 == "ellBall" && sprite2.StartsWith("cnvKicker"))
    {
    Score += 10;
    }

    if (_lostTheBall == null &&ampsprite1 == "ellBall" && sprite2 == "rectPlatform")
    {
    _lostTheBall = new LostTheBall();
    _lostTheBall.Closed += new EventHandler(dialog_Closed);
    _lostTheBall.Show();
    }
    }

    void dialog_Closed(object sender, EventArgs e)
    {
    _lostTheBall = null;
    PhysicsSprite ball = _physicsController.PhysicsObjects["ellBall"];
    ball.BodyObject.Position = new Vector2(460, 430);
    }

Adding Sound Effects

We can easily add buffered sound effects using the PhysicsSoundBehavior.

  1. In Visual Studio, Import two sound files into the project, score.wma and click.wma. Set their Build Action to Content.
  2. Open MainPage.xaml in Expression Blend. Drag a PhysicsSoundBehavior to the LayoutRoot Canvas. Set the TriggerType to PhysicsCollisionTrigger and set the following properties:
  3. Drag another PhysicsSoundBehavior to the LayoutRoot Canvas. Set the TriggerType to PhysicsCollisionTrigger and set the following properties:
  4. Drag another PhysicsSoundBehavior to the LayoutRoot Canvas. Set the TriggerType to PhysicsCollisionTrigger and set the following properties:
  5. Run the project.

About the Author

Andy Beaulieu is a software developer and trainer who is well versed in many Microsoft technologies including Silverlight, ASP.NET, ADO.NET and WindowsForms. Visit Andy's Blog for more fun and games with Silverlight.

Tags:

Follow the Discussion

  • Clint RutkasClint I'm a "developer"

    @Michael Washington, Thanks man, we try our best.

  • Michael WashingtonMichael Washington

    This is a really great tutorial. This covers a lot of advanced stuff but it is clear and easy to follow. Top notch work.

  • Clint RutkasClint I'm a "developer"

    @Rich Alger, you need to do the "Setup and Prerequisites" step Smiley

  • Rich AlgerRich Alger

    I don't see the "Physics Controller Behavior" in my list of behaviors.   I am using  Microsoft Expression Blend 3 (Free Trial)

  • Michael WashingtonMichael Washington

    @Rich Alger - I was on one computer and I could not get the Behaviors to show up. However, I was unable to get any behaviors to show up. I had to uniinstall my Expression Blend and Expression SDK ect. and reinstall everything to get them to show up properly.

    This tutorial does work, but your Expression Blend set-up may not work correctly and re-installing should help.

  • Clint RutkasClint I'm a "developer"

    @Keith Simmons thanks for the heads up, yeah, I'm going to have to fix the video links.

  • Keith SimmonsKeith Simmons

    The video on this site doesn't work. It times out when you try to click on it.

    Big fan I wrote a small ragdoll program using these steps at my site (My name is a link to it)and it all worked smoothly except that when I tried to use any of the water or magnet behaviors it decides that non of the physics should work and freezes up. But as long as I use the basic stuff it works fine.

    Keep up the cool posts!

  • Clint RutkasClint I'm a "developer"

    @Keith Simmons, took a bit but got the videos on Channel 9

  • Clint RutkasClint I'm a "developer"

    @ge-force watch video 3 for how to do that.  http://channel9.msdn.com/posts/Clint/Silverlight-Pinball-with-Farseer-Physics-Part-3-of-10/Default.aspx

  • ge-forcege-force

    I don't know how to round the corners of the rectangle for the left flipper. And when you said "Expression Blend's main menu" I got a little lost.

  • Ima TrollIma Troll

    That looks like a library I need to spend some time with.  Nice video series too.  

Remove this comment

Remove this thread

close

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.