Making your own WP7 Angry Birds

Currently on the blog you’ll find a series of posts detailing how to write your own Angry Birds for WP7 using XNA.

You can download the free app from the Marketplace: http://social.zune.net/redirect?type=phoneApp&id=3fb4ef79-9983-e011-986b-78e7d1fa76f8
If the  above link doesn’t work, use the Marketplace on your phone and search for ‘Pigs Fly’.

The game will look like this:

What WP7 should learn from Windows 8

The Windows Phone Marketplace Certification team sent me a mail today notifying me that Pigs Fly had a non-critical problem: for the marketplace I used icons with a transparent background.

For those who don’t know yet: every WP7 app has five different icons: 3 versions for the Marketplace and 2 for the Start screen/app list.

At first I wondered why they disallowed this transparency for the Marketplace icons, but seem to encourage it for the Start screen icons.

Have a look at the screenshot to the left. You notice a lot of the tiles share the same accent color. This is because of the transparent background obviously. When you change the accent color your whole Start screen gets a make-over.

This looks great! Actually, it only looks great on screenshots and during demos. When I actually use my phone all I see is a sea of identically-colored tiles. This is a usability nightmare!

Look at the contrast with the Windows 8 screenshot below, where every tile has a different background color. I bet that after a while you will automatically recognize the Green tile as the Stocks tile, the Orange tile as the Calendar tile, and so on… .

Now all I can wonder is why the certification rules allow transparent backgrounds for any of the icons at all.

Get rid of the accent color and give every icon its own distinctive background color.

Making your own WP7 Angry Birds – Part 8 – Introducing the target

So far we can shoot our pig at a stack of blocks. It is now time to introduce a proper target. What better target than a bird?

.

1. Creating a target

Save the above image as bird.png and add it to the spritesheet xml asset. Before we add it to the PrefabBodyFactory we need to add a new property to PrefabUserData:

public bool IsTarget { get; set; }

Obviously, this property is used to set apart our target bodies from regular bodies, such as our plain old GrayBox.

Now we can add the following to the PrefabBodyFactory:

Library.Add(PrefabType.Bird, (world, position) =>
		{
			Body body = CreateCircle(world, position, "bird", 0.2f, 3);
			((PrefabUserData) body.UserData).IsTarget = true;
			return body;
		}
	);

As you can see, nothing special going on, except we also mark this body as a target, using our new IsTarget property.

2. Keeping scores

The goal of the game will of course be to destroy all of the targets.

To keep track of our progress we will introduce a Scoreboard class.
Every time a body is destroyed we will inform the scoreboard. If it figures out all targets have been destroyed it will set an IsSuccess property to True.

Having said that, here’s the implementation:

public class Scoreboard
{
	private int _numberOfTargets;
	private int _numberTargetsDestroyed;
	private bool _isSuccess;

	public bool IsSuccess
	{
		get { return _isSuccess; }
	}

	public static readonly Scoreboard Current = new Scoreboard();

	private Scoreboard()
	{
	}
public void Initialize(List<Body> bodies)
	{
		_numberTargetsDestroyed = 0;
		_numberOfTargets = 0;

		foreach (var body in bodies)
		{
			if (((PrefabUserData)body.UserData).IsTarget)
			{
				_numberOfTargets++;
			}
		}
	}

	public void ProcessDestroyedBody(Body body)
	{
		if (((PrefabUserData)body.UserData).IsTarget)
		{
			_numberTargetsDestroyed++;
			if (_numberTargetsDestroyed == _numberOfTargets)
				_isSuccess = true;
		}
	}
}

To wire up the scoreboard to our game, we need to call Initialize, ProcessDestroyedBody and IsSuccess.

Initialize

Here’s what our updated LoadContent looks like. We add the bird to our level. When our _bodies list is complete we pass it to the Scoreboard‘s Initialize method.

...
Body box1 = PrefabBodyFactory.CreateBody(PrefabType.GrayBox, _world, new Vector2(9.0f, 3.999274f));
_bodies.Add(box1);
Body box2 = PrefabBodyFactory.CreateBody(PrefabType.GrayBox, _world, new Vector2(9.0f, 3.612630f));
_bodies.Add(box2);
Body box3 = PrefabBodyFactory.CreateBody(PrefabType.GrayBox, _world, new Vector2(9.0f, 3.222778f));
_bodies.Add(box3);
Body bird = PrefabBodyFactory.CreateBody(PrefabType.Bird, _world, new Vector2(9.0f, 3.0f));
_bodies.Add(bird);
Body pig = PrefabBodyFactory.CreateBody(PrefabType.Pig, _world, new Vector2(1.5f, 3.9f));
_bodies.Add(pig);
_projectile = new Projectile(pig);

Scoreboard.Current.Initialize(_bodies);
...

As you can see, the pile of boxes is moved a little further away than before. You might want to adjust your Projectile‘s ImpulseModifier to give it enough force to reach this new distance.

ProcessDestroyedBody

In PrefabUserData we have a method Destroy. This is the place where we will inform our Scoreboard to keep count:

public void Destroy(Body body)
{
	Status = BodyStatus.Destroyed;
	ExplosionAnimation.Activate(body);
	Scoreboard.Current.ProcessDestroyedBody(body);
}

IsSuccess

In our Game‘s Draw method we will add the following code at the end of the method.
If the Scoreboard marked the game as won (IsSuccess), we draw a ‘You won!’ texture in the middle of the screen.

Notice we use a new Spritebatch begin/end block. The original Spritebatch block we used takes the camera’s position into account.
As we simply want to draw our texture in the middle of the screen, we don’t want to have the Camera‘s TransformationMatrix to interfere.

Make sure to make a sprite called ‘youwon’ and add it to the spritesheet to make the code below work correctly.

	...

	if (Scoreboard.Current.IsSuccess)
	{
		spriteBatch.Begin();

		spriteBatch.Draw(
			_sheet.Texture,
			new Vector2(302, 200),
			_sheet.SourceRectangle("youwon"),
			Color.White
			);

		spriteBatch.End();
	}

	base.Draw(gameTime);
}

And here’s today’s result:

Mango’s new GameStateManagement

With the beta release of the  Windows Phone Developer Tools for Mango, an updated GameStateManagement sample has been released: http://create.msdn.com/en-US/education/catalog/sample/game_state_management

What exactly has been updated?

1. Lifecycle events taken from Microsoft.Phone assembly (part of the Silverlight assemblies), also supporting the reactivation of ‘preserved game instances’
2. Introduction of the IScreenFactory interface
3.  Introduction of the InputAction class

1. Lifecycle events taken from Microsoft.Phone assembly

The original GameStateManagement sample used the Game’s constructor to deserialize the peristed game state. By overriding the OnExiting method it serialized the game state.

In the Mango release this has been changed to use the following events:

// Hook events on the PhoneApplicationService so we're notified of the application's life cycle
Microsoft.Phone.Shell.PhoneApplicationService.Current.Launching +=
    new EventHandler<Microsoft.Phone.Shell.LaunchingEventArgs>(GameLaunching);
Microsoft.Phone.Shell.PhoneApplicationService.Current.Activated +=
    new EventHandler<Microsoft.Phone.Shell.ActivatedEventArgs>(GameActivated);
Microsoft.Phone.Shell.PhoneApplicationService.Current.Deactivated +=
    new EventHandler<Microsoft.Phone.Shell.DeactivatedEventArgs>(GameDeactivated);

The Launching event is called when the application is started by tapping the icon for the application from the installed programs list or by tapping the tile for the application on the device’s Start screen.

The Actived event is called when the user returns to your application. The application can be either tombstoned (serialized state) or dormant. The dormant state is new (fast app switching) and in this case the ActivedEventArgs.IsApplicationInstancePreserved will be set to True.

The Deactived event is called when the user navigates away from your application and offers the possibility to tombstone your application.

For more info on the the execution model of WP7 apps see this link: http://msdn.microsoft.com/en-us/library/ff817008(v=VS.92).aspx.

2. Introduction of the IScreenFactory interface

In the original GameStateManagement sample, it was required that all GameScreen constructors were parameterless as it uses Activator.CreateInstance(screenType).

This restriction has been lifted by introducting an IScreenFactory interface. The interface defines a method CreateScreen, which is called with a parameter Type screenType.

By implementing the public GameScreen CreateScreen(Type screenType) method, you can choose to fetch additional parameters from Isolated storage or the State dictionary and use them to instatiate your game screens.
If all your GameScreens have default constructors you can implement the interface as followed:

public GameScreen CreateScreen(Type screenType)
{
    // All of our screens have empty constructors so we can just use Activator
    return Activator.CreateInstance(screenType) as GameScreen;
}

See the GameStateManagementSample’s ScreenFactory implementation for more details.

3.  Introduction of the InputAction class

Here is the verbatim quote of the class’ summary:

 Defines an action that is designated by some set of buttons and/or keys.

 The way actions work is that you define a set of buttons and keys that trigger the action. You can
 then evaluate the action against an InputState which will test to see if any of the buttons or keys
 are pressed by a player. You can also set a flag that indicates if the action only occurs once when
 the buttons/keys are first pressed or whether the action should occur each frame.

 Using this InputAction class means that you can configure new actions based on keys and buttons
 without having to directly modify the InputState type. This means more customization by your games
 without having to change the core classes of Game State Management.

For WP7 apps this can be used to react on the Back button.

For example:


InputAction menuCancel = new InputAction(new Buttons[] { Buttons.Back }, null, true);

...
if (menuCancel.Evaluate(input, ControllingPlayer, out player))
{
    //executecancel code
}

Follow me on twitter? Sure! http://www.twitter.com/jodegreef

Making your own WP7 Angry Birds – Part 7 – Explosions in the sky

Today is a cosmetic update for the previous post: we will add a animated puff of smoke when one of our boxes is destroyed.

See the video for the effect we want to achieve:

.

1. Smoke animation

We will introduce a class that will draw the explosion. The class will be responsible for the animation as well. What exactly will we animate?
The puff of smoke will be visible for 400 milliseconds. During that time it will go from a solidly rendered sprite to a fully transparent sprite. At the same time, it will scale from 75% up to 175% of its size.

Here’s the smoke sprite we will use in the example. Save it as ‘smoke.png’ and add it to the spritesheet as usual.

2. Framerate-independent animations

The following line of code is a common way of implementing animations, independent from the framerate. If in the future our game runs at 60fps instead of 30, we don’t want the animation to speed up. Rather than that, it will animate is smaller steps, resulting in a smoother animation.

transition += (float)(gameTime.ElapsedGameTime.TotalMilliseconds /
			TimeSpan.FromSeconds(0.4).TotalMilliseconds);

The line above can be called from either a Draw or an Update method and will make the transition variable go from 0.0f to 1.0f in 400 milliseconds. It does so by dividing GameTime.ElapsedGameTime by the desired timespan (0.4s in this case).

GameTime.ElapsedGameTime returns a timespan from since the last Update. By creating the sum of these timespan slices over a period of frames, we eventually reach a total of 1 second. The division slows down the rate at which we achieve 1 second: divide by 2 and it will take 2 seconds instead of 1.

Now you can use the transition variable as a seed for your animations. Need to animate a movement from position X to position X + 200? Simply draw the animation using X + (transition * 200.0f).

You can apply this logic to all instances where you need to have time-dependent, but framerate-independent animations.

3. ExplosionAnimation implementation

Our animated smoke puff will be implemented in an ExplosionAnimation class which has an Activate and a Draw method. Activate will signal that the animation can start, while the Draw method obviously renders the animation on screen.

/// <summary>
/// Component to display an explosion
/// </summary>
public class ExplosionAnimation
{
	private bool _isActive;
	private Vector2 _position;
	private float _rotation;

	private float _transitionValue; // goes from 0 (start explosion) to 1 (end)

	private TimeSpan _timeToLive = TimeSpan.FromSeconds(0.4); //how long the explosion lives

	private readonly string _spritename;

	public ExplosionAnimation(string spritename)
	{
		_spritename = spritename;
	}

	/// <summary>
	/// Start an explosion
	/// </summary>
	/// <param name="body">exploded body</param>
	public void Activate(Body body)
	{
		_position = body.Position * Constants.Scale;
		_rotation = body.Rotation;

		_transitionValue = 0f;
		_isActive = true;
	}

	public void Draw(SpriteBatch sb, SpriteSheet spritesheet, GameTime elapsedGameTime)
	{
		if (_isActive)
		{
			UpdateTransition(elapsedGameTime);

			int colorChannel = 255 - (int)(_transitionValue * 255);
			int alphaCannel = 160 - (int)(_transitionValue * 160);
			Color color = new Color(colorChannel, colorChannel, colorChannel, alphaCannel);
			float scale = 0.75f + _transitionValue;

			Rectangle sourceRect = spritesheet.SourceRectangle(_spritename);
			Vector2 origin = new Vector2(sourceRect.Width / 2.0f, sourceRect.Height / 2.0f);

			sb.Draw(
				spritesheet.Texture,
				_position,
				sourceRect,
				color,
				_rotation,
				origin,
				scale,
				SpriteEffects.None,
				0);
		}
	}

	private void UpdateTransition(GameTime elapsedGameTime)
	{
		// How much should we move by?
		float transitionDelta = (float)(elapsedGameTime.ElapsedGameTime.TotalMilliseconds /
										_timeToLive.TotalMilliseconds);

		// Update the transition position.
		_transitionValue += transitionDelta;

		// Did we reach the end of the transition?
		if (_transitionValue >= 1)
		{
			_transitionValue = 1f;
			_isActive = false;
		}
	}
}

Last time we refactored our drawing code in order to have a Body draw itself through its PrefabUserData instance. We will now extend it so it can Draw an animated explosion.

Add the following property and constructor to PrefabUserData so that it has an instance of ExplosionAnimation available:

public ExplosionAnimation ExplosionAnimation { get; set; }

public PrefabUserData()
{
	ExplosionAnimation = new ExplosionAnimation("smoke");
}

Next, add the Destroy method, which will set the Status to Destroyed and activates the animation.

public void Destroy(Body body)
{
	Status = BodyStatus.Destroyed;
	ExplosionAnimation.Activate(body);
}

Finally, add the following case to PrefabUserData.Draw:

case BodyStatus.Destroyed:
	ExplosionAnimation.Draw(spriteBatch, sheet, gameTime);
	break;

The last step is to update our Game class. Look for the following line within the ProcessContact method:

userData.Status = BodyStatus.ToBeDestroyed

Replace it with:

userData.Destroy(body);

All done!

Making your own WP7 Angry Birds – Part 6 – Impact!

Today we will implement a physics effect: when a box gets hit with sufficient force, it is destroyed and removed from our world.

In the next post, I will explain how to animate the destruction of our body.


Before we can destroy a body we need to keep track of its health (= hit points). Each collision, or contact as it is known in Farseer, between our projectile and a body, or a collision between two regular bodies will result in the loss of a certain amount of points. Once it reaches zero, the body is destroyed.

1. New PrefabUserData properties

Add the following properties to PrefabUserData:

public int HitPoints { get; set; }
public BodyStatus Status { get; set; }
public bool IsProjectile { get; set; }

Here’s what the BodyStatus enum looks like:

public enum BodyStatus
{
	Active,
	ToBeDestroyed,
	Destroyed
}

Notice that we now keep track of the status of a body: is it ‘alive’ or has it been destroyed?
Rather than dereferencing a destroyed body, we will marked it as destroyed. Why? Allocating and de-allocating objects feeds the garbage collector and we don’t want that. If the GC decides to kick in, it will cause frames to be dropped while it’s doing its business. This is a common trick applied in XNA games.

The last property we’ve added is the boolean IsProjectile; we need to know if a body is a regular body or a projectile. Why? In our game we don’t want our projectile to break upon impact. We will make it invincible and have it self-destruct a couple of seconds after impact (more on this in the future).

2. Initializing the new PrefabUserData properties

We will initialize the HitPoints in the PrefabBodyFactory. Add an additional parameter to CreateRectangle and CreateCircle which holds the amount of hit points a body has.

This is what my PrefabBodyFactory looks like now. Notice the last parameter, which are the hitpoints for my bodies. After some trial-and-error, it turns out 7 is a good value for our game as it is right now.

static PrefabBodyFactory()
{
	Library.Add(PrefabType.GoldBox, (world, position) => CreateRectangle(world, position, "box1", 0.4f, 0.4f, 7));
	Library.Add(PrefabType.GrayBox, (world, position) => CreateRectangle(world, position, "box2", 0.4f, 0.4f, 7));
	Library.Add(PrefabType.Pig, (world, position) => CreateRectangle(world, position, "midipig", 0.5f, 0.59f, 7));
}

The IsProjectile property will be set from the Projectile class; the wrapper class that upgrades our regular Body into a Body we can catapult.

public Projectile(Body body)
{
	Body = body;
	((PrefabUserData) Body.UserData).IsProjectile = true;
}

3. Deducting hitpoints upon impact

Now that we have setup all the data, it’s time to work on the logic. When the Farseer engine simulation detects a collision between 2 bodies, it can notify us by calling a delegate and providing us with all the collision data grouped into a Contact object.
We assign a method to this delegate in our LoadContent:

_world.ContactManager.BeginContact = BeginContact;

Here is our implementation of BeginContact:

public bool BeginContact(Contact contact)
{
	Body bodyA = contact.FixtureA.Body;
	Body bodyB = contact.FixtureB.Body;

	// get the speed of impact between the two bodies
	Manifold worldManifold;
	contact.GetManifold(out worldManifold);
	ManifoldPoint p = worldManifold.Points[0];
	Vector2 vA = bodyA.GetLinearVelocityFromLocalPoint(p.LocalPoint);
	Vector2 vB = bodyB.GetLinearVelocityFromLocalPoint(p.LocalPoint);
	float approachVelocity = Math.Abs(Vector2.Dot(vB - vA, worldManifold.LocalNormal));

	//deduct hitpoints from both bodies
	ProcessContact(contact, bodyA, approachVelocity);
	ProcessContact(contact, bodyB, approachVelocity);

	return true;
}

private void ProcessContact(Contact contact, Body body, float approachVelocity)
{
	PrefabUserData userData = body.UserData as PrefabUserData;

	// only deduct hitpoints if there is userdata (word edges have no userdata)
	// and
	// if the body is not our projectile (projectiles are invincible)
	if (userData != null && !userData.IsProjectile)
	{
		var hitPoints = (int)Math.Round(approachVelocity);
		userData.HitPoints -= hitPoints;

		if (userData.HitPoints <= 0)
		{
			// let Farseer know this contact and body are processed and
			// will have no further impact (pun intended)
			contact.Enabled = false;
			body.IsSensor = true;
			// mark this status as ToBeDestroyed
			userData.Status = BodyStatus.ToBeDestroyed;
		}
	}
}

4. Drawing and *not* drawing bodies when appropriate

Our next step is doing some refactoring of existing code: we will move code to draw a Body from the main Game class to PrefabUserData. This way a Body knows how to Draw itself, rather than letting someone else (=Game) do its drawing. This is a cleaner design and you will see the pay off in later on in the series.

Here’s the loop to draw our bodies right now:

foreach (var body in _bodies)
{
	var prefabUserData = (PrefabUserData) body.UserData;

	spriteBatch.Draw(
		_sheet.Texture,					// Texture
		body.Position * Constants.Scale,		// Position
		_sheet.SourceRectangle(prefabUserData.SpriteName),
		Color.White,					// Neutral
		body.Rotation,					// Rotation
		prefabUserData.Origin * Constants.Scale,	// Origin, relative to source rectangle size
		1f,								// Scale
		SpriteEffects.None,				// No effects
		0f);							// Z-layer
}

Change it into this:

foreach (var body in _bodies)
{
	var prefabUserData = (PrefabUserData) body.UserData;

	prefabUserData.Draw(gameTime, spriteBatch, _sheet, body);
}

And move the code into a new Draw method on PrefabUserData:

public void Draw(GameTime gameTime, SpriteBatch spriteBatch, SpriteSheet sheet, Body body)
{
	spriteBatch.Draw(
		sheet.Texture, // Texture
		body.Position*Constants.Scale, // Position
		sheet.SourceRectangle(SpriteName),
		Color.White, // Neutral
		body.Rotation, // Rotation
		Origin*Constants.Scale, // Origin, relative to source rectangle size
		1f, // Scale
		SpriteEffects.None, // No effects
		0f);
	}
}

Now we have done this refactoring, we will immediately make a change to the PrefabUserData.Draw method.

I made this two distinctive steps to make the difference clear between the refactoring, which doesn’t change behaviour, and the addition of a new feature, which does affect behaviour.

The change is simple: only draw the body if it is still in an Active state:

public void Draw(GameTime gameTime, SpriteBatch spriteBatch, SpriteSheet sheet, Body body)
{
	switch(Status)
	{
		case BodyStatus.Active:
			spriteBatch.Draw(
				sheet.Texture, // Texture
				body.Position*Constants.Scale, // Position
				sheet.SourceRectangle(SpriteName),
				Color.White, // Neutral
				body.Rotation, // Rotation
				Origin*Constants.Scale, // Origin, relative to source rectangle size
				1f, // Scale
				SpriteEffects.None, // No effects
				0f);
			break;
	}
}

Run the code as-is and catapult the pig into our tower of boxes. Unlike before, you should see some boxes diappear immediatly upon impact:


Next time, we will animate the explosions instead of just having them disappear. Meanwhile you can follow me on Twitter: http://www.twitter.com/jodegreef.

Making your own WP7 Angry Birds – Part 5 – Camera? Action!

Up until now our world was restricted to a 800×480 view. We’d like our game to have a slightly larger playing field however, especially on the horizontal axis.

To achieve this we need to introduce a camera, which can pan horizontally. The only tricky part is that the camera will affect our input handling system: when we click on a certain position on the screen, it is no longer an absolute position in our world, but a position relative to the camera’s position.

Our camera will only do horizontal panning, but vertical panning as well as zooming in and out are easy to support later on.

This will be our goal for now: we start with the camera pointed at the left most part of the level. When we catapult our pig, the camera will start travelling to the right, keeping the pig in center view. It will continue to do so until the tower of boxes comes into the center view of the camera. At this point the camera stops panning and we see the impact happening.
See this video which shows a demo of our camera: http://www.youtube.com/watch?v=V4j6y0iNUVs.

1. Initializing the camera

During LoadContent we will initialize our camera and define the horizontal range it can cover. The left most point on the horizontal axis is zero in all cases.
As right most point we choose the to not pan farther than the center of our target: the tower of boxes. We will call this the CenterPointTarget and can be a different value for each level.
Finally, we will also instruct the camera to track our Projectile.

protected override void LoadContent()
{
	...
	Camera.Current.CenterPointTarget = 620f;
	Camera.Current.StartTracking(_projectile.Body);
}

2. Keeping the camera up-to-date

In the Game.Update method we will need to call an Update method on our Camera instance. In this method it will perform the required panning in order to keep our pig centered and stay within our level’s range.

/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
	Camera.Current.Update(gameTime);
	...
}

3. Taking the camera into account when handling input

In our Projectile’s HandleInput method we convert our touch position to a simulated world coordinate by changing this line:

Vector2 touchPositionSim = touchLocation.Position / Constants.Scale;

to:

Vector2 touchPositionSim = Camera.Current.ScreenToSimulation(touchLocation.Position);

4. Seeing the world through the camera’s point of view

Finally, in the Game.Draw method we need to take the camera into account in order to render our level according to the current camera’s panning value.
We will expose a Matrix property on our Camera, which defines the translation. Matrices can be used for translations, rotations, scaling and much more.
One of the overloads of SpriteBatch.Begin accepts such a Matrix.  This transformation matrix will cause our sprites to be rendered by a certain offset on the screen, without having to change their position.

/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
	GraphicsDevice.Clear(Color.CornflowerBlue);

	spriteBatch.Begin(0, null, null, null, null, null, Camera.Current.TransformationMatrix);
	...
}

5. Camera implementation

And here is our implementation of the Camera:

public class Camera
{
	private float _offsetX;
	private Body _trackingBody;

	public Matrix TransformationMatrix { get; private set; }
	public float CenterPointTarget { get; set; }

	public static readonly Camera Current = new Camera();

	private Camera()
	{
	}

	public void Update(GameTime gameTime)
	{
		if (_trackingBody != null)
		{
			// if tracking body is not located in the center of the view (half screen width + current offset)
			if (_trackingBody.Position.X * Constants.Scale != Constants.HalfScreenWidth + _offsetX)
			{
				// move camera's offset so tracking body is dead center again
				_offsetX = Clamp(_trackingBody.Position.X * Constants.Scale - Constants.HalfScreenWidth);
			}
		}

		TransformationMatrix = Matrix.CreateTranslation(-_offsetX, 0f, 0f);
	}

	private float Clamp(float value)
	{
		// Camera's offset is not allowed to move beyond the centerpoint of our target
		return MathHelper.Clamp(value, 0, CenterPointTarget - Constants.HalfScreenWidth);
	}

	public void StartTracking(Body body)
	{
		_trackingBody = body;
	}

	public void StopTracking()
	{
		_trackingBody = null;
	}

	/// <summary>
	/// Converts a location on the screen to a location in our world, taking care of the current camera settings and physics scale.
	/// </summary>
	/// <param name="touchPosition"></param>
	/// <returns></returns>
	public Vector2 ScreenToSimulation(Vector2 touchPosition)
	{
		return Vector2.Transform(touchPosition, Matrix.Invert(TransformationMatrix)) / Constants.Scale;
	}
}

And here’s the glorious result:

Here’s my twitter: http://www.twitter.com/jodegreef

Follow

Get every new post delivered to your Inbox.