Unity - Window size depending on Screen size - c#

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.

Related

Dealing with multiple DPI screens

Let's suppose that I have two screens, side by side:
1920x1080 100% DPI
1360x768 125% DPI
For my Window, this means:
1920x1080: Ok
1088x614: Not ok, it's divided by 1,25 because of the scaling factor.
Turning into this:
1920x1080 + 1088x614: 3008x1080
I want to use the CopyFromScreen/BitBlt methods.
These methods ignore all DPI info, making the Left and Top properties (of a window, for example) useless if inside a high dpi screen. Or left to a high dpi screen, since it behaves like 1 screen, example:
So whenever I need to get a screen point from within a set of screens with at least one having a high DPI, it will return a smaller point.
Is there any way to get the true (by true, ignoring the scaling factor) XY info from a set of screens with (at least one) high DPI?
I already tried the managed PointToScreen and the unmanaged ClientToScreen methods, both resulting the same "right" point.
Please, read
I want to take screenshots of the screen based of the position of my Window.
I have two monitors, one with 100% DPI, other with 125% DPI.
If my Window is inside the 1st monitor, the screenshot based on the Left/Top properties of my Window works.
If my Window is inside the second monitor, the screenshot won't take the right spot!
Because
The BitBlt API method ignores the scaling of the screens. Example:
Screenshot of the point 100;100 will be right, because it's inside the 1st screen.
Screenshot of the point 1950;100 won't be right, because it's inside the 2nd screen. Notice that it's 30 pixels to the right.
Why?
As said earlier, for my app, the 125% DPI reduces the screen resolution to 1088x614, but for the BitBlt method, it is still 1360x768.
So I can't convert the Left/Top properties, because it will be wrong, since there is a 100% DPI screen to the left.
Example of the Left property:
I believe this is the right way to convert:
1920px + 50px: 100% + 125%: 1920 + 62: 1982px
And this is the proposed version:
1920px + 50px: 100% + 125%: 2400 + 62: 2462px
See, if I simple convert the current Left property based on the DPI of the current Window, on this case my second screen, I'll also be converting the values of my first screen. This should not happen.

Detect if non DPI-aware application has been scaled/virtualized

I'm trying to detect in a WinForms application if it has been launched in scaled/virtualized mode due to the OS having a high DPI. Currently, in a system running at 3840x2400 with 200% scaling, the application sees the resolution as 1920x1200, the DPI as 96, and the scale factor is 1.
We are in the process of making the application DPI-aware, but until then, we need a "quick fix" that will allow us to detect if scaled. The reason for this is that it breaks a functionality in the application that takes a screenshot. We use the scaled dimensions in Graphics.CopyFromScreen, it takes a screenshot of the wrong size since it is expecting the non-scaled dimensions.
I am aware of the DPI-awareness setting, but for the moment, we still want the application to be scaled, but be able to detect that we are scaled and get the non-scaled dimensions, if possible.
An application that is not explicitly marked as high-DPI aware will be lied to by the system and told that there are 96 DPI with a scaling factor of 100%. In order to get the real DPI settings, and avoid automatic virtualization by DWM, you will need to include <dpiAware>True/PM</dpiAware> in your application's manifest. More information is available here.
In your case, it sounds like you are looking for the LogicalToPhysicalPointForPerMonitorDPI and PhysicalToLogicalPointForPerMonitorDPI pair of functions. As the linked documentation explains, by default, the system will return information about other windows based on the DPI awareness of the caller. So if a non-DPI aware application tries to get the bounds of a window of a high-DPI aware process, it will get bounds that have been translated into its own non-DPI aware coordinate space. This would be, in the vernacular of these functions, the "logical" coordinates. You can convert these to "physical" coordinates, which are those that are actually used by the operating system (and other high-DPI aware processes).
To answer your actual question, though: If you absolutely need to break through the operating system's lies in a process that is not DPI aware, I can think of two ways to do so:
Call the GetScaleFactorForMonitor function. If the resulting DEVICE_SCALE_FACTOR value is anything other than SCALE_100_PERCENT, then you are scaled. If your application is not DPI aware, then you are being virtualized.
This is a quick-and-dirty solution, as a simple P/Invoke definition is all you need to call it from a WinForms application. However, you should not rely on its results for anything more than a Boolean "are we scaled/virtualized?" indicator. In other words, do not trust the scale factor that it returns!
On a Windows 10 system where the system DPI is 96, and a high-DPI monitor has a 144 DPI (150% scaling), the GetScaleFactorForMonitor function returns SCALE_140_PERCENT when it would be expected to return SCALE_150_PERCENT (144/96 == 1.5). I don't really understand why this is the case. The only thing I can figure out is that it was designed for Metro/Modern/UWP apps on Windows 8.1, where 150% is not a valid scale factor but 140% is. The scaling factors have since been unified in Windows 10, but this function appears not to have been updated and still returns unreliable results for desktop applications.
Calculate the scaling factor yourself, based on the logical and physical widths of the monitor.
First, of course, you'll need to obtain an HMONITOR (handle to a specific physical monitor). You can do this by calling MonitorFromWindow, passing a handle to your WinForms window, and specifying MONITOR_DEFAULTTONEAREST. That will get you a handle to the monitor that your window of interest is being displayed on.
Then, you'll use this monitor handle to get the logical width of that monitor by calling the GetMonitorInfo function. That fills in a MONITORINFOEX structure that contains, as one of its members, a RECT structure (rcMonitor) that contains the virtual-screen coordinates of that monitor. (Remember that, unlike .NET, the Windows API represents rectangles in terms of their left, top, right, and bottom extents. The width is the right extent minus the left extent, while the height is the bottom extent minus the top extent.)
The MONITORINFOEX structure filled in by GetMonitorInfo will also have given you the name of that monitor (the szDevice member). You can then use that name to call the EnumDisplaySettings function, which will fill in a DEVMODE structure with a bunch of information about the physical display modes for that monitor. The members you're interested in are dmPelsWidth and dmPelsHeight, which give you the number of physical pixels per width and height, respectively.
You can then divide the logical width by the physical width to determine the scaling factor for the width. Same thing for the height (except that all monitors I'm aware of have square pixels, so the vertical scaling factor will be equal to the horizontal scaling factor).
Example code, tested and working in Windows 10 (written in C++ because that's what I have handy; sorry you'll have to do your own translation to .NET):
// Get the monitor that the window is currently displayed on
// (where hWnd is a handle to the window of interest).
HMONITOR hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
// Get the logical width and height of the monitor.
MONITORINFOEX miex;
miex.cbSize = sizeof(miex);
GetMonitorInfo(hMonitor, &miex);
int cxLogical = (miex.rcMonitor.right - miex.rcMonitor.left);
int cyLogical = (miex.rcMonitor.bottom - miex.rcMonitor.top);
// Get the physical width and height of the monitor.
DEVMODE dm;
dm.dmSize = sizeof(dm);
dm.dmDriverExtra = 0;
EnumDisplaySettings(miex.szDevice, ENUM_CURRENT_SETTINGS, &dm);
int cxPhysical = dm.dmPelsWidth;
int cyPhysical = dm.dmPelsHeight;
// Calculate the scaling factor.
double horzScale = ((double)cxPhysical / (double)cxLogical);
double vertScale = ((double)cyPhysical / (double)cyLogical);
ASSERT(horzScale == vertScale);

How to align windows when they are very near

I have an application with some small windows on the screen. I would like to align them to each other, so when I move one close enough, it will automatically be align with the other. Helping me positioning and size them all.
How can I know the position of other windows when there isn't a parent window? Is it possible to know it even if they are different process (applications)?
I am not fully sure what you mean but the following trick is what I use for dynamic layout and it gives me full control over anything, you just need to play around with it and you can easily get the distance between two windows.
This code is copied from a windows phone app but it is easily understood.
width = Convert.ToInt32(Window.Current.Bounds.Width);//gets window width
height = Convert.ToInt32(Window.Current.Bounds.Height);// gets window height
double dist Math.Abs(Btn1.GetValue(Canvas.LeftProperty) - Btn2.GetValue(Canvas.LeftProperty) + Btn2.Width);
note first two lines are only in order to have the proportions right in every single movement and resize you do, you can keep it all proportional to the window size.

When building to mobile the size of fonts reduce and became very small

Like I said in the title when i built to mobile the letter became too small. I think it is because phone has more pixel for his little screen.
Well I tried to fix text by simply making text to images in photoshop. But how can I make InputFields text (not the placeholder).
At a guess, your Canvas' CanvasScaler's UI Scale Mode is probably set to Constant Pixel Size, and if your mobile device's resolution is too high, text will appear small.
Try changing the UI Scale Mode to Scale With Screen Size and change the properties it provides as needed.
(if your Canvas doesn't have a CanvasScaler component, click the Add Component button and add it)

Going fullscreen without stretching in an XNA game

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).

Categories