Sign in to follow this  
rpiller

GUI aspect ratio independence

Recommended Posts

rpiller    839

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?

Share this post


Link to post
Share on other sites
Xai    1838

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.

Share this post


Link to post
Share on other sites
Xai    1838

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.

Share this post


Link to post
Share on other sites
SimonForsman    7642

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

Share this post


Link to post
Share on other sites
rpiller    839

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

Share this post


Link to post
Share on other sites
SimonForsman    7642

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

Share this post


Link to post
Share on other sites
rpiller    839

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

Share this post


Link to post
Share on other sites
SimonForsman    7642

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

Share this post


Link to post
Share on other sites
rpiller    839

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

Share this post


Link to post
Share on other sites
Waterlimon    4398

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.

Share this post


Link to post
Share on other sites
haegarr    7372

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.

Share this post


Link to post
Share on other sites
SimonForsman    7642

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

Share this post


Link to post
Share on other sites
rpiller    839

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.

Share this post


Link to post
Share on other sites
Eck    7581

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

 

 

Share this post


Link to post
Share on other sites
l0calh05t    1796

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

Share this post


Link to post
Share on other sites
rpiller    839

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;
            }

Share this post


Link to post
Share on other sites
haegarr    7372


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.

Share this post


Link to post
Share on other sites
rpiller    839

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

Share this post


Link to post
Share on other sites
haegarr    7372

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

Share this post


Link to post
Share on other sites
rpiller    839

Anyone have ideas on how to handle dynamic text controls (like a label control) to get text to "scale"? The engine I'm using uses TTF where you specify the font size. Since scaling fonts generally look horrible I don't think drawing to bitmap then scaling would work very well. Any algo to find best font size to use based on control, height I guess it would be? As I'm typing this it almost seems like control height would maybe dictate the size of the font. Anything else to think about?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this