Note: 22/04/2013 – Due to popular request I made an article about achieving this effect in OpenGL
Independent Resolution Rendering?? What’s this all about?
Basically a way of not caring what you resolution is. Ever had Gui elements misplaced because you changed the resolution? Or getting out of the screen?
If you are doing a game on Xna just for Xbox360 you can basically use a 1280×720 base resolution and the Xbox will scale the game for you making the proper Letterbox.
But what about on Windows? Or if you use a different resolution on the Xbox? You have to manage that yourself.
I’ve made a small example on how to achieve this.
By the means of a class that I called Resolution ( just change it for whatever you feel it’s better) you can set both Virtual and Actual Resolution. Virtual or Base resolution is what I call the actual resolution in which I’ll work everything, and you stick with it, for both moving sprites, calculations etc. It’s your working Resolution. The other one is the resolution at which the game is rendering, which we want to be independent of the game. So the class will scale your Virtual to the Actual, making a LetterBox or a PillarBox to match them.
Resolution.SetVirtualResolution(1280, 720); Resolution.SetResolution(800, 600, false); |
This is telling that you are working as if the game is on a 1280×720 but it is rendering on a window of 800×600.
The third flag is fullscreen or windowed.
On the main Draw Pump just add the following so that the class can make the appropriate viewport.
Resolution.BeginDraw(); |
Next whenever you make a SpriteBatch.Begin() you have to pass the Resolution Scale Matrix as the forth parameter:
spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.SaveState, Resolution.getTransformationMatrix()); |
Now you can draw everything on the same way you would normally do
spriteBatch.Draw(_texture, Vector2.Zero, Color.White); |
This said you can now change to different Resolutions keeping the same base and the same code and this will scale everything for you, neat right?
Here are a few screenshots of an application (using Machinarium) with a Virtual resolution of 1024×768 on different real resolutions
An another example (using Braid) with a Virtual Resolution of 1280×720
You can download the project with the source here. Once again if you run into any mistakes (most probably 😛 ) let me know.
cool:)
Thinking in integrate it with my engine too:)
Thanks for sharing!
Nice one 😉
My engine is working with a fixed resolution only, so this will help me a lot 🙂
This is amazingly sweet!!
This should help a ton for my current WP7 project which I am currently developing at 1280 X 720.
I was dreading resizing all of my textures for 800 X 480 landscape WP7 mode.
Thanks for posting this.
Allan Chaney
Fantastic! Just what I was looking for 😀
Thanks for sharing this!
But one off Topic question: How have you bought Braid/Machinarium in a XNA Window? Are these Game with XNA?
Hi Niklas,
No not really, it’s a screenshot of the games just to show what would happen to your game. I’m using a Texture2D and it’s automatically scaled to the proper aspect ratio.
Ah.. Simple but effective 😉
You know what.. Can you please moderate my previous post and delete it? I made a stupid mistake. I switched SetVirtualResolution and SetResolution by accident. Your class works fine. And it’s great! No modifications needed nothing.
Done. Thanks for you feedback 🙂
Hello,
Nice article, but it left me wondering on two points, maybe you could clear things up.
1) I was wondering if by using this approach, wouldn’t it also be required to transform whatever mouse coordinates you receive from Mouse.GetState() with the inverse of the resolution matrix, in order to get it back into “game space” and be able to do picking or anything you need?
2) And what if your SpriteBatch is already using a transformation matrix (such as for camera transformations). Would multiplying them together be enough (and is the order of multiplication relevant here?)
Thanks,
David Gouveia
1) Yes you have to translate whatever you receive from Mouse.GetState(). In fact you should abstract Mouse with your own class. So when you receive Mouse pos from your class it’s automatically translated to the current resolution and it’s transparent for your game. It’s wise to put wrappers around most XNA stuff to have more control and abstraction for stuff like this,
2) Yes you have to multiply them and pass it to SpriteBatch. Order is important. If I’m not mistaken you should do CameraMatrix * IndependetResolutionMatrix. But try it both ways if it fails
Cheers
Hello David!
I just added independent resolution rendering to my engine today, and would like to share my experiences to make life easier for anyone that reads this and is faced with the same problems.
1) Converting mouse coordinates from “window space” (what Mouse.GetState gives you) to “viewport space” (what you’d have without independent resolution implemented) needs to take into account both the scaling matrix and the translation (that creates the letterbox or pillarbox).
After some tweaking, what finally worked for me was this:
Vector2.Transform(_mousePosition – new Vector2(Resolution.ViewportX, Resolution.ViewportY), Matrix.Invert(Resolution.Matrix));
Where Resolution.ViewportX and Resolution.ViewportY are the X and Y values calculated for the inner viewport (I cached them and added acessors).
AND when drawing the mouse cursor, I also scale it with the independent resolution matrix so that it grows or shrinks correctly.
This way, when the mouse is on the borders of the inner viewport, you get the coordinates specified for your virtual resolution as intended, properly scaled and translated.
2) You were right about the order. So if your game implements a 2D camera by passing a transformation matrix to SpriteBatch and you want to mix it with independent resolution rendering, simply multiply both matrixes together, like so: CameraMatrix * IndependentResolutionMatrix.
David Gouveia
This is so great. Thanks man. Especially for sharing the complete class 🙂
Best Wishes from Germany
Daniel
Hey mate, I thought I should let you know that this sample needs a little update to work properly in XNA4.
That’s because starting from XNA4 doing a GraphicsDevice.Clear will always clear the entire backbuffer no matter what Viewport you’ve set!
This implies two things.
1) You no longer need to reset to the full Viewport before clearing to black. You just call GraphicsDevice.Clear(). This also means that it’s no longer necessary to switch between the full and virtual Viewport constantly – just keep it always in the virtual Viewport.
2) You need to use SpriteBatch instead to fill *only* the virtual Viewport with the desired color. Simple 1×1 texture drawn to fill the Viewport’s rectangle should do.
You are right. Sh** that’s because they switched to a dx10 arquitecture type. DirectX 10 clear doesn’t clear the active viewport. The Clear is no longer bind-ed to the Viewport.
Even so we usually the draw part should still work and just of the viewport so it’s just 1 unnecessary viewport clear.
Damn I really hate the speed how they change stuff like this.
Yes it works, but since the second clear (after setting the virtual viewport again) also fills the entire screen, you lose the pillarbox/letterbox effect.
And unfortunately filling with a quad is somewhat slower than the original Clear :\
Making it always black should work for the pillarbox/letterbox effect. I have Vizati using XNA4 on WP7 and Xbox and I still get that effect.
I didn’t explain myself properly. What I mean is that even though the pillarbox will still be there and work properly, if you fill everything with black you won’t be able to “see them”.
In a normal game you’re usually filling the entire viewport with your own graphics, so this is not a problem.
But if you want the XNA sample appearance of having a CornflowerBlue background, then obviously clearing it to black won’t work. In that case, if you want to retain a certain background color, you have to replace the second Clear it with a SpriteBatch.Draw call to actually see it.
In your 3.1 version of this article, you’d clear to Black and then clear to CornflowerBlue, so even if nothing was drawn you’d see the difference between viewports. That won’t work now.
Any way to get this to work on 3D models as well?
When I go to fullscreen the models are always stretched but I have yet to find a way to use Resolution.GetMatrix to fix it.
Thank you so much for sharing ! Finally i solved my problem with resolution, really thanks !!! 😉
I need some help here.
The following code doesn’t work in XNA 4.0:
So I changed it to this:
But then I get this error:
“No overload for method ‘Begin’ takes 3 arguments”
Any help would be appreciated! Also, I’m still a bit new to XNA, so an explaination would be nice too. 🙂
In XNA 4 SpriteBatch::Begin takes more parameters
http://msdn.microsoft.com/en-us/library/ff433701.aspx
Try something like:
Wow, fast reply 😀
It worked! Thanks!
hello, thansks for your post , any advise for programming a penalty shootout soccer game??
I’ve been working on a project using the game state management sample code from xna website and have been trying to incorporate this into my game. Have got the sample working fine and i can change resolutions fine however when i implemented it into my project everything is drawn at the top of the screen. It all resizes properly but it’s not centred.
This is there i set the resolution in my game class
Have changes _Width and _Height in Resolutions to
And this is how i’m drawing my sprites
Any Help would be greatly appreciated.
Stephen,
Not sure if you’re still having issues with this but my guess would be that it is drawing the background image in the top left corner. This is because you are using the
method which draws the texture by mapping the default texture “origin” to the given vector. Unless otherwise specified in the Draw method, the default texture “origin” is at (0,0) in sprite coordinates which happens to be the very middle of the sprite and you are telling it to draw this at (0, 0) in screen coordinates which is the very top left corner of your screen.
You can fix this two ways.
The easiest would be to set your position to the middle of the viewport instead ( viewport width / 2, viewport height / 2).
Or you can use the following Draw method
to manipulate the origin position of your sprite.
Hi David, I’m a having a conflict using the last parameter of the Draw method (the matrix transformation). Normally I use the matrix to translate the camera across the level and I want to know if there’s a way to “mix” the two matrices.
This is my matrix transformation and my current Draw method:
Thanks in advance.
I think there may be an issue. Here is the code I have in your constructor for Game1
pre lang=”cpp”> Resolution.SetVirtualResolution(1024, 768);
Resolution.SetResolution(1280, 1024, true);
To test out your code i put this in the game1 update method:
When i use this, the image does not keep its aspect ratio, and stretches horizontally. I also noticed something odd in your RecreateScaleMatrix() function:
Shouldn’t height/vheight come into play somewhere?
Any help would be appreciated, your code has been invaluable. Thanks
Update: it only doesnt work right when in full screen. Otherwise it seems fine.
Nevermind, figured it out. I was just setting a full screen resolution that had a different aspect ratio than my monitor. Got ahead of myself and made a simple mistake.
I love you… seriously… thanks for this code, it works like a charm and my XBox to PC porting just became that much easier! I owe you big time.
Hi people!!
Can someone post the code to convert the mouse coordinates and touches too?
Thanks a lot
See if something like this works for you
Vector2.Transform(_mousePosition – new Vector2(Resolution.ViewportX, Resolution.ViewportY), Matrix.Invert(Resolution.Matrix))
I read that on another post but I don’t know how to calculate ViewportX, ViewportY and Matrix.
Thanks David
You can get your current Viewport from the GraphicsManager I think, sorry don’t really remember all the XNA functions. Matrix.Invert is a function you can use
First of all I would like to thank you for the sample code, it has been very helpful.
When I set the resolution to the max one my graphics cards supports and drag the game window then I get an error. The error is on the line of code where the Viewport is set. The error is: “The viewport is invalid. The viewport cannot be larger than or outside of the current render target bounds. The MinDepth and MaxDepth must be between 0 and 1.”
This is in XNA 4.0. Any idea what might be going wrong?
Cheers,
poohshoes
some additional info:
When the app breaks Viewport is set at 1920×1059 for some bizzare reason.
It is trying to set the Viewport to 1920×1080.
I have put a breakpoint in the app where the view is happily 1920×1080.
Excellent! Thanks for sharing this!
I know this is an old post, but using monogame this line gives me an error:
_Device.GraphicsDevice.Viewport = vp;
System.NullReferenceExeption: object reference not set to an instance of an object.
It is possible that MonoGame works different then the original XNA, try searching for the proper function to set a Viewport on MonoGame instead of using that code exactly
Hi David – awesome post! This information has helped me ship “Candy Kid” [http://bit.ly/1OngOgj] to numerous devices (iOS / Android) and all rendering great regardless of the target resolution – so major thanks for this code sample.
@epse – I also used MonoGame (v3.5) and all worked ok for me.
I use the default XNA code to construct the GraphicsDeviceManager, and pass this to initialize the _Device before invoke BeginDraw();
@Pablo – here is the psuedo code that I wrote to convert the mouse coordinates and touches too:
Vector2 touchPosition = null;
Vector2 viewport = “Resolution.Viewport”;
TouchLocation location = null;
TouchCollection touchCollection = TouchPanel.GetState();
if (touchCollection.Count > 0)
{
location = touchCollection[0];
TouchLocation touchLocation = (TouchLocation)location;
Vector2 deltaPosition = touchLocation.Position – viewport;
Matrix transformationMatrix = Matrix.CreateScale((float)viewport.Width / _VWidth, (float)viewport.Width / _VWidth, 1f);
Matrix invertTransformationMatrix = Matrix.Invert(transformationMatrix);
touchPosition = Vector2.Transform(deltaPosition, invertTransformationMatrix);
}
Awesome, congrats and good luck.
Thanks for sharing