Thursday, August 27, 2015

xna 4.0 - XNA Platformer Sample: Supporting multiple resolutions?



I have been looking at the XNA Platformer Sample and noticed that, when you change the resolution, the game world still renders at 800x480 and not the new width and height of the display.


Can anyone suggest a simple way to make the Platformer sample support multiple resolutions?



Answer



First of all, to test this answer on Windows, use a fresh copy of the game and add the following line to the constructor of PlatformerGame.


Window.AllowUserResizing = true;

This will let you resize the window and see the changes in action.




The easiest way is to simply scale the game so that it fits on screen. You need to pass a scaling matrix to SpriteBatch.Begin.


Here I have provided code to calculate a matrix that will "fit" the content into the available space and centre it:



var gameWorldSize = new Vector2(800, 480);
var vp = GraphicsDevice.Viewport;

float scaleX = vp.Width / gameWorldSize.X;
float scaleY = vp.Height / gameWorldSize.Y;
float scale = Math.Min(scaleX, scaleY);

float translateX = (vp.Width - (gameWorldSize.X * scale)) / 2f;
float translateY = (vp.Height - (gameWorldSize.Y * scale)) / 2f;


Matrix camera = Matrix.CreateScale(scale, scale, 1)
* Matrix.CreateTranslation(translateX, translateY, 0);

spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, camera);

(Note the way that I scale by 1 and translate by 0 on the Z axis. Not modifying the Z axis of sprites is important when using SpriteBatch.)


Note that, when you scale the game like this, any graphics that are "pixel perfect" will no longer be perfectly aligned to any pixels. One way to solve this is to have multiple versions of your assets at different, fixed scales - draw them at fixed world sizes (not by their pixel size) and then only allow scale in the above code to be a set of fixed values matching those asset sizes.




The other way is to implement a scrolling camera (focused on the player). This is good because it allows the world to be any size, independent of the screen size.


var vp = GraphicsDevice.Viewport;


float translateX = vp.Width / 2 - level.Player.Position.X;
float translateY = vp.Height / 2 - level.Player.Position.Y;

Matrix camera = Matrix.CreateTranslation(translateX, translateY, 0);
spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, camera);

A good camera will "lag" a little bit behind the player in order to feel more natural. You can find lots of tutorials on making a good platformer camera online.


Continuing on my notes about keeping assets fixed to the pixel grid: This method allows you to do this with a single set of assets. Just make sure your camera position is rounded to the nearest pixel if this is what you want to do.





Note that the HUD is - in the original sample - sharing the same sprite batch as the level display. This will cause HUD elements to appear in the wrong place. You should separate them out so that the HUD is not affected by the camera, like so:


spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, camera);
level.Draw(gameTime, spriteBatch);
spriteBatch.End();

spriteBatch.Begin();
DrawHud();
spriteBatch.End();




You can, of course, combine these approaches and come up with your own variations (like the fixed-scales-multiple-graphics-sets variation I suggested earlier). What you do will depend on the game you are making.


Obviously both methods require you to make the game world larger so that it looks nice when you can see beyond its edge. I'll leave this as an exercise.


There are a few other good questions on handling 2D game scaling on this site. Here is one such question.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...