Physics Engine Woes Part 4
Following the last post we posted the following debugview code
172 //If you want to see debug info on top then place this function before other spritebatch calls
173 debugView.RenderDebugData(ref proj, ref view);
This used an identity matrix for the viewport and a projection matrix that looked like so
147 Matrix proj = Matrix.CreateOrthographic(50 * AspectRatio, 50, 0, 1);
This was all well and good except for... IT WASN'T MATCHING UP!!!
Alot of foul language was heard from Corndog as he tried desperately to match them to no avail. However having taken a few graphics classes I had a better idea of what I was looking at and dug in. The following answer is adapted from some kind soul's code posted on a forum. I cannot find the forum entry again and therefore I am failing to give proper credit. The code was adapted to suit our specific program.
141 Viewport Vport = GraphicsDevice.Viewport;
142 GraphicsDevice.Clear(Color.CornflowerBlue);
143
144 //Creates screen projection for debug mode that sets the height of the physics world to variable
145 // ScreenHeightInPhysicsWorld.
146 // This height is in meters
147 Matrix proj = Matrix.CreateOrthographic(ScreenHeightInPhysicsWorld * graphics.GraphicsDevice.Viewport.AspectRatio, ScreenHeightInPhysicsWorld, 0, 1);
148
149 // Viewport should either be identity matrix or camera transformation
150 Matrix view = _camera.get_transformation(GraphicsDevice);
151
152 // This transform matrix is passed into spritebatch to convert pixel sizes into meters
153 // This means physics world coordinates are the same as spritebatch coordinates
154 // Use locations and sizes of physics bodies for coordinates
155 Matrix transform = view * Matrix.CreateScale(Vport.Height / ScreenHeightInPhysicsWorld)
156 * Matrix.CreateScale(1,-1,1)
157 * Matrix.CreateTranslation(Vport.Width * .5f, Vport.Height * .5f, 0f);
158
159 //Be sure to pass in transform into any spritebatch you want to conform to the physics world!
160 //
161 // One other note! JigLibX which farseer is based off of uses a reversed Y scale. This means you need
162 // to reverse any textures you use or they will be culled by spritebatch! It might be possible to fix
163 // this by changing the scale matrix above, but edit at your own risk.
164 spriteBatch.Begin(SpriteBlendMode.AlphaBlend,SpriteSortMode.BackToFront,SaveStateMode.SaveState,transform);
165
166
167 //map.Draw(spriteBatch, graphics);
168 player.Draw(spriteBatch, graphics);
169
170
171
172 //If you want to see debug info on top then place this function before other spritebatch calls
173 debugView.RenderDebugData(ref proj, ref view);
The Code above contains many comments which should help explain it, but for simplicity I will break it down farther. The view variable represents a viewport matrix. This will be your camera transform. We are using a camera2D class which provides the transform.
Next the proj variable represents a projection matrix. Since we are making a 2D game our projection will be orthographic. The purpose of an orthographic matrix is to make a scene fit on any screen by scaling and transforming the image to fit the current resolution. Our orthographic projection's goal is to make a standard height and width for the physics coordinates and spritebatch coordinates. Since XNA's graphicDevice stores the aspect ratio, or ratio of width to height, we will use this to set standard sizes for our world.
You might have noticed references to a variable called screenHeightInPhysicsWorld. Farseer uses meters to measure coordinates and sizes of its fixtures, so we want to use meters as well. This variable is how tall the portion of the world viewable on the screen is in meters. In this case it is 50. However feel free to change this to whatever you want.
Now our screen size is standardized and our camera is set up... but things still don't match up!! Well that is where the transform matrix comes in. Spritebatch can accept a matrix called a transform. We want to pass it a transform that will convert pixels to meters. This is much more complex. In fact it means little to anyone who doesn't have a background in graphics. In short this matrix converts pixel coordinates used in your spritebatch calls to meter coordinates used by farseer.
One final note, the transform flips coordinates to match Farseer's reversed Y scale. This means textures will by default be applied upside down and culled by the graphicsDevice. You need to flip textures on the Y axis in order to keep them visible. This is accomplished by something similar to the following.
175 Vector2 scale = new Vector2((circleFixture.Shape.Radius * 2) / (squareTexture.Width), -(circleFixture.Shape.Radius * 2) / (squareTexture.Width));
This scale vector is passed into spritebatch when drawing a texture. For spritebatch position information use the fixture position and spritebatch will draw the texture exactly where the physics object is.
Alright You should now have debug mode matching up with your spritebatch calls now. Have fun with physics!


