1
\$\begingroup\$

So I made a scaling system for different resolutions, and I got very weird results.

I have an old "scaling" system where you can choose which scaleSize you want (for testing with the old 800 x 450 resolution) and you can choose between 1 and 2. I made a new scaling system after this that makes a scale with a matrix.

(old scaling system with scaleSize is just for making the textures/collision boxes bigger/smaller, not resolution dependent)

int currentWidth;
int currentHeight;
int preferredWidth;
int preferredHeight;
float scaleWidth;
float scaleHeight;

public Matrix scale;
GameWindow Window;
ContentManager Content;
GraphicsDeviceManager Graphics;
GraphicsDevice GraphicsDevice;

public ScreenSize(GraphicsDevice graphicsDevice, GraphicsDeviceManager graphics, ContentManager content, GameWindow window)
{
    Window = window;
    Content = content;
    Graphics = graphics;
    GraphicsDevice = graphicsDevice;
    LoadContent();
}

public void LoadContent()
{
    Graphics.PreferredBackBufferWidth = 1920;
    Graphics.PreferredBackBufferHeight = 1080;

    currentWidth = GraphicsDevice.Viewport.Width;
    currentHeight = GraphicsDevice.Viewport.Height;

    preferredWidth = 800;
    preferredHeight = 450;

    scaleWidth = currentWidth / preferredWidth;
    scaleHeight = currentHeight / preferredHeight;

    scale = Matrix.CreateScale(scaleWidth, scaleHeight, 1.0f);

    this.Graphics.IsFullScreen = false;

    Graphics.ApplyChanges();
}

I add the scale to my spriteBatch.Begin() and the textures are scaled. It works fine when the scaleSize is 2 but not when the scaleSize is 1.

The ScaleSize scales the collision box and the textures (not with the resolution, but just when you need (for example) a texture that's 2x bigger, then you use the scaleSize of 2. See an example below.

public virtual void Draw(GameTime gameTime, SpriteBatch spriteBatch, Color color, float scaleSize)
{
    spriteBatch.Draw(textureImage, position, new Rectangle(
    (currentFrame.X * frameSize.X),
    (currentFrame.Y * frameSize.Y),
    frameSize.X, frameSize.Y),
    color, 0, Vector2.Zero, scaleSize, SpriteEffects.None, depth);
}

But the problem is that, when I use the scaleSize of 1, then the pixels in-game are not the same size (for resolutions that are not the default resolution (800 x 450)). But they are when scaleSize is 2. In my first code block I use the resolution 1920 x 1080. When the scaleSize is 2, then it looks good. When the scaleSize is 1, then it looks bad (in fullscreen it looks good for some reason).

example: (left is good, right is wrong)

enter image description here enter image description here

I ask this question because I want the textures in-game smaller. (scaleSize = 1)

EDIT: scaleSize 2 doesn't look good on some resolutions (like 1440 x 900). Do I need another preferredWidth and preferredHeight?

example: enter image description here

summary: Everything looks/works fine with scaleSize 2, but doesn't with 1. -> edit: scaling doesn't work well.

\$\endgroup\$
0
\$\begingroup\$

The effect you encounter is probably due to rounding errors in the scaling. Note that the rectangle is all integers while the vectors and matrices are in floats.

Instead of scaling each Draw call, you can provide a transformation matrix in the Spritebatch.Begin() (see MSDN spritebatch).

Like so:

float _scaleSize = 0.5f; //scale to half size.
Matrix _scaleMatrix = Matrix.CreateScale(
               _scaleSize,
               _scaleSize,
                       1f);

spriteBatch.Begin(SpriteSortMode.Immediate,
                  BlendState.AlphaBlend,
                  null,
                  null,
                  null,
                  null, 
                  _scaleMatrix);

From that point the sprites drawn in the batch will use the scale matrix (so for both positions and size parameters).

Now you have two dimensions; the "real" screen and your "virtual screen". Suppose the user has a screen of 1920x1080 (backbuffersize) while your game is made for 1280x768 (virtualsize).

So you can calculate the scale factor using these resolution to upscale your game graphics. However the aspect ratios are different so you may need to adjust your viewport so it centers (pillarbox or letterbox). To achieve this you need to adjust your viewport.

You can control the viewport (basically where the 0,0 coordinate is and the width and height of the drawable area) using GraphicsDevice.Viewport. This code puts the drawable screen in the center of the screen:

        Viewport viewport = new Viewport();

        viewport.X = (GraphicsDevice.PreferredBackBufferWidth / 2) - (virtualwidth / 2);
        viewport.Y = (GraphicsDevice.PreferredBackBufferHeight / 2) - (virtualheight / 2);
        viewport.Width = width;
        viewport.Height = height;
        viewport.MinDepth = 0;
        viewport.MaxDepth = 1;

        GraphicsDevice.Viewport = viewport;

You could start each draw call by clearing the screen black for borders, set the virtual viewport and start drawing using the matrix in your spriteBatch.Begin() for a resolution independant game. All scaling is done using the matrix so no code should concern what resolution the client is viewing.

Bonus, if you need the mousepointer, use the inverted scale matrix like so:

Matrix invertScaleMatrix = Matrix.Invert(_scaleMatrix);
Vector2 mouseposition = Vector2.Transform(screenposition, invertScaleMatrix);

Another thing I've noticed when for example drawing a tilemap is that sometimes scaled sprites rendered have a rounding error on the edges of each tile. To fix this set the samplerstate to SamplerState.PointClamp in the spriteBatch.Begin(); statement.

For a complete sample with code, David Amador wrote code that employs these concepts here: XNA 2D independent resolution rendering

\$\endgroup\$
  • \$\begingroup\$ Thanks for the answer. I don't use scaling anymore though, I just enlarge the game window since it's easier if I want pixel perfect sprites for all my resolutions. I used a scaling matrix with ((int)x, (int)y, z) though.... and yes, I did provide the transformation matrix in my spriteBatch.Begin() code (when I wrote the question I already used the transformation matrix like in your code example). But thanks for the info/sources on aspect ratios and black borders. I will accept this answer. NOTE: scaleSize wasn't intended for creating a matrix, just for testing (with sprites). \$\endgroup\$ – Jelle Jan 4 '16 at 19:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.