Day #22: Using The Farseer Physics Engine in Silverlight

Today we’re going to venture into something you may never have played with before: a physics engine. This is article #22 in my series 31 Days of Silverlight. We are going to take a look at the Farseer Physics Engine for Silverlight.

What IS a physics engine?

Specifically, this is a 2-dimensional physics engine. What that means is that we can create elements that are smart enough to bounce off of each other, react to “gravity”, and other things that we might expect to see in the real world. There’s even properties like friction that we can apply to our XAML elements.

How do we get started?

The first thing you’ll want to do is download the actual Farseer engine from CodePlex. I provided a link to the project home page in the opening paragraph, but if you want a link to the specific version we’ll be using for this demo, you can also download the Farseer Engine 2.1.1 Silverlight here.

We’re going to grab another open source project, a Physics Helper. While the Farseer engine is incredibly useful, it could really use an additional layer of abstraction, and this Physics Helper does exactly that.

In order to make this incredibly simple, however, I’m providing a link to download the 3 DLLs we will need to add to our project. Download the Farseer and Physics Helper DLLs here.

1. Start a new Silverlight project.

Create a new Silverlight project. I named mine SilverlightPhysicsPyramid. You can name yours whatever you like.

2. Add some references to the DLLs.

Since you’ve already downloaded the three DLLs we will need, store them in a safe place, and add references to them in your project. Go ahead. I’ll wait. Once you’ve got that done, you’ll also want to add a reference to the System.Windows.Interactivity assembly as well. This is found in the “.NET” tab of the Add Reference dialog box.

3. Open your MainPage.xaml file.

For an application using these physics DLLs, we will always want to use a <Canvas> tag. So change the <Grid> tag to a <Canvas>. Don’t forget to change the closing tag as well. Next, I’ve created some XAML that we will use, so copy and paste this XAML into your <Canvas> tag. Here’s what the contents of your XAML file should look like:

<UserControl x:Class="SilverlightPhysicsPyramid.MainPage"
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" d:DesignWidth="640" d:DesignHeight="480">
<
Canvas x:Name="LayoutRoot" Background="Black">
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="215" Canvas.Top="433"/>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="245" Canvas.Top="433"/>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="275" Canvas.Top="433"/>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="305" Canvas.Top="433"/>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="335" Canvas.Top="433"/>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="365" Canvas.Top="433"/>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="230" Canvas.Top="403"/>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="290" Canvas.Top="283"/>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="305" Canvas.Top="313"/>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="275" Canvas.Top="313"/>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="320" Canvas.Top="343"/>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="290" Canvas.Top="343"/>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="260" Canvas.Top="343"/>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="335" Canvas.Top="373"/>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="305" Canvas.Top="373"/>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="275" Canvas.Top="373"/>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="245" Canvas.Top="373"/>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="350" Canvas.Top="403"/>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="320" Canvas.Top="403"/>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="290" Canvas.Top="403"/>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="260" Canvas.Top="403"/>
<
Rectangle Fill="White" StrokeThickness="2" Height="29" Width="640" Canvas.Left="0" Canvas.Top="475"/>
<
Ellipse Fill="Red" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Top="173" Canvas.Left="255"/>
</
Canvas>
</
UserControl>

If you run your project at this point, you should see a black background, and a pyramid of squares over a white platform with a red ball above them. Here’s a screenshot:

4. Adding those XML namespace entries

So, we added our DLLs as references, but because we’re going to do this work in our XAML file, we need to make sure that we have referenced the ones we’re using directly at the top of our file. We need to add the Spritehand.PhysicsBehaviors assembly, as well as System.Windows.Interactivity. If you’ve never used these statements before, just think of them like “using” statements in C#. Your <UserControl> tag should now look like this:

<UserControl x:Class="SilverlightPhysicsPyramid.MainPage"
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"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:pb="clr-namespace:Spritehand.PhysicsBehaviors;assembly=Spritehand.PhysicsBehaviors"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">

I gave them nicknames of “i” and “pb” respectively, but you can call them whatever works for you. They’re just going to be shortcuts for adding our physics behaviors to our XAML elements.

5. Adding our PhysicsControllerBehavior

The first thing we need to do is define our PhysicsController. In most cases, this will be your primary <Canvas>, but you can really make any XAML container your PhysicsController. To do this in XAML, we going to add a Behaviors node to our &ltCanvas> and then specify the physics behavior we want to use. It looks like this:

<Canvas x:Name="LayoutRoot" Background="Black">
<
i:Interaction.Behaviors>
<
pb:PhysicsControllerBehavior MousePickEnabled="True"/>
</
i:Interaction.Behaviors>

You can see that I have added the PhysicsControllerBehavior to our <Canvas>. I also enabled the MousePickEnabled property, which will allow us to pick up and move any of our XAML elements that are specified as PhysicsObjects. Let’s do that next.

6. Creating PhysicsObjects

Much like we did with the PhysicsController, we are going to add a behavior to elements of our XAML document. The first two we’re going to start with are the <Ellipse> and the white rectangular platform at the bottom. For each of these, we’ll have to expand the tags to not be self-closing, but they’ll have to have both opening and closing tags. As an example, here’s what the <Ellipse> should look like now:

        <Ellipse Fill="Red" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Top="173" Canvas.Left="255">
<
i:Interaction.Behaviors>
<
pb:PhysicsObjectBehavior/>
</
i:Interaction.Behaviors>
</
Ellipse>

Our <Rectangle> platform will have the same thing done to it, but we are going to add one property: IsStatic. This defines the platform as an object that will participate in the physics engine, but doesn’t have any gravity affecting it. By default, each element that is a PhysicsObject will automatically have gravity, mass, etc. Here’s what our platform looks like:

        <Rectangle Fill="White" StrokeThickness="2" Height="29" Width="640" Canvas.Left="0" Canvas.Top="475">
<
i:Interaction.Behaviors>
<
pb:PhysicsObjectBehavior IsStatic="True"/>
</
i:Interaction.Behaviors>
</
Rectangle>

If you run your project now, you should see the red ball fall behind the pyramid of blocks, and “land” on the platform. Also, because we used the MousePickEnabled property on our PhysicsController, you can click on the ball and drag it around the screen, throwing it in the air, rolling it along the platform, etc. Have some fun with it! Please note that the “pyramid” of blocks don’t interact with the ball AT ALL. This is because they have not been defined as PhysicsObjects yet. That’s our next step.

7. Creating a falling pyramid.

For each of the <Rectangles> in our pyramid, we are going to add the same code that we added to our <Ellipse>. Once you’ve done that, your entire XAML file should look like this:

<UserControl x:Class="SilverlightPhysicsPyramid.MainPage"
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"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:pb="clr-namespace:Spritehand.PhysicsBehaviors;assembly=Spritehand.PhysicsBehaviors"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<
Canvas x:Name="LayoutRoot" Background="Black">
<
i:Interaction.Behaviors>
<
pb:PhysicsControllerBehavior MousePickEnabled="True"/>
</
i:Interaction.Behaviors>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="215" Canvas.Top="433">
<
i:Interaction.Behaviors>
<
pb:PhysicsObjectBehavior/>
</
i:Interaction.Behaviors>
</
Rectangle>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="245" Canvas.Top="433">
<
i:Interaction.Behaviors>
<
pb:PhysicsObjectBehavior/>
</
i:Interaction.Behaviors>
</
Rectangle>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="275" Canvas.Top="433">
<
i:Interaction.Behaviors>
<
pb:PhysicsObjectBehavior/>
</
i:Interaction.Behaviors>
</
Rectangle>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="305" Canvas.Top="433">
<
i:Interaction.Behaviors>
<
pb:PhysicsObjectBehavior/>
</
i:Interaction.Behaviors>
</
Rectangle>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="335" Canvas.Top="433">
<
i:Interaction.Behaviors>
<
pb:PhysicsObjectBehavior/>
</
i:Interaction.Behaviors>
</
Rectangle>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="365" Canvas.Top="433">
<
i:Interaction.Behaviors>
<
pb:PhysicsObjectBehavior/>
</
i:Interaction.Behaviors>
</
Rectangle>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="230" Canvas.Top="403">
<
i:Interaction.Behaviors>
<
pb:PhysicsObjectBehavior/>
</
i:Interaction.Behaviors>
</
Rectangle>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="290" Canvas.Top="283">
<
i:Interaction.Behaviors>
<
pb:PhysicsObjectBehavior/>
</
i:Interaction.Behaviors>
</
Rectangle>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="305" Canvas.Top="313">
<
i:Interaction.Behaviors>
<
pb:PhysicsObjectBehavior/>
</
i:Interaction.Behaviors>
</
Rectangle>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="275" Canvas.Top="313">
<
i:Interaction.Behaviors>
<
pb:PhysicsObjectBehavior/>
</
i:Interaction.Behaviors>
</
Rectangle>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="320" Canvas.Top="343">
<
i:Interaction.Behaviors>
<
pb:PhysicsObjectBehavior/>
</
i:Interaction.Behaviors>
</
Rectangle>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="290" Canvas.Top="343">
<
i:Interaction.Behaviors>
<
pb:PhysicsObjectBehavior/>
</
i:Interaction.Behaviors>
</
Rectangle>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="260" Canvas.Top="343">
<
i:Interaction.Behaviors>
<
pb:PhysicsObjectBehavior/>
</
i:Interaction.Behaviors>
</
Rectangle>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="335" Canvas.Top="373">
<
i:Interaction.Behaviors>
<
pb:PhysicsObjectBehavior/>
</
i:Interaction.Behaviors>
</
Rectangle>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="305" Canvas.Top="373">
<
i:Interaction.Behaviors>
<
pb:PhysicsObjectBehavior/>
</
i:Interaction.Behaviors>
</
Rectangle>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="275" Canvas.Top="373">
<
i:Interaction.Behaviors>
<
pb:PhysicsObjectBehavior/>
</
i:Interaction.Behaviors>
</
Rectangle>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="245" Canvas.Top="373">
<
i:Interaction.Behaviors>
<
pb:PhysicsObjectBehavior/>
</
i:Interaction.Behaviors>
</
Rectangle>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="350" Canvas.Top="403">
<
i:Interaction.Behaviors>
<
pb:PhysicsObjectBehavior/>
</
i:Interaction.Behaviors>
</
Rectangle>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="320" Canvas.Top="403">
<
i:Interaction.Behaviors>
<
pb:PhysicsObjectBehavior/>
</
i:Interaction.Behaviors>
</
Rectangle>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="290" Canvas.Top="403">
<
i:Interaction.Behaviors>
<
pb:PhysicsObjectBehavior/>
</
i:Interaction.Behaviors>
</
Rectangle>
<
Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="260" Canvas.Top="403">
<
i:Interaction.Behaviors>
<
pb:PhysicsObjectBehavior/>
</
i:Interaction.Behaviors>
</
Rectangle>
<
Rectangle Fill="White" StrokeThickness="2" Height="29" Width="640" Canvas.Left="0" Canvas.Top="475">
<
i:Interaction.Behaviors>
<
pb:PhysicsObjectBehavior IsStatic="True"/>
</
i:Interaction.Behaviors>
</
Rectangle>
<
Ellipse Fill="Red" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Top="173" Canvas.Left="255">
<
i:Interaction.Behaviors>
<
pb:PhysicsObjectBehavior/>
</
i:Interaction.Behaviors>
</
Ellipse>
</
Canvas>
</
UserControl>

8. A quick summary

So, in a few quick minutes, we’ve created an environment that behaves like the real world. Try grabbing and dragging one of the bottom blocks from the pyramid. The structure crumbles much like you’d expect a stack of blocks to crumble. By starting with the simple, we can start to add more and more robust behaviors to our XAML elements, creating some really interesting user interfaces. If you’d like to see more examples of how to use these tools and behaviors, head over to the Codeplex project for the PhysicsHelper and download their Silverlight examples. There’s some neat examples of fluid dynamics, explosions, and even a Pinball game. Here’s our finished application:

2 thoughts on “Day #22: Using The Farseer Physics Engine in Silverlight

  1. Great post, Jeff!I don't seem to have the System.Windows.Interactivity assembly available on my machine. I think I heard Rocky Lhotka mention in a recent DNRTV episode that this assembly is only available if Expression Blend 3 is installed. Do you know if this is true?Thanks!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s