I’m sure some of us may be taking a little break for the holidays, and might even get a few hours of coding time to sip from the firehose of technologies streaming out of Microsoft.
Might I recommend indulging in a little Silverlight game programming? J
To help you along, and as my gift for the holidays, I have put together an introduction to using Silverlight and Farseer Physics in perfect harmony.
Happy holidays!
Overview
The Farseer Physics Engine was created by Jeff Weber and later ported to Silverlight by Bill Reiss. Game programming with Silverlight is fun enough, but when you combine it with this easy to use physics engine, it’s almost as much fun to create games as play them!
In this walkthrough, I will be using the familiar “foobar” sprites from the Sort the Foobars game I posted last month.
DOWNLOAD THE SOURCE CODE
SEE THE SAMPLE IN ACTION
The Ingredients
First, let’s cover what you will need for this walkthrough:
- Visual Studio 2008. I am using the Professional Edition, but you should be fine with Express as well.
- Microsoft Silverlight 1.1 Tools Alpha for Visual Studio 2008. This provides the silverlight templates for VS.
- Microsoft Silverlight 1.1 Alpha Refresh. This is the actual Silverlight plug-in, in case you have not already installed it.
- (Optional) Microsoft Expression Blend 2 December Preview. Recommended, but if you’re not into playing with the design stuff, you can skip this.
- (Optional) Farseer Physics Engine and Silverlight Samples.
Recommended, because there are some really great Silverlight samples by Bill Reiss in the download.
Creating the Physics Simulator
The PhysicsSimulator class is your main communication channel with the Physics Engine. If we examine Page.xml.cs, we can see the create call, which passes in a value for gravity. In this case, gravity will be straight down in the vertical (y) direction with a value of 500:
PhysicsSimulator _physicsSimulator;
_physicsSimulator = new PhysicsSimulator(new Vector2(0, 500));
_physicsSimulator.Iterations = 10;
We also optionally set Iterations which will affect the precision of the physics calculations, at the cost of performance.
Creating the “Sprites”
User Controls are a convenient mechanism for creating and animating the characters (or Sprites) in your game. You can draw them out in Expression Blend, and even include Storyboard animations to bring them to life.
We need to be sure to add a RotateTransform and TranslateTransform to the User Controls. These will be required for integration with Farseer, since the physics engine will determine position and rotation of the sprites at all times.
In the demo application, there are three User Controls defined:
- ucHead.xaml – this is the cute little head that drops from the sky and slams into the ground. Note that there are two Storyboard animations defined for the head: timeLineMoveEyes, which gives the little bugger some life by moving his eyes around, and timeLineOuchy, which plays when the head collides with another sprite.
- ucGround.xaml – the grassy floor that the heads collide with initially.
- ucWall.xaml – this is used for both the left and right side “walls” which keep the little heads from bouncing off our playing field.
If we look at each the XAML for any of these user controls, we can see that there are two transforms defined for the main canvas:
<Canvas.RenderTransform>
<TransformGroup>
<!-- This transform conrols the rotation-->
<RotateTransform x:Name="rotateSprite" Angle="0" CenterX="30" CenterY="30"/>
<TranslateTransform x:Name="translateTransform" X="0" Y="0"/>
</TransformGroup>
</Canvas.RenderTransform>
… these transforms will be used to keep the Farseer Physics Bodies in sync with our user controls’ positions. One thing to note which can be confusing is that the Farseer Engine keeps track of x,y position at the center of an object, instead of at the top left which we are used to. So the TranslateTransform will be used to keep that centered position of the object at our top, left position on screen.
Also note that each of these user controls inherits from SpriteBase instead of Control:
namespace HeadDrop
{
public class ucHead : SpriteBase
{
This technique was explained in an earlier blog post, any allows for some OOP and code reuse. SpriteBase contains basic sprite information such as the Farseer Body Object, position, and state.
We also need to define the Physics Body and Geometry (for collisions) for each sprite. Let’s examine the code in ucHead.xaml.cs.
First,we define the Physics body, which gives the object a location and size in the simulated world. In the case of the “head sprites” we want to create a circular body to match their shape. We pass in an instance of the Physics Simulator, the radius of the circle, and a mass value:
BodyObject = BodyFactory.Instance.CreateCircleBody(physicsSim, (float)getWidth / 2, 0.5f);
Next, we define the Geometry of the sprite, which is used for collision detection by the Physics Engine. We call CreateCircleGeometry passing in the Physics Simulator, the BodyObject defined in the call above, the radius of the circle, and also a parameter called collisionGridCellSize – which is used to change the accuracy of collision detection at the cost of performance. Depending on your game or simulation, you may want to play with the value of collisionGridCellSize if you are seeing jittery movement or other collision issues.
fsc.Geometry headGeometry = fsc.GeometryFactory.Instance.CreateCircleGeometry(physicsSim, BodyObject, (float)getWidth / 2, 20);
Next, we set up the Collision event handler, which will fire when this sprite collides with other sprites in another CollisionGroup. We should also give the geometry a unique Tag value, as this is a handy way to identify it when collision events occur:
headGeometry.Tag = "headGood";
headGeometry.CollisionHandler += new FarseerGames.FarseerPhysics.Collisions.Geometry.CollisionHandlerDelegate(HandleCollision);
The Timer Event
Inside our Game Timer Event (_timer_Completed, implemented as a Storyboard), we need to be sure to update the Physics Simulator to a new point in time, so that it can calculate the new positions and velocities of all objects in the simulation. To do so, we simply call Update on the Physics object, passing in a time span that we wish to update to:
_physicsSimulator.Update(.02f);
Once this call is made, the Body objects in the Farseer library will have the new positions calculated for all of our sprites (user controls). However, it is up to us to ensure that the sprites are actually moved to their new position in the UI. To ensure this, we call the Update method on all of our Sprites:
foreach (SpriteBase sprite in _spritesEnvironment)
{
sprite.Update();
}
foreach (ucHead sprite in _spritesHeadGood)
{
sprite.Update();
}
The implementation of this Update is contained in our SpriteBase class. This method synchronizes the X,Y position of the sprite to the X,Y position of the Farseer Body object:
public virtual void Update()
{
if (BodyObject == null) return;
if (_X != BodyObject.Position.X)
{
_X = BodyObject.Position.X;
RootCanvas.SetValue<double>(Canvas.LeftProperty, _X);
}
if (_Y != BodyObject.Position.Y)
{
_Y = BodyObject.Position.Y;
RootCanvas.SetValue<double>(Canvas.TopProperty, _Y);
}
if (BodyObject.Rotation != _rotation)
{
_rotation = BodyObject.Rotation;
RotationTransform.Angle = (_rotation * 360) / (2 * Math.PI);
}
}
Handling Collisions
If we look at ucHead.xaml.cs, we can see there is a HandleCollision event handler which we wired up above when creating the sprites:
private bool HandleCollision(fsc.Geometry g1, fsc.Geometry g2, fsc.ContactList contactList)
{
if (BodyObject.LinearVelocity.X > 300 || BodyObject.LinearVelocity.Y > 300)
{
// this was a fairly hard hit - begin the ouchy animation
_timeLineOuchy.Begin();
Random rand = new Random();
}
// NOTE that we can track when two objects collide in the following way:
// 1. add Tag values to each Geometry
// 2. inside this event we compare tag values to see if a collision happened...
if (g1.Tag.ToString() == "groundLeft" || g2.Tag.ToString() == "groundLeft")
{
// collision happened
}
return true;
}
While this implementation of the Collision Handler is very simple, you can imagine that this would be a very handy method for triggering explosions and other events when two sprites collide.
In this example, we check if the LinearVelocity of the body is fairly high ( > 300) and if so, we play an “ouchy” animation which makes the little head frown because he has been hit L
Summary
I have only hit on the highlights of this sample, to help you understand how Silverlight and Farseer can work together. Hopefully the strategies I mentioned here will help you along in creating your own great physics games in Silverlight!