3

I decided to move my game from windowed to fullscreen mode and that's the first problem I face. I'm looking for a way of resizing all of my sprites based on screen resolution. My background is now in the (0, 0) coordinates, but I need to have it and all sprites to scale with some kind of fixed aspect ratio (16:9 preferred). And resize them to that portion that the background is stretched to fill the screen. And not more, not less.

I've looked into some online tutorials but I really couldn't understand the concept they used. Can you explain how you would to that? I read using a RenderTarget2D and passing it to a spriteBatch.Begin() call, has some kind of effect, but there's got to be more code.

I'm not looking to supporting resolution change option, but adapting the sprites to the current resolution.

Mephy
  • 2,978
  • 3
  • 25
  • 31
Johny P.
  • 648
  • 1
  • 9
  • 31
  • You can use the rectangle in `SpriteBatch.Draw(Texture2D, Rectangle, Color)` (or overloads) to resize any texture to any size. The rest is math. – Mephy Aug 30 '15 at 22:17
  • Can you provide an example? I assume you mean giving overloads of `Texture2D.Width`/`.Height` multiplied by something, and if so, by what? – Johny P. Aug 30 '15 at 22:24

2 Answers2

10

It sounds like you're talking about resolution independence.

The general idea is to make your game using a virtual resolution and scale it up or down to fit the actual resolution of the screen.

var scaleX = (float)ActualWidth / VirtualWidth;
var scaleY = (float)ActualHeight / VirtualHeight;
var matrix = Matrix.CreateScale(scaleX, scaleY, 1.0f);

_spriteBatch.Begin(transformMatrix: matrix);

For example, if your virtual resolution was 800x480 you would simply render all your sprites relative to that. Then before rendering the sprite batch, create a transformation matrix to pass into the Begin call.

The other thing you should know is that you'll need to scale the mouse / touch input coordinates in reverse to deal with them in the virtual resolution. In the Update method you can scale the mouse position in reverse like this:

var mouseState = Mouse.GetState(); // you're probably already doing this
var mousePosition = new Vector2(mouseState.X, mouseState.Y);
var scaledMousePosition = Vector2.Transform(mousePosition, Matrix.Invert(matrix));

Then you can use the scaled value in all the places you're currently using mouseState.X and mouseState.Y.

It gets more complicated if want to implement letterboxing or pillarboxing. Take a look at the Viewport Adapters in MonoGame.Extended if you want to know how that works.

craftworkgames
  • 9,437
  • 4
  • 41
  • 52
  • So `ActualWidth` and `ActualHeight` is `GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width` and `GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height` right? And then I set `VirtualWidth` and `VirtualHeight` to be `1366x768`. And I assume that `mousePosition` is a `MouseState` variable? – Johny P. Aug 31 '15 at 13:12
  • I tend to use `GraphicsDevice.Viewport.Width` and `GraphicsDevice.Viewport.Height`. Did you read the [blog post](http://dylanwilson.net/resolution-independence-in-monogame-with-viewport-adapters)? And yes, the virtual resolution is whatever your code currently expects. If you've already been positioning sprites in a 1366x768 window make the virtual resolution that. – craftworkgames Aug 31 '15 at 13:18
  • I'm reading it at the moment. Also if I put the above code in `Draw()` I can't use the temporary variable `matrix` with the `scaledMousePosition` if that's for `Update()` right? – Johny P. Aug 31 '15 at 13:24
  • Right. The code I posted is just an example to give you the idea. You'll need to make the matrix a member variable or move it into a class or something. – craftworkgames Aug 31 '15 at 13:27
  • For some strange reason using `GraphicsDevice.Viewport.Width` and `GraphicsDevice.Viewport.Height` threw an exception and I had to use `GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width` and `.Height`. However this simple implementation works perfectly. Also the code in that blog is only Monogame.Extended specific? I think I'm going to try this lib for the more complex implementation described there. – Johny P. Aug 31 '15 at 13:54
  • Glad you got it working. The blog post is about how it's implemented in MonoGame.Extended. The library is still fairly new but it should be more complete than trying to do it yourself. If you have any issues post them on the github issues page. – craftworkgames Aug 31 '15 at 13:59
  • @craftworkgames-How did you use this scaledMousePosition ,I mean in which method(update or draw).How did you relate with touch inputs. – uncle_scrooge Oct 01 '15 at 07:10
  • @uncle_scrooge I've updated the answer with a little more detail. I can't remember the exact code for doing it with touch input but the concept is the same as with the mouse. – craftworkgames Oct 01 '15 at 08:04
  • @craftworkgames Your resolution independence link returns a 404 now. Is there an updated link? – Matthew Jan 13 '21 at 15:40
  • Link is dead but I found an archived copy: https://web.archive.org/web/20161017225653/http://www.dylanwilson.net/resolution-independence-in-monogame-with-viewport-adapters – MetaGuru May 20 '22 at 01:29
  • Here's another article that is still posted and might even be better: http://blog.roboblob.com/2013/07/27/solving-resolution-independent-rendering-and-2d-camera-using-monogame/ – MetaGuru May 20 '22 at 01:43
1

You have a texture with size (W, H), to be put in the position (X, Y), according to a scale (sW, sH). Initially, the scale was (1, 1), so the sprite would be positioned in the rectangle (X, Y, W, H).

Now, let's say the initial resolution was 800x600, but you now want a resolution of 1440x900. If 800 -> sW = 1, 1440 -> sW = 1440/800 = 1.8. Similar, our new sH is 1.5.

What this is saying is: if something was supposed to be on the X-coordinate 500 on the initial resolution, it is now on 500*1.8 = 900 X position on the new resolution. This is clear for the edge: if something was on X=800 previously, it is now on 800*1.8 = 1440, still on the edge of the screen!

All said and done, we simply have to multiply. Going back to the first paragraph, we can say that a rectangle (X, Y, W, H) can be rescaled by a scale (sW, sH) to (X * sW, Y * sH, W * sW, H * sH).

This is of course calculated by assuming the original resolution is scaled by (1, 1), don't forget this!

Mephy
  • 2,978
  • 3
  • 25
  • 31
  • So I'm going to code the game resolution like `1366 / 800 = 1.7075` and `768 / 600 = 1.28`. So now I have `new Rectangle (0 * 1.7075, 0 * 1.28, 800 * 1.7075, 600 * 1.28)` for my background, of course the `(0, 0)` position multiplied will return 0, so I could just use `0` there. And what about input? I can't hardcode my `Point` variables where I check for mouse clicking if it's 500 pixels away, right? – Johny P. Aug 30 '15 at 23:45
  • @JohnyP. the input is already in the new coordinate system, and should be checked against the rectangle transformed to the new coordinate system. I recommend statically changing the sizes (resizing textures in Photoshop or whatever), instead of at run-time. – Mephy Aug 30 '15 at 23:59