Going fullscreen without stretching in an XNA game - c#

I've got a 2D game that I'm working on that is in 4:3 aspect ratio. When I switch it to fullscreen mode on my widescreen monitor it stretches. I tried using two viewports to give a black background to where the game shouldn't stretch to, but that left the game in the same size as before. I couldn't get it to fill the viewport that was supposed to hold the whole game.
How can I get it to go fullscreen without stretching and without me needing to modify every position and draw statement in the game? The code I'm using for the viewports is below.
// set the viewport to the whole screen
GraphicsDevice.Viewport = new Viewport
{
X = 0,
Y = 0,
Width = GraphicsDevice.PresentationParameters.BackBufferWidth,
Height = GraphicsDevice.PresentationParameters.BackBufferHeight,
MinDepth = 0,
MaxDepth = 1
};
// clear whole screen to black
GraphicsDevice.Clear(Color.Black);
// figure out the largest area that fits in this resolution at the desired aspect ratio
int width = GraphicsDevice.PresentationParameters.BackBufferWidth;
int height = (int)(width / targetAspectRatio + .5f);
if (height > GraphicsDevice.PresentationParameters.BackBufferHeight)
{
height = GraphicsDevice.PresentationParameters.BackBufferHeight;
width = (int)(height * targetAspectRatio + .5f);
}
//Console.WriteLine("Back: Width: {0}, Height: {0}", GraphicsDevice.PresentationParameters.BackBufferWidth, GraphicsDevice.PresentationParameters.BackBufferHeight);
//Console.WriteLine("Front: Width: {0}, Height: {1}", width, height);
// set up the new viewport centered in the backbuffer
GraphicsDevice.Viewport = new Viewport
{
X = GraphicsDevice.PresentationParameters.BackBufferWidth / 2 - width / 2,
Y = GraphicsDevice.PresentationParameters.BackBufferHeight / 2 - height / 2,
Width = width,
Height = height,
MinDepth = 0,
MaxDepth = 1
};
GraphicsDevice.Clear(Color.CornflowerBlue);
The image below shows what the screen looks like. The black on the sides is what I want (and is from the first viewport) and the second viewport is the game and the cornflower blue area. What I want is to get the game to scale to fill the cornflower blue area.

Use a viewport http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.viewport_members.aspx

As is also the case in commercial games, you should provide an option to the user that allows them to switch between 4:3 aspect and 16:9 aspect. You should be able to just modify the camera viewing ratio accordingly.
EDIT:
As far as I have seen, there are no games that 'auto-detect' the proper aspect ratio to use.
As has been pointed out, there are ways to make a good guess as to what the proper aspect ratio is. If XNA allows you to get at the current Windows user's screen settings data, you can determine an aspect ratio based off of the monitor resolution.
Once you have determined the monitor resolution of the user, you can best decide how to deal with it. At first, the best bet may be to just put black bars on the left/right side of the screen to allow full-screen with a 16:9 aspect ratio that is essentially still using the 4:3 artwork.
Eventually you could modify the game so that it changes the viewing port size when the aspect ratio is 16:9. This wouldn't require changing any art assets, just how they are being rendered.

First of all I'm assuming you're talking about XNA 4.0, which AFAIK there are breaking changes between XNA 3.x and XNA 4.0.
I'm relatively new at XNA, however it seems to me that your assets does not fit the size of the window. Let's say that your game are is 320x240 and your window is bigger e.g. 640x480.
Thus you can specify PreferredBuffer in order to scale up your application. So, tell to XNA you are going to use 320x240 by setting the following values;
_graphics.PreferredBackBufferWidth = 320;
_graphics.PreferredBackBufferHeight = 240;
Additionally you can start fullscreen mode by setting:
_graphics.IsFullScreen = true;
Also, you have to handle manually the how the items should change their size once the Window has changed their size.
Checkout my sample at.
https://github.com/hmadrigal/xnawp7/tree/master/XNASample02
(BTW, you can press F11 to switch between fullscreen and normal view)
Best regards,
Herber

I'm not sure if you can actually scale your view port like that. I understand what you're trying to do, but to do it you'd have to do the following.
Set your screen backbuffer width and height to the 16:9 resolution.
Program in the displacement so that objects didn't draw in those borders.
The thing is, all major games these days, if you play them on a 16:9 monitor and select a 4:3 resolution, will stretch to fit the screen. This isn't something you usually want to overcome. You either support many resolutions in your game, or you will get stretching when a user uses the wrong resolution for his or her screen type.
Usually, one sets up their game, and their textures to work based on the relative dimensions of the current viewport or backbuffer width and height. This way, regardless of the resolution inputted, the game scales to work with that width/height ratio.
It's a bit more work, but in the end, makes your game far more polished and compatible with a wide array of systems.
The only time this may not be done is if the app runs in a window (NOT fullscreen).

Related

UWP SwapChainPanel Contents Blurry/Scaled

I am using skia to do drawing on a SwapChainPanel (via ANGLE), and all is going well. However, the one issue that I am having is that the content is being scaled on high DPI screens.
I have a hi-res (x2) and a normal (x1) screen on my machine here. The contents of the panel look great on the 1x display, but when I drag it over, the window resizes and scales the contents. How do I tell the panel not to scale the contents, but to rather let me adjust my side?
My current solution is to create my renderbuffer at the desired scale (eg: panel.Width * 2) and then use the RenderTransform property and set the ScaleTransform (eg: 0.5).
The content now appears smooth and crisp, but now the layout is affected. Since the render transform has "resized" the panel.
What is the correct way to do this?
EDIT
This is what I am doing to create the buffer:
https://github.com/mono/SkiaSharp/blob/master/source/SkiaSharp.Views/SkiaSharp.Views.UWP/AngleSwapChainPanel.cs#L199-L285
You essentially have to set scale on DXGISwapChain to be inverse of swap chain panel composition scale using IDXGISwapChain2.SetMatrixTransform method:
SwapChainPanel panel = ...;
IDXGISwapChain2 swapChain = ...;
DXGI_MATRIX_3X2_F scale;
inverseScale._11 = 1.0f / swapChainPanel.CompositionScaleX;
inverseScale._22 = 1.0f / swapChainPanel.CompositionScaleY;
var hr = swapChain.SetMatrixTransform(ref scale);
if (hr < 0)
Marshal.ThrowExceptionForHR(hr);
I don't know exactly how to obtain the swapchain object reference through the library you're using, though.

Unity - Window size depending on Screen size

I would like my game to have a 1:1 aspect ratio, but scaled up to a certain amount. Meaning that the width and height must be identical, but never larger than the actual screen size. Ontop of that, to ensure consistent pixel sizes the width and height values must be power of 2 value.
I didn't have any problems figuring out the needed value.
int value = 2;
int limit = Screen.currentResolution.height;
while (value * 2 < limit) value *= 2;
Debug.Log(value);
I much rather have no idea how to set the window size BEFORE the splash image is even shown. Is there any way how to do this?
Yes, there is, but that means that you'll need to get rid of the launch window.
The reason is that, if you enable the launch window (from which you can select resolutions, quality, windowed or fullscreen mode etc.), Unity will show only the video card available resolutions - and this means no 1:1 aspect ratio resolutions available.
So, in order to do this, you need to setup the Player Settings as follows:
The important part is to disable the Display Resolution Dialog.
Then you set the Default Screen Width and Height by disabling the Default Is Native Resolution.
Notice that the standalone will be forced to this, and only this, resolution at start - after the splash screen you can set whatever resolution you want by calling the Screen.SetResolution method from any script in the first scene loaded.
Of course you can make the standalone start in windowed or fullscreen mode, by unticking/ticking the Default Is Full Screen option.
That's pretty much it, if you wanted to give the user the option to choose from a list of 1:1 AR resolutions, you simply just can't at the moment afaik.
Edit: The resolution info of the Player Settings are stored in the registry inside HKEY_CURRENT_USER\Software\[YourCompanyName]\[YourGameName].
The 3 keys are these:
Screenmanager Is Fullscreen mode
Screenmanager Resolution Height
Screenmanager Resolution Width
To change those from inside the game at runtime, you need to use:
PlayerPrefs.SetInt("Screenmanager Is Fullscreen mode", [0/1]);
PlayerPrefs.SetInt("Screenmanager Resolution Height", [HeighthRes]);
PlayerPrefs.SetInt("Screenmanager Resolution Width", [WidthRes]);
These will be read the next time the game is launched, setting the starting resolution before the splash screen.

Trouble understanding render targets and back buffers in monogame

My graphics device is set up as follows:
GraphicsDeviceManager = new GraphicsDeviceManager(GameBase.GameRef);
GraphicsDeviceManager.PreferredBackBufferWidth = 640;
GraphicsDeviceManager.PreferredBackBufferHeight = 480;
GraphicsDeviceManager.ApplyChanges();
I can't find anywhere on google explaining exactly what the preferred back buffer width means.
I set up a render target like this:
RenderTarget = new RenderTarget2D(
GraphicsDeviceManager.GraphicsDevice,
640,
480,
false,
GraphicsDeviceManager.GraphicsDevice.PresentationParameters.BackBufferFormat,
DepthFormat.Depth24);
I can't find anything on google about what the 640 and 480 actually mean with respect to how the render target is drawn.
The problem I have is that if I resize the window, everything stretches. I'm trying to understand what monogame is doing to cause this, and then finally figure out a solution.
At the start my window is 640x480 and the PreferredBackBufferWidth and height are 640/480 and the render target is 640/480
If I resize the window to 1920x1080 for example, everything stretches, so somehow monogame no longer rendering the 640x480 texture onto a 640x480 part of the screen, but I haven't changed any of the values or done any code for this.
If I resize the screen, should the render target be destroyed and recreated at the new size of the window, or should the back buffer width and height be changed, or both, or something else?
I am very confused and there isn't any documentation online technical enough to answer in detail. Once I understand it I should be able to write some code so that when you resize the screen nothing happens, everything stay the same size, and there is a black area around the right/bottom edge because the window is larger than what i'm rendering to.
The back buffer is a special render target that is displayed on the screen. It is stretched to fill the window, which results in scaling if the window size does not match.
You can resize the back buffer when the window size changes. The Game.Window.ClientSizeChanged event is fired when that happens (note that it may be fired multiple times if the user is resizing the window by dragging). Then resizing the back buffer is simply
GraphicsDeviceManager.PreferredBackBufferWidth = Window.ClientBounds.Width;
GraphicsDeviceManager.PreferredBackBufferHeight = Window.ClientBounds.Height;
GraphicsDeviceManager.ApplyChanges();
Whether you should recreate the RenderTarget depends on what you're using it for.

XNA Disable Auto Fit to Screen

I'm using the XNA 4.0 framework to create a business intelligence screen that will be projected in a control room. The screen itself is designed to fit on two 1920 * 1080 projectors in series.
Currently I'm defining the resolution of the screen as follows:
graphics.PreferredBackBufferWidth = 3840;
graphics.PreferredBackBufferHeight = 1080;
However if I run the solution, XNA automatically 'squashes' the 2D graphics so that the entire screen fits on my primary 1920 * 1080 screen. How do you disable this 're-sizing' functionality in XNA? What I want to achieve is one big screen that I can show across two 1920 * 1080 monitors. Not a squashed screen that fits on one monitor.
Note that my XNA knowledge is very limited. I'm using SpriteFonts and Texture2D to create the graphic objects
Your solution should work if you are setting those values on the Game class constructor and have configured the screens to expand content between themselves.
Another, not really recommended, way to do it is to apply the changes on either the Initialize or LoadContent method.
In order to do this, just add the following line after setting the dimensions:
this.graphics.ApplyChanges();
So your whole thing would look like this:
protected virtual void Initialize()
{
this.graphics.PreferredBackBufferWidth = 1024;
this.graphics.PreferredBackBufferHeight = 600;
this.graphics.ApplyChanges();
}
If you do not want to use the ApplyChanges method, you can set the values on the class constructor without calling this method.
Also, be sure to check that the graphics.IsFullScreen property is not set to true.

How do I determine the true pixel size of my Monitor in .NET?

I want to display an image at 'true size' in my application. For that I need to know the pixel size of the display.
I know windows display resolution is nominally 96dpi, but for my purposes I want a better guess. I understand this information may not always be available or accurate (e.g. older CRT displays), but I imagine with the prevelance of LCD displays that this should be possible!
Is there a way to get the pixel size of my display?
Is there a way to determine if the pixel size is accurate?
.NET API's preferred (I couldn't find them), but Win32 is OK too, I'm happy to P/Invoke.
For the display size you'll want Screen.PrimaryScreen.Bounds.Size (or Screen.GetBounds(myform)).
If you want the DPI, use the DpiX and DpiY properties of Graphics:
PointF dpi = PointF.Empty;
using(Graphics g = this.CreateGraphics()){
dpi.X = g.DpiX;
dpi.Y = g.DpiY;
}
Oh, wait! You wanted actual, hold a ruler up to the monitor and measure, size?! No. Not possible using any OS services. The OS doesn't know the actual dimensions of the monitor, or how the user has it calibrated. Some of this information is theoretically detectable, but it's not deterministic enough for the OS to use it reliably, so it doesn't.
As a work around, you can try a couple of things.
You can try to query the display string of the installed monitor device (I'm not sure how to do that) and see if you can parse out a sensible size out of that. For example, the monitor might be a "ValueBin E17p", and you might deduce that it's a 17" monitor from that. Of course, this display string is likely to be "Plug and Play Monitor". This scheme is pretty sketchy at best.
You could ask the user what size monitor they have. Maybe they'll know.
Once you know (or think you know) the monitor's diagonal size, you need to find its physical aspect ratio. Again, a couple of things:
Assume the current pixel aspect ratio matches the monitor's physical aspect ratio. This assumes that (A) the user has chosen a resolution that is ideal for their monitor, and that (B) the monitor has square pixels. I don't know of a current consumer-oriented computer monitor that doesn't have square pixels, but older ones did and newer ones might.
Ask the user. Maybe they'll know.
Once you know (or think you know) what the monitor's diagonal size and physical aspect ratio are, then you you can calculate it's physical width and height. A2 + B2 = C2, so a few calculations will give it to you good:
If you found out that it's a 17" monitor, and its current resolution is 1280 x 1024:
12802 + 10242 = 2686976
Sqrt(2686976) = 1639.1998047828092637409837247032
17" * 1280 / 1639.2 = 13.274768179599804782820888238165"
17" * 1024 / 1639.2 = 10.619814543679843826256710590532"
This puts the physical width at 13.27" and the physical height at 10.62". This makes the pixels 13.27" / 1280 = 10.62" / 1024 = 0.01037" or about 0.263 mm.
Of course, all of this is invalid if the user doesn't have a suitable resolution, the monitor has wacky non-square pixels, or it's an older analog monitor and the controls aren't adjusted properly for the display to fill the entire physical screen. Or worse, it could be a projector.
In the end, you may be best off performing a calibration step where you have the user actually hold a ruler up to the screen, and measure the size of something for you. You could:
Have the user click the mouse on any two points an inch (or a centimeter) apart.
Draw a box on the screen and have the user press the up and down arrows to adjust its height, and the left and right arrows to adjust its width, until the box is exactly one inch (or centimeter) square according to their ruler.
Draw a box on the screen and have the user tell you how many inches/centimeters it is in each dimension.
No matter what you do, don't expect your results to be 100% accurate. There are way too many factors at play for you (or the user) to get this exactly correct, every time.
Be aware that 96 dpi is usually pretty close to accurate. Modern pixels on non-projected screens all tend to be about 0.25 mm, give or take, so you usually end up with about 100 physical pixels per inch, give or take, if the monitor is set to its native resolution. (Of course, this is a huge generalization and does not apply to all monitors. Eee PCs, for example, have pixels about 0.19 mm in size, if I remember the specs correctly.)
sorry, you've got to P/Invoke for this information.
Here's the link that I utilized for it a while ago:
http://www.davidthielen.info/programming/2007/05/get_screen_dpi_.html
You can check by just manually calculating from your screen size
cos(45)*LCD_SCREEN_DIAGONAL_IN_INCHES/sqrt(HORZ_RES^2 + VERT_RES^2)
That would give you the pixel width in inches
GetDeviceCaps can be P/Invoke'd to get some figures, but I've never known the figures to be that trustworthy...
You may obtain the physical dimensions of the display using the EDID information stored in the registry. You can obtain the appropriate monitor's registry key using the EnumDisplayDevices windows API call.
Physical Dimensions to the Screen object:
TL;DR
WPF's True Size = Pixels * DPI Magnification
DPI Magnification:
Matrix dpiMagnification
= PresentationSource.FromVisual(MyUserControl).CompositionTarget.TransformToDevice;
double magnificationX = dpiMagnification.M11;
double magnificationY = dpiMagnification.M22;
Discussion
I had trouble solving this question still in 2020. Back when this question was asked/answered in 2009, .NET C# probably meant Windows Forms. But WPF is the de facto standard of the day...
By asking about "true size" you have probably already figured out that the operating system does some calculation with actual pixels (say 1366x768, which I understand is usual laptop resolutions) and the DPI (hard to find) in order to give a control's true size. And you are trying to make an app that scales to different monitors.
This DPI actual number seems to be hidden, but it has been normalized (converted to a percentage). Assume 100% = 96 DPI, just because the actual number does not matter anymore. People can easily increase the system-wide text size by going to Desktop on Windows 10 > right click > Display settings > section Scale and layout > change the percentage to magnify text and other elements.
You can find the pixels another way, and multiple/divide the pixel by the DPI percentage in order to get true size. For instance, I want to drag a UserControl around a canvas element of a WPF window with the mouse. The user control's pixel count and the mouse xy-coordinates were off by the normalized DPI. In order to keep the mouse moving at the same rate as the user control, I use:
double newXCoord = System.Windows.Forms.Cursor.Position.X;
double newYCoord = System.Windows.Forms.Cursor.Position.Y;
double deltaX = newXCoord - oldXCoord;
double deltaY = newYCoord - oldYCoord;
double magnificationX = 1;
double magnificationY = 1;
Matrix dpiMagnification
= PresentationSource.FromVisual(visual).CompositionTarget.TransformToDevice;
if (magnificationMatrix != null)
{
magnificationX = dpiMagnification.M11;
magnificationY = dpiMagnification.M22;
}
PixelsFromLeft += deltaX / m_magnificationX;
PixelsFromTop += deltaY / m_magnificationY;

Categories