Jump to content

  • Log In with Google      Sign In   
  • Create Account

GUI aspect ratio independence


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
20 replies to this topic

#1 rpiller   Members   -  Reputation: 706

Like
0Likes
Like

Posted 24 October 2013 - 07:09 PM

I'm making a 2D GUI for my game. I get screen resolution independence by using percentages for x, y & width, height for the controls relative to it's parent container where the top level is the entire screen. This helps make my GUI look exactly the same on all resolutions of the same aspect ratio.

 

Now I want to make it aspect ratio independent so that I don't have to make my screens per aspect ratio. So when I initially make the GUI, I use a base screen res/aspect ratio to get it to look just right. I want to take this base and make it look the same (positioning and sizing) on any resolution the player selects for the game. What is the math on something like that?



Sponsor:

#2 Xai   Crossbones+   -  Reputation: 1459

Like
1Likes
Like

Posted 24 October 2013 - 10:51 PM

Depends on what you mean.  Do you mean you want letter boxing, so the actual controls are in correct aspect ratio as your base version ... or do you mean if the screen is 20% wider, you want your controls to be 20% wider?  Either way, you can see that if you just thing about it from those 2 choice, the answer is almost obvious.



#3 Xai   Crossbones+   -  Reputation: 1459

Like
0Likes
Like

Posted 24 October 2013 - 10:58 PM

Opps.  Accidentally hit post.  So anyway .. lets say your base aspect ratio was probably either 4/3 or 16/9 doesn't matter .. but whatever it is.  You consider that the base rectable, which is 1bwu wide (1 base width unit) and 1bhu tall (base height unit).  So now for 4/3, 1bwu = 1.333_bhu ... So then you do this math on your other aspect ratio ... lets 1swu (screen width unit) = 1.777_shu.  So if you want to letterbox, you need to create letterboxes equal to 0.222 (1.777 - 1.333 / 2) shu wide on each side.  If you want to stretch instead, you just mulitiple your bwu widths by 0.444 to stretch them wider.



#4 SimonForsman   Crossbones+   -  Reputation: 6305

Like
4Likes
Like

Posted 25 October 2013 - 12:24 AM

on top of the options xai mentioned you can also place your GUI elements relative to an arbitrary screen edge/corner/center rather than relative to a fixed origin.


Edited by SimonForsman, 25 October 2013 - 12:43 AM.

I don't suffer from insanity, I'm enjoying every minute of it.
The voices in my head may not be real, but they have some good ideas!

#5 rpiller   Members   -  Reputation: 706

Like
0Likes
Like

Posted 25 October 2013 - 05:26 AM

No letterboxing. I think that looks bad.

 

From using %'s what I see is that the position (x,y) of the controls can always use straight up % and look the way I want. ie. x,y at 50% will always be the same position on any resolution because it's using 50% of it's parent and if no parent then that's calculating based on the screen width/height.

 

However using % for size (width, height) doesn't work correctly when done on a base resolution. ie. 50% width is not the same on a 4:3 vs 16:9. It'll appear much wider. However I don't want it to appear that much wider. I want it to look proportionate just like it does on my base resolution that I developed on. It will be wider than the 4:3 base resolution but not what 50% width would give you. Somehow it needs to know that 50% at 4:3 would = some other % at 16:9 to look basically the same on screen when I scale the control images.


Edited by rpiller, 25 October 2013 - 05:30 AM.


#6 SimonForsman   Crossbones+   -  Reputation: 6305

Like
0Likes
Like

Posted 25 October 2013 - 06:38 AM

No letterboxing. I think that looks bad.

 

From using %'s what I see is that the position (x,y) of the controls can always use straight up % and look the way I want. ie. x,y at 50% will always be the same position on any resolution because it's using 50% of it's parent and if no parent then that's calculating based on the screen width/height.

 

However using % for size (width, height) doesn't work correctly when done on a base resolution. ie. 50% width is not the same on a 4:3 vs 16:9. It'll appear much wider. However I don't want it to appear that much wider. I want it to look proportionate just like it does on my base resolution that I developed on. It will be wider than the 4:3 base resolution but not what 50% width would give you. Somehow it needs to know that 50% at 4:3 would = some other % at 16:9 to look basically the same on screen when I scale the control images.

 

I gave you a solution that does just that.

 

skip the percentages and use a coordinate system where the viewport is always 1 unit high and resolution.x / resolution.y wide and then place your objects relative to different corners,edges (or the center) instead.

 

so if you place a 0.5 units wide and 0.1 unit high healthbar 0.05 units down and 0.25 units to the left (to compensate for its width) from the top-center you just draw it at:

 

x = res.x / 2 + (-0.25 * res.y) //not a typo, we always multiply with the vertical resolution, the horizontal resolution only affects the origin position

y = 0 + (0.05 * res.y) 

 

no stretching, no letterboxing and element size will be the same at 1440x1080 and 1920x1080, a wider aspect ratio only gives you more clean space in the center of the screen (if you place GUI elements at the left and right edges)


Edited by SimonForsman, 25 October 2013 - 06:39 AM.

I don't suffer from insanity, I'm enjoying every minute of it.
The voices in my head may not be real, but they have some good ideas!

#7 rpiller   Members   -  Reputation: 706

Like
0Likes
Like

Posted 25 October 2013 - 06:55 AM

You have the position formula, but what would the size (width/height), of the control, formula be? I don't want it to be the same absolute size on every resolution. I want it to scale proportionately with the resolution.so that you won't have more clean space but so it looks the same on every resolution.

 

 

As far as I can tell using %'s for position does the same as what you're saying as the % is always based on the top/left corner of the parent control. Results in the same positioning it would seem, and for me %'s make more sense in my head. The question still remains on the sizing of controls though and how to scale them proportionately across all resolutions using a base resolution that the values were designed against.

 

I'm making an editor to design my GUI screens. In this editor you give a default resolution that you position all the controls against. In the editor the positioning will be in pixels, but when I save the positioning to file I'm converting them to %'s. The game will read the %'s against the resolution and position based on that. This way everything is done automatically for me. I'm going for a WYSIWYG editor meaning the position & size of the controls will always look that exact same on every resolution giving the exact same look across all resolutions.


Edited by rpiller, 25 October 2013 - 07:00 AM.


#8 SimonForsman   Crossbones+   -  Reputation: 6305

Like
0Likes
Like

Posted 25 October 2013 - 07:09 AM

You have the position formula, but what would the size (width/height), of the control, formula be? I don't want it to be the same absolute size on every resolution. I want it to scale proportionately with the resolution.so that you won't have more clean space but so it looks the same on every resolution.

 

 

As far as I can tell using %'s for position does the same as what you're saying as the % is always based on the top/left corner of the parent control. Results in the same positioning it would seem, and for me %'s make more sense in my head. The question still remains on the sizing of controls though and how to scale them proportionately across all resolutions using a base resolution that the values were designed against.

 

if you use percentages for position you have to use them for scale as well or things will get bad.

 

consider for example an object that is 1% wide and placed at x:99% (it will reach the edge of the screen perfectly).

 

if you make the window wider and keep x:99% you also have to keep the width at 1% (if you reduce the width to avoid stretching it won't reach the edge anymore and its position will be off).

 

 

With the system i described the scale is always size * resolution.y

so a 0.1x0.1 square sprite becomes 108x108px at both 1920x1080 and 1440x1080 (or any other resolution that is 1080px high,

at 800x600 however it would be 60x60px


I don't suffer from insanity, I'm enjoying every minute of it.
The voices in my head may not be real, but they have some good ideas!

#9 rpiller   Members   -  Reputation: 706

Like
0Likes
Like

Posted 25 October 2013 - 08:04 AM

OK, I'll give this a shot. I have to see it in action and the same settings for different resolutions as I can't picture how it would exactly look in terms of size (I get position) in my head. I get that you'd get more clean space but for some reason I feel like that's not ideal. I feel like trying to closely match the screen space volume the UI takes up on all screen resolutions is ideal but maybe it's just not possible.

 

What is res.x & res.y? Resolution x & y would be 0's wouldn't they or are you assuming the positioning was done before this code and res.x and res.y represent the top-center point?

so if you place a 0.5 units wide and 0.1 unit high healthbar 0.05 units down and 0.25 units to the left (to compensate for its width) from the top-center you just draw it at:

x = res.x / 2 + (-0.25 * res.y) //not a typo, we always multiply with the vertical resolution, the horizontal resolution only affects the origin position
y = 0 + (0.05 * res.y) 

So the size formula in the above situation would be: width = .5 * res.width & .1 * res.width?

 

So basically all controls have to be given a starting point of either the edges, corners, or center and then these units are offsets from that?


Edited by rpiller, 25 October 2013 - 08:07 AM.


#10 Waterlimon   Crossbones+   -  Reputation: 2638

Like
0Likes
Like

Posted 25 October 2013 - 08:06 AM

One approach is to use both scale and pixel for positioning and size.

 

So position would be

(XScale, XPixels, YScale, YPixels)

 

So you may set XScale to 1 (assuming it goes from 0-1), and XPixels to -100 to add a gui element 100 pixels away from the RIGHT side of the screen.

 

You can also incorporate the relative-to-one-screen-dimension thing on top, so scale would be relative to either X or Y size of the screen only instead of X corresponding to screen X size and Y to Y size.


o3o


#11 haegarr   Crossbones+   -  Reputation: 4587

Like
1Likes
Like

Posted 25 October 2013 - 08:16 AM

If you want positions, spacing and sizes look the same regardless of display aspect ratio, then there is definitely just one way: letterbox / pillarbox (perhaps covered with some background, but not with UI elements). As soon as you want the recognized density of UI elements over the entire screen being independent on the display aspect ratio, you need to free one or more of position, spacing, and/or size from above restriction.

 

There are 2 situations to look at: (1) a HUD during the gameplay time, and (2) a general GUI ("menu screen").

 

For (1) it is usually so that the "logical" locations center of screen, edges, and corners give a good anchor for placement.

 

IMHO, the most satisfying solution for (2) is to define a mean aspect ratio from the targeted platforms, and use that at the design choice. When running on a target platform, fit the designed size into the available display and scale both the positions and sizes accordingly to the factor calculated from fitting (this is one factor for both dimensions). To avoid the nasty black margins of letter-/pillarbox, just use an oversized background so that all pixels are covered. Doing so, on a 16:9 display there are small GUI-less areas left and right, and on a 4:3 display they are at the top and bottom.



#12 SimonForsman   Crossbones+   -  Reputation: 6305

Like
0Likes
Like

Posted 25 October 2013 - 08:19 AM

OK, I'll give this a shot. I have to see it in action and the same settings for different resolutions as I can't picture how it would exactly look in terms of size (I get position) in my head. I get that you'd get more clean space but for some reason I feel like that's not ideal. I feel like trying to closely match the screen space volume the UI takes up on all screen resolutions is ideal but maybe it's just not possible.

 

What is res.x & res.y? Resolution x & y would be 0's wouldn't they or are you assuming the positioning was done before this code and res.x and res.y represent the top-center point?

so if you place a 0.5 units wide and 0.1 unit high healthbar 0.05 units down and 0.25 units to the left (to compensate for its width) from the top-center you just draw it at:

x = res.x / 2 + (-0.25 * res.y) //not a typo, we always multiply with the vertical resolution, the horizontal resolution only affects the origin position
y = 0 + (0.05 * res.y) 

So the size formula in the above situation would be: width = .5 * res.width & .1 * res.width?

 

So basically all controls have to be given a starting point of either the edges, corners, or center and then these units are offsets from that?

 

res.x = resolution on the x axis (or resolution width).

 

as for the size formula: multiply by height rather than width (otherwise things get bigger on a widescreen)

you could also multiply by whichever of width and height is smallest to handle vertical displays as well. (otherwise objects would get very big on vertical displays which you probably don't want)


Edited by SimonForsman, 25 October 2013 - 08:21 AM.

I don't suffer from insanity, I'm enjoying every minute of it.
The voices in my head may not be real, but they have some good ideas!

#13 rpiller   Members   -  Reputation: 706

Like
0Likes
Like

Posted 25 October 2013 - 08:57 AM

So the way you get top-center in the equation is:

 

(res.x / 2) center in the x formula and the 0 in the y formula for top. So in my designer I'll have to make like a dropdown to select which the control should be anchored to.

 

Good call on the size formula. I guess at startup (or orientation change event for mobile) I'll calculate which one to use.



#14 Eck   Crossbones+   -  Reputation: 3343

Like
0Likes
Like

Posted 25 October 2013 - 09:15 AM

Very well said haegarr. +1

 

rpiller, you can't have it both ways, Stretch to fit, but with proper aspect ratio. If you could, then DVD's wouldn't have the display options Letter Box, Stretch to Fit, Zoom and Pan. They's just have "do it right without stretching, letterboxes, or cropping the display".  As soon as you start dealing with multiple aspect ratios, you have to decide which is most important to you. 

 

Let's take an extreme example with numbers our brains can easily process. Let's say we have two screen resolutions.

 

Resolution 1: 

100 width x 50 height

And there is one gui element, a red X in the 50 x 50 square, filling up the left half of the screen.

 

Resolution 2:

400 width x 50 height

And there is one gui element, a red X that "looks the same" as Resolution 1

 

How do you want that Red X drawn in Resolution 2?

* Stretch to fit, it should cover the left half. X will look more like ><

* Letter Box, so that the X is drawn in a size 50 x 50. Stays looking like an X, but we have left over screen real estate

 * Fill in leftovers with black (DVD players do this)

 * Fill in leftovers with a bigger background image.

 * Fill in leftovers with a wider camera of your game world (this is what World of Warcraft does)

* Zoom and Pan - probably not a good option for most games

 

 



#15 l0calh05t   Members   -  Reputation: 816

Like
0Likes
Like

Posted 25 October 2013 - 11:31 AM

Have you considered positioning items relative to fixed anchors (bottom left, left center, top left, top center, etc.) and either

a) using distances/lengths defined relative to the minimum of the width and height of the screen

b) using area percentages instead of length percentages

You could also use the width/height and anchors defined by the appropriate safe areas for these


Edited by l0calh05t, 25 October 2013 - 11:36 AM.


#16 rpiller   Members   -  Reputation: 706

Like
0Likes
Like

Posted 28 October 2013 - 08:52 PM

I'm using SimonForsman's method but I'm converting from pixels to units and units to pixels in my GUI editor. When I use TopLeft for the anchor the formulas he has to convert units to pixels works. The functionality I want is when the user changes the anchor I want the control to stay where it is, but recalc the units using the new anchor values in the formula. Below is what I have in my calc for anchor values but when I change the anchor the control doesn't get the right new units. What am I messing up with this?

 

switch (Anchor)
            {
                case Anchors.TopLeft:
                    p.X = 0;
                    p.Y = 0;
                    break;
                case Anchors.TopCenter:
                    p.X = (res.Width / 2);
                    p.Y = 0;
                    break;
                case Anchors.TopRight:
                    p.X = res.Width;
                    p.Y = 0;
                    break;
                case Anchors.RightCenter:
                    p.X = res.Width;
                    p.Y = (res.Height / 2);
                    break;
                case Anchors.BottomRight:
                    p.X = res.Width;
                    p.Y = res.Height;
                    break;
                case Anchors.BottomCenter:
                    p.X = (res.Width / 2);
                    p.Y = res.Height;
                    break;
                case Anchors.BottomLeft:
                    p.X = 0;
                    p.Y = res.Height;
                    break;
                case Anchors.LeftCenter:
                    p.X = 0;
                    p.Y = (res.Height / 2);
                    break;
                case Anchors.Center:
                    p.X = (res.Width / 2);
                    p.Y = (res.Height / 2);
                    break;
            }


#17 haegarr   Crossbones+   -  Reputation: 4587

Like
0Likes
Like

Posted 29 October 2013 - 02:51 AM


When I use TopLeft for the anchor the formulas he has to convert units to pixels works. The functionality I want is when the user changes the anchor I want the control to stay where it is, but recalc the units using the new anchor values in the formula.

Using the editors screen as reference, an absolute pixel position is

   pp = ( x, y )

 

When computing the absolute position from the relative position pr, the relative position is scaled by the vertical resolution rv in both dimensions, and the absolute position of the anchor pa is added:

   pp := pa + pr * rv

 

Because you're interested in the relative position for a given absolute position, you need to solve for that variable. Hence:

   pr = ( pp - pa ) / rv

Notice that the anchor position pa herein is those of the newly picked anchor, because you want to calculate the result relative to those.



#18 rpiller   Members   -  Reputation: 706

Like
0Likes
Like

Posted 29 October 2013 - 05:27 AM

So  pr = ( pp - pa ) / rv is the equation I'm using to get the (I call x,y unit):

         // this is called from the editor when a control is placed. we pass in the actual pixel x and convert to "units"
        private float _SetPixelX(int x, Size res)
        {
            return (float)(x - GetAnchorPoint(res).X) / (float)res.Height;
        }

To convert from units back to pixels for drawing I do the following (which works because it draws fine with default TopLeft anchor):

        protected int XUnitsToPixels(Size res)
        {
            return GetAnchorPoint(res).X + (int)(X * res.Height);
        }

When the user changes the anchor, I convert units (which is what I store) back to pixels using the above function, then using the new anchor values I convert the pixels back to the new units, but it doesn't work using a different anchor. The units end up all over the place.

 

 

[EDIT]

Actually initially it works for any anchor. It's when I change anchors that it doesn't end up being the same position after recalcing the units with the new anchor.

 

inside my Control class

        private Anchors anchor;
        public Anchors Anchor 
        {
            get { return anchor; }
            set
            {
                Size res = new Size();
 
                anchor = value;
 
                // get the canvas resolution so we can recalc position
                if (OnAnchorChanged != null)
                    res = OnAnchorChanged(this);
 
                // recalc the pos units to keep the widget in the same location given this new anchor selection
                int _x = XUnitsToPixels(res);
                int _y = YUnitsToPixels(res);
 
               // x & y are member variables that are in units
                x = _SetPixelX(_x, res);
                y = _SetPixelY(_y, res);
 
                // need to call this to show the changes by making the canvas redraw the controls
                if (OnInvalidated != null)
                    OnInvalidated(this);
            }
        }

Edited by rpiller, 29 October 2013 - 07:37 AM.


#19 haegarr   Crossbones+   -  Reputation: 4587

Like
0Likes
Like

Posted 29 October 2013 - 08:25 AM

If I understand your code snippet correctly, then this part

                anchor = value;

                // get the canvas resolution so we can recalc position
                if (OnAnchorChanged != null)
                    res = OnAnchorChanged(this);

                // recalc the pos units to keep the widget in the same location given this new anchor selection
                int _x = XUnitsToPixels(res);
                int _y = YUnitsToPixels(res);

               // x & y are member variables that are in units
                x = _SetPixelX(_x, res);
                y = _SetPixelY(_y, res);

should instead look like this

                // get the canvas resolution so we can recalc position
                if (OnAnchorChanged != null)
                    res = OnAnchorChanged(this);

                // recalc the pos units to keep the widget in the same location given this new anchor selection
                int _x = XUnitsToPixels(res);
                int _y = YUnitsToPixels(res);

                anchor = value;

               // x & y are member variables that are in units
                x = _SetPixelX(_x, res);
                y = _SetPixelY(_y, res);

in order to ensure that at the moment where the pixel position is calculated the now obsolete anchor is used. Just after that the anchor is allowed to be switched to the new one, and that one has to be used to re-calculate the new relative position.

 

However, make sure that an initial anchor is correctly set-up, or else the first invocation of the setter will fail.


Edited by haegarr, 29 October 2013 - 08:26 AM.


#20 rpiller   Members   -  Reputation: 706

Like
0Likes
Like

Posted 29 October 2013 - 10:00 AM

Oh man how did I miss that :) You are correct! Thank you for helping me with that. It was driving me crazy as I was pretty sure my formulas were right.






Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS