I am using OnGUI Buttons on a car game and attached forward and backward buttons on it. But they keep on adjusting on all screens badly.
I have tried various online solutions, but failed. I am using this:
GUI.RepeatButton (new Rect (Screen.width - 780, Screen.height - 130, 120, 120), LeftBtnTexture, TransparentStyle)
I know this is wrong, what will be alternative width and height setting for all screens. :(
Take a look at Screen.dpi - http://docs.unity3d.com/ScriptReference/Screen-dpi.html
It will allow you to specify button size in inches. But be aware that dpi could be unavailable (equals zero) or maybe even incorrect, therefore you should do some sanity check. For example:
public float sizeInInches = 0.5;
float dpi = Screen.dpi;
if (dpi < 25 || dpi > 800) {
dpi = 150;
}
float sizeInPx = sizeInInches * dpi;
As an alternative for some cases, you may want to set GUI element size in percent of screen dimensions, like sizeInPx = Screen.width * 0.1;
Related
I'm making a game using Go with the rendering library "pixel".
I am trying to get fullscreen/resizing working, but I have an issue, and that's how to deal with stretching of images
I've seen and have used the solution of a "letterbox" effect for the game.
That is, drawing the game in the same aspect ratio, so stretching wouldn't be an issue, leaving the extra space as black bars.
My issue is, when trying to do this in this rendering library, I can only scale the matrix of the "Canvas" I'm drawing on.
I'm used to SFML with C++ where I can just define a fixed size for the "View" [what's being drawn on], not scaling it.
This is how I'm getting the current scaling for the matrix, it's incorrect, but it's what I have.
camZoom is 2.0, it's in there so the screen is bigger. If the camZoom is 1.0 [normal], the images are too small.
func letterBox(win *pixelgl.Window) {
windowRatio := winWidth / winHeight
viewRatio := win.Bounds().W() / win.Bounds().H()
sizeX := 1.
sizeY := 1.
horizontalSpacing := true
if windowRatio < viewRatio {
horizontalSpacing = false
}
if horizontalSpacing {
sizeX = viewRatio / windowRatio
} else {
sizeY = windowRatio / viewRatio
}
viewMatrix = pixel.IM.
Moved(pixel.V(win.Bounds().Center().X/camZoom, win.Bounds().Center().Y/camZoom)).
ScaledXY(pixel.V(win.Bounds().Center().X/camZoom, win.Bounds().Center().Y/camZoom), pixel.V(sizeY, sizeX))
}
Here's what it currently looks like:
Normal [no resizing done, 1024x768]:
Width of window increased [shrinks]
[]2
Height of window increased [stretches on X, hiding most of 'Canvas']
[]3
Fullscreen [just keeps it's original size, but the width of the 'canvas' is slightly shrunken]
[]4
I just can't really figure out the math to it.
If this is not the best way to solve the full-screen issue I have, then let me know and I can make another question, but I was told this is how you should do it.
Ended up being super simple just a math error on my part.
func letterBox(win *pixelgl.Window) {
sizeX := 1.
sizeY := 1.
if win.Bounds().H()-winHeight > win.Bounds().W()-winWidth {
sizeX = win.Bounds().W() / winWidth
sizeY = win.Bounds().W() / winWidth
} else {
sizeX = win.Bounds().H() / winHeight
sizeY = win.Bounds().H() / winHeight
}
viewMatrix = pixel.IM.
Moved(pixel.V(win.Bounds().Center().X/camZoom, win.Bounds().Center().Y/camZoom)).
ScaledXY(pixel.V(win.Bounds().Center().X/camZoom, win.Bounds().Center().Y/camZoom), pixel.V(sizeX, sizeY))
}
Just had to figure out if the width difference of the newly-sized window was greater, or the height, then apply the same ratio [new window width / width of old frame] to scale the matrix properly. Dumb answer to a dumb question.
In Xiaomi devices, there are drawn an image outside of camera's letterbox
In other devices everything is correct
I attached both sumsung and xiaomi images, the screenshot that looks ugly is xiaomi, and good look in samsung
float targetaspect = 750f / 1334f;
// determine the game window's current aspect ratio
float windowaspect = (float)Screen.width / (float)Screen.height;
// current viewport height should be scaled by this amount
float scaleheight = windowaspect / targetaspect;
// obtain camera component so we can modify its viewport
Camera camera = GetComponent<Camera>();
// if scaled height is less than current height, add letterbox
if (scaleheight < 1.0f)
{
Rect rect = camera.rect;
rect.width = 1.0f;
rect.height = scaleheight;
rect.x = 0;
rect.y = (1.0f - scaleheight) / 2.0f;
camera.rect = rect;
}
try setting the image to clamp instead of repeat.
this will give the result of black borders but you won't have that weird texture
I don't know what caused that problem, however i solved it in a tricky way. I just added second camera to display black background. Only My main camera's viewport is letterboxed, but not second camera. So it made display to look good
I'm trying to make my UI elements work and remain the same for every different resolution. I added a Canvas Scaler to my Canvas and played around with the settings until it looked finished.
I then tried building the game and running it at few different resolutions to confirm that it was working. However, the Canvas Scaler doesn't seems to work.
http://prntscr.com/d1afz6
Above is some random resolution but that's how big my editor screen is and that's what I'm using as my reference resolution. That's also the hierarchy for this specific Canvas http://prntscr.com/d1aggx. It takes almost the whole screen when ran at 640x480. I have no clue why this is not working. I've read most of the unity guides on that but none of them seem to have that problem.
Ok, to fit something no matter the size of the screen, you have to use a separate coordinate system than Unity's absolute system. One of Unity's models is the View. The View is coordinates 0,0 at the top left, and 1,1 at the bottom right. Creating a basic Rect that handles that, is the following.
using UnityEngine;
namespace SeaRisen.nGUI
{
public class RectAnchored
{
public float x, y, width, height;
public RectAnchored(float x, float y, float width, float height)
{
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public static implicit operator Rect(RectAnchored r)
{
return new Rect
{
x = r.x * Screen.width,
y = r.y * Screen.height,
width = r.width * Screen.width,
height = r.height * Screen.height
};
}
}
}
Here, we take the normal Rect floats, the x,y coordinates along with a width and height. But these are in the values [0..1]. I don't clamp it, so it can be tweened on and off the screen with animation, if desired.
The following is a simple script that create's a button in the lower right corner of the screen, and resizes as the screen grows or shrinks.
void MoveMe()
{
RaycastHit hit;
if (Physics.Raycast(transform.position, -Vector3.up, out hit, float.MaxValue)
|| Physics.Raycast(transform.position, Vector3.up, out hit, float.MaxValue))
transform.position = hit.point + Vector3.up * 2;
}
void OnGUI()
{
if (GUI.Button(new RectAnchored(.9f, .9f, .1f, .1f), "Fix me"))
{
MoveMe();
}
}
The X is .9 to the right and Y .9 from the top, and width and height of .1, so the button is 1/10th of the screen in height and width, positioned in the bottom 1/10th of the screen.
Since OnGUI is rendered every frame (or so), the button rect updates with the screen resize automatically. The same would work in a typical UI, if you are using Update() to render your windows.
I hope this explains the difference between what I meant with absolute coordinates. Setting say the previous example (using absolutes) in 640x480, it'd be something like new Rect(576, 432, 64, 48) and it wouldn't scale. By using new RectAnchored(.9f, .9f, .1f, .1f) and have it rendered into UI space based on Screen size, then it scales automatically.
This is the code the set the layout for my game the problem is that when my game is play on a smaller version phone the layout changes how do i keep the same dimensions
Code:
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
if let scene = GameScene.unarchiveFromFile("GameScene") as? GameScene {
let sKView = self.view! as! SKView
sKView.ignoresSiblingOrder = true
scene.size = sKView.bounds.size
let reveal = SKTransition.fadeWithDuration(0.45)
sKView.presentScene(scene, transition: reveal)
}
}
The code above doesn't work well it does but only for iPhone 6 plus which is the iPhone I'm test my application with but it doesn't change dimensions when on smaller iPhones.
The only way that I'm aware of is to manually resize and move nodes based on screen size, for example:
if size.width == 320 && size.height == 568 {
//iPhone 5/5S screen dimensions
spriteNode!.size = CGSize(height: 30, width: 45)
spriteNode!.position = CGPoint(x: 100, y: 50)
} else if size.width == 768 && size.height == 1024 {
//iPad screen dimensions
spriteNode!.size = CGSize(height: 60, width: 90) /* Twice the normal size */
spriteNode!.position = CGPoint(x: 200, y: 80) /* Moved over and above a bit */
}
It's a pain in the neck, but AFAIK it's the easiest way to resize nodes to fit different screen sizes.
The way I'd usually go about this is as follows: when creating the scene, I will set the size to specific defaults based on the device; eg: for landscape only mode (sizes are just for example):
let size =
UIDevice.currentDevice().userInterfaceIdiom == .Pad ?
CGSizeMake(3200, 2400) : // all ipads, 4:3 ratio
UIScreen.mainScreen().bounds.landscapeSize().width < 568 ?
CGSizeMake(2400, 1600) : // iphone 4s rows; 3:2 ratio
CGSizeMake(2850, 1600) // other iphones 16:9 ratio
This allows me to have known sizes for the scene on any device.
To layout UI elements, I use the scene size and use offsets, so a button at the top left will have coordinates (0, scene.height) (for a sprite anchored to (0,1)), and that button will always appear on the top left of any scene in any device.
It is usually better to have a node hiearchy in your scene
Scene
BackgroundNode: SKNode, zPosition = 0
WorldNode: SKNode, zPosition = 10
HudNode: SKNode, zPosition = 20
This will allows you to zoom in / zoom out on the world node (which contain gameplay elements) while keeping UI elements (buttons) in the HUD node a fixed size.
To zoom in/out you modify the worldNode scale directly, for example scene.worldNode.setScale(scaleAmount). (assuming you are not using SKCameraNode)
This scheme will allow you to not have to position/resize individual sprites for each device; rather changes are done at the root level - in this case the worldNode, and also allows you to have UI elements that more or less scale appropriately based on device, and remain fixed independent of your worldNode scale.
I am using GetSystemMetrics(SM_CXSIZEFRAME) for the width of the border, it works with the basic design, but not with aero, how can I improve it, so it works with aero?
I am using GetSystemMetrics(SM_CYCAPTION) for the height of the title bar but the value is too small for both, basic and aero design, what am I doing wrong here?
In unthemed Windows, GetSystemMetrics(SM_CYCAPTION) is the height of the text in the title bar; you need to add in the size of the frame and border padding (GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CYEDGE) * 2).
For themed Windows (which is the default these days), GetThemeSysSize is most likely the function you're looking for; in particular, GetThemeSysSize(SM_CXBORDER) for the border width, and GetThemeSysSize(SM_CYSIZE) + GetThemeSysSize(SM_CXPADDEDBORDER) * 2 for the title bar.
I had a similar problem, where I needed to subtract the title bar height from the mouse Y to render a cursor in the window.
After looking around, and looking at functions like GetSystemMetrics, I ended up just using GetWindowRect and ClientToScreen.
I got the screen position of the client area using ClientToScreen with point 0,0.
Then subtracted that from the window top retrieved through GetWindowRect. Result is the distance from the top of the window to the inside of the window. Or the titlebar height.
int getWindowHeadSize()
{
RECT Rect;
GetWindowRect(hWnd, &Rect);
POINT point = { 0, 0 };
ClientToScreen(hWnd, &point);
return point.y - Rect.top + GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CYEDGE) * 2;
}