I created a little Windows Phone 8 game that supports multiple resolutions. If the resolution of the device is less than 720p, then the sprites and their corresponding Farseer bodies need to be scaled down. If I run my project with the 720p emulator, then everything looks fine. But if I choose an emulator with a lower resolution(for example WVGA emulator), then everything looks weird.
In this picture, I use the 720p emulator: http://s1.directupload.net/images/140520/gnpwvjsw.jpg
In this picture, I use the WVGA emulator: http://s14.directupload.net/images/140520/hxhicnph.jpg
If I use the WVGA emulator, then the Farseer collision shapes are drawn on another position than their corresponding sprites. In addition, a part of the red ball sprite is drawn in the blue rectangle sprite, but their corresponding shapes are drawn differently(the ball shape isn't in the rectangle shape).
How can I scale the Farseer bodies so that everything works correctly?
Here is my entire code:
Game1 class:
public class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
bool Draw_debugView = true;
World world;
Body Rectangle, Ball;
Texture2D RectangleSprite, BallSprite;
public Camera camera;
public const int VirtualScreenWidth = 1280;
public const int VirtualScreenHeight = 720;
Vector2 vp, gameWorldSize = new Vector2(1280, 720);
float ScaleX, ScaleY, Scale;
DebugViewXNA physicsDebug;
private Matrix _view;
private Vector2 _cameraPosition;
private Vector2 _screenCenter;
private static float _displayUnitsToSimUnitsRatio = 100f;
public static Vector2 ToDisplayUnits(Vector2 simUnits)
{
return simUnits * _displayUnitsToSimUnitsRatio;
}
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.IsFullScreen = true;
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
BallSprite = Content.Load<Texture2D>("ball");
RectangleSprite = Content.Load<Texture2D>("tile");
vp = new Vector2(GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height);
ScaleX = vp.X / gameWorldSize.X;
ScaleY = vp.Y / gameWorldSize.Y;
Scale = Math.Min(ScaleX, ScaleY);
camera = new Camera(new Vector2(640,360), Scale, vp);
_view = Matrix.Identity;
_cameraPosition = Vector2.Zero;
_screenCenter = new Vector2(graphics.GraphicsDevice.Viewport.Width / 2f,
graphics.GraphicsDevice.Viewport.Height / 2f);
if (world == null)
{
world = new World(new Vector2(0, 1));
}
else
{
world.Clear();
}
physicsDebug = new DebugViewXNA(world);
physicsDebug.DefaultShapeColor = Color.White;
physicsDebug.SleepingShapeColor = Color.LightGray;
physicsDebug.LoadContent(GraphicsDevice, Content);
Ball = BodyFactory.CreateCircle(world, 0.25f*ScaleX, 1.0f);
Ball.BodyType = BodyType.Dynamic;
Ball.Position = new Vector2(6.4f, 0.6f);
Ball.LinearVelocity = new Vector2(0, 2.0f);
Rectangle = BodyFactory.CreateRectangle(world, 1.50f*ScaleX, 0.50f*ScaleY, 1.0f);
Rectangle.BodyType = BodyType.Static;
Rectangle.Position = new Vector2(6.4f, 3.6f);
Rectangle.Rotation = 0f;
Rectangle.Restitution = 0.5f;
Rectangle.CollisionCategories = Category.Cat5;
}
protected override void Update(GameTime gameTime)
{
float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
world.Step(Math.Min(elapsed, (1f / 60f)));
ScaleX = vp.X / gameWorldSize.X;
ScaleY = vp.Y / gameWorldSize.Y;
Scale = Math.Min(ScaleX, ScaleY);
//The ball should always be the center of the camera
camera.Update(gameTime, new Vector2(Ball.Position.X*100f, Ball.Position.Y*100f), Scale, vp);
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend, null, null, null, null, camera.GetMatrix());
spriteBatch.Draw(RectangleSprite, ConvertUnits.ToDisplayUnits(Rectangle.Position),
null,
Color.White, Rectangle.Rotation, new Vector2(RectangleSprite.Width / 2.0f, RectangleSprite.Height / 2.0f), 1f,
SpriteEffects.None, 0f);
spriteBatch.Draw(BallSprite, ConvertUnits.ToDisplayUnits(Ball.Position),
null,
Color.White, Ball.Rotation, new Vector2(BallSprite.Width / 2.0f, BallSprite.Height / 2.0f), 1f,
SpriteEffects.None, 0f);
spriteBatch.End();
if (Draw_debugView == true)
{
// calculate the projection and view adjustments for the debug view
Matrix projection = Matrix.CreateOrthographicOffCenter(0f, graphics.GraphicsDevice.Viewport.Width / _displayUnitsToSimUnitsRatio,
graphics.GraphicsDevice.Viewport.Height / _displayUnitsToSimUnitsRatio, 0f, 0f,
1f);
Matrix view = Matrix.CreateTranslation(new Vector3((_cameraPosition / _displayUnitsToSimUnitsRatio) - (_screenCenter / _displayUnitsToSimUnitsRatio), 0f)) * Matrix.CreateTranslation(new Vector3((_screenCenter / _displayUnitsToSimUnitsRatio), 0f));
// draw the debug view
physicsDebug.RenderDebugData(ref projection, ref view);
}
base.Draw(gameTime);
}
}
Camera class:
public class Camera
{
private Vector2 BallPosition;
private Vector2 Viewport;
private float Scale;
public Camera(Vector2 ballposition, float scale, Vector2 viewport)
{
BallPosition = ballposition;
Scale = scale;
Viewport = viewport;
}
public void Update(GameTime gameTime, Vector2 ballposition, float scale, Vector2 viewport)
{
BallPosition = ballposition;
Scale = scale;
Viewport = viewport;
}
public Matrix GetMatrix()
{
return Matrix.CreateTranslation(new Vector3(-BallPosition.X, -BallPosition.Y, 0))
* Matrix.CreateScale(Scale, Scale, 1)
* Matrix.CreateTranslation(Viewport.X / 2, Viewport.Y / 2, 0);
}
}
UPDATE: I changed my code and tried it without scaling the sprites and Farseer objects. In addition, I changed my code in the Camera class to:
public Matrix GetMatrix()
{
return Matrix.CreateTranslation(new Vector3(-BallPosition.X, -BallPosition.Y, 0))
* Matrix.CreateTranslation(Viewport.X / 2, Viewport.Y / 2, 0);
}
But it's still not working. I get the following result if I don't use scaling: http://s7.directupload.net/images/140523/oumh3e7c.jpg
Why is the DebugView drawn on another position than the sprites? I found out that if I change something in this code block, then the DebugView is drawn on another position, but I don't know how to change this code block so that the DebugView is drawn on the correct position(on the same position as their corresponding sprites). I know that it's not working because I use a Camera class, but how should I change this code block so that it works with the Camera class?
if (Draw_debugView == true)
{
// calculate the projection and view adjustments for the debug view
Matrix projection = Matrix.CreateOrthographicOffCenter(0f, graphics.GraphicsDevice.Viewport.Width / _displayUnitsToSimUnitsRatio,
graphics.GraphicsDevice.Viewport.Height / _displayUnitsToSimUnitsRatio, 0f, 0f,
1f);
Matrix view = Matrix.CreateTranslation(new Vector3((_cameraPosition / _displayUnitsToSimUnitsRatio) - (_screenCenter / _displayUnitsToSimUnitsRatio), 0f)) * Matrix.CreateTranslation(new Vector3((_screenCenter / _displayUnitsToSimUnitsRatio), 0f));
// draw the debug view
physicsDebug.RenderDebugData(ref projection, ref view);
}