Apply BoxingViewportAdapter to the entire game

I am creating a pixel based game in a virtual resolution of 360x240, so when the player have a larger window, the resolution need be increased uniformly, so I chose the BoxingViewportAdapter, to keep the ratio correct, but I really don’t figured how to apply this to the entire game.

At the moment, I was able to set the virtual resolution to a ViewportAdapter and pass it through the camera, it works well, but only if I have a camera (only in the map screen) and it does not add the black bars. So, how can I apply the BoxingViewportAdapter to the entire game screen?

The camera has a method called GetViewMatrix() and this is intended to be used with the SpriteBatch like this:

_spriteBatch.Begin(transformMatrix: _camera.GetViewMatrix());
_spriteBatch.Draw(_sprite);
_spriteBatch.End();

Once you get game drawing correctly you’ll probably also want to handle mouse input. To get the correct mouse position in virtual resolution you’ll need the ScreenToWorld method.

var mouseState = Mouse.GetState();
var virtualMousePosition = _camera.ScreenToWorld(mouseState.X, mouseState.Y);

Looking at the samples repo might be helpful.

Yeah, that’s what I already made work, the camera is successfully showing what I want. Now I want know how to use the BoxingViewportAdapter as “default viewport” for the entire game, without the use of a camera, if possible, because I only want to use the camera on the map screen (where it is suppose to be used, is not?).

Ah, I think I understand what you mean now.

The viewport adapters have a method called GetScaleMatrix() that you can use much like the GetViewMatrix on the camera. This is often used when you want to draw a map controlled by the camera and a user interface (UI) over the top.

_spriteBatch.Begin(transformMatrix: _camera.GetViewMatrix());
// draw map
_spriteBatch.End();

_spriteBatch.Begin(transformMatrix: _viewportAdapter.GetScaleMatrix());
// draw UI
_spriteBatch.End();

Obviously, you can create different combinations for different effects. For example, you might want to draw a background layer differently again.

This worked for the normal screens, but in the map screen it didn’t. I can’t make the camera and the viewport work together. Is there a way to render the camera inside the viewport, with the black bars? When I try to draw the map, it fills all the screen, even if I pass the BoxingViewportAdapter as camera viewport.

I’m really not sure what you mean. Can you post a screenshot and perhaps some code?

Sorry, english is not my main language, so sometimes is hard to understand what I mean. See this GIF I recorded, its a sample screen that shows a image, its have the BoxingViewportAdapter, as I desire: http://i.imgur.com/905lbuI.gif

I want to the same with the screen that shows the map, but the map does not behave as it should, the black bars are not shown. See: http://i.imgur.com/qHUiinL.gif

Its the Draw() method from SceneTitle:

spriteBatch.Begin(transformMatrix: viewportAdapter.GetScaleMatrix());
spriteBatch.Draw(_backgroundImage);
spriteBatch.End();

But to the SceneMap, I don’t know how to draw the things to get the BoxingViewportAdapter effect, because it has the camera and is confounding me, the camera have his own transformMatrix, so how pass the transformMatrix of BoxingViewportAdapter?

That explains it better.

What you should be able to do is this:

        var viewportAdapter = new BoxingViewportAdapter(GraphicsDevice, 800, 480);
        _camera = new Camera2D(viewportAdapter);

But that appears to not be working. It looks like you’ve found a bug. I’ve raised an issue to get this fixed.

@RafaelAlmeida

I’ve fixed the bug in the code. It’ll be available in the next release.

The problem turned out to be fairly simple, but no obvious. The map is being rendered to a RenderTarget2D to avoid tearing and gaps between the tiles. What didn’t occur to me is that changing the render target destroys the size of the viewport and the viewport adapter doesn’t know how to scale properly.

Anyway, here’s how the code turned out:

    public void Draw(Camera2D camera, bool useMapBackgroundColor = false)
    {
        // it's important to get the camera state before setting the render target
        // because the render target changes the size of the viewport
        var boundingRectangle = camera.GetBoundingRectangle();
        var viewMatrix = camera.GetViewMatrix();
        var viewport = _graphicsDevice.Viewport;

        _graphicsDevice.SetRenderTarget(_renderTarget); 

        if(useMapBackgroundColor && BackgroundColor.HasValue)
            _graphicsDevice.Clear(BackgroundColor.Value);

        foreach (var layer in _layers)
            layer.Draw(boundingRectangle);

        _graphicsDevice.SetRenderTarget(null);

        _graphicsDevice.Viewport = viewport;
        _spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied, SamplerState.PointClamp, transformMatrix: viewMatrix);
        _spriteBatch.Draw(_renderTarget, Vector2.Zero, Color.White);
        _spriteBatch.End();
    }

As you can see, I’m now preserving the viewport during the render target operation and restoring it before the final render with the camera. This solves the issue :relieved:

Thank you so much! I found another bug. If you resize the window to a bigger size, the tiles are not drawn correctly: http://i.imgur.com/2uWX1fW.gif

I solved the problem by myself and I created a pull request. I don’t know if it is the better solution, but works very well: http://i.imgur.com/NKVzQaw.gif. I compared with the master branch because its a fix and not a new version feature. Am I correct?

@RafaelAlmeida btw… how do you create those animated gifs? I’ve seen them around everywhere but never looked into it.

It is the GifCam, awesome program :slightly_smiling:
I started using the Gitter chat, hope I can switch ideas with other programmers.