FOV, Aspect Ratio and HUDs – ShiVa Engine

FOV, Aspect Ratio and HUDs

Developing cross platform games confronts you with a large number of operating systems, processor architectures, graphics APIs and input methods. But don't forget about all the different screen sizes, orientations and aspect ratios, which you need to take into account for FOV and HUD design!

Field of View

Let's be gin by defining the term FOV. Wikipedia tells us,

In first person video games, the field of view or field of vision (abbreviated FOV) is the extent of the observable game world that is seen on the display at any given moment. It is typically measured as an angle, although whether this angle is the horizontal, vertical, or diagonal component of the field of view varies from game to game.

The FOV in a video game may change depending on the aspect ratio of the rendering resolution. In computer games and modern game consoles the FOV normally increases with a wider aspect ratio of the rendering resolution.

In the 80s and 90s, computer screens were mostly in 4:3. When 16:9 widescreen became more and more widespread in the early 2000s, many games did not take kindly to the new aspect ratio and were either pillarboxed or stretched. The pillarbox effect occurs in widescreen video displays when black bars are placed on the sides of the image. The original material is shrunk and placed in the middle of the widescreen frame. Stretching on the other hand leads to image distortions. Squares become oblongs and circles become ellipses. The following image shows pillarboxing and stretching of 4:3 content onto 16:9 in CS GO:

Nowadays, Widescreen 16:9 has become the de-factor standard for hoe entertainment and desktop computers, while 4:3 can still be found on older devices, 5:4 dominates the office space and 21:9 becomes more and more popular. To support this wide range of aspect ratios, game developers had to put adjustable FOV into their games for years now. Typically, this FOV is measured horizontally. By this method, a widescreen has a larger field of view than a 4:3 screen. If the user does not adjust the horizontal FOV, the game will appear to "zoom in". Look how the ship moves back into the distance with a higher FOV.

Of course you can get the same effect by manipulating the FOV in ShiVa.

Note how in addition to the zoom effect your sense of depth also changes. Wider FOVs make objects look more distant to each other, and narrower FOVs feel more crowded.

Gamers often use unrealistically large FOVs to gain an advantage, since they can see so much more of the environment. This comes at the cost of severe fisheye image distortion which makes a lot of people sick.

FOV in ShiVa

Unlike many other game engines, ShiVa's Field of View calculations are based on the the vertical FOV rather than the horizontal FOV. This usually referred to as "Hor+" Mode, which means that the game will not suffer any distortions going from a more square screen like 5:4 or 4:3 to 16:9 or 21:9.

Gaming at wider aspect ratios in Hor+ can give you a distinctive advantage, since you can see a lot more of the scene vertically. Note how the car and sign disappear in 16:9.

To calculate the proper FOV for a screen, you can use the following formula. Make sure your units match, do not mix inches, feet and meters.

  1. _FOV = math.abs ( math.atan ( ( math.sqrt ( math.pow ( nScreenDiameter, 2 ) / ( math.pow ( nMonitorAspectRatio, 2 ) + 1 ) ) ) / ( 2 * nDistanceToScreen ) ) )

HUD anchors and Aspect Ratio

The placement of HUDs requires some thought with different aspect ratios. You can quickly make the mistake of anchoring vital information to the screen corners, like in this Battlefield screenshot.

The area of focus for player is the middle of the screen. To see the map and important stats, bad anchoring forces the player to move their head and eyes away from the center of the screen, taking their attention away in addition to creating neck fatigue.

A much better solution offer shooters like Quake Champions, where the HUD is aligned around a virtual 4:3 or 16:9 monitor in the middle, with the sides rendering the extended field of view.

ShiVa's "keep aspect ratio" does exactly that. A centered 100x100 component in aspect ratio independent mode is a square in the middle of the screen that stays there no matter how wide the screen gets.

Bot that's not to say that there is no use case for corner anchoring, just don't use it to display vital player stats. However, it is the perfect setting for things like touch joysticks, where you get to see a lot more of the game world by moving the input controls as far to the side as possible.

Keep Aspect Ratio and Portrait Mode

Keeping the aspect ration is a problem however with portrait orientations where the aspect ratio is smaller than 1. Since ShiVa does not know whether and in which way to scale your components, they simply get cut off.

A similar problem affects components that are anchored to a corner. If the screen becomes too narrow, like in portrait mode of a phone, these components can overlap, as shown here with the life bar and score counter in The Hunt.

Of course, HUDs can both overlap and be cut off at the same time if you have centered components that are too large.

The answer to these issues is containers.

Parent Containers

Containers are HUD elements which can be used to group elements that belong together logically, like a row of buttons. That makes it particularly easy to move these elements around all at once, scale them, animate/show/hide them and so forth.

However 100x100 containers can also be used to provide a reference frame which can be used to dynamically adjust child components. Let's put our button bar from before into a parent container. The bar itself is aspect ratio independent, however the container is must not be.

The container is also anchored to the bottom of the screen, so ShiVa knows where to scale the container towards. The scaling itself must be handled in a short bit of code. First, you have to detect whether the aspect ratio has changed. You can run a small routine like that in onEnterFrame:

  1. --------------------------------------------------------------------------------
  2. function scaleme.onEnterFrame ( )
  3. --------------------------------------------------------------------------------
  4.  
  5. local w = application.getCurrentUserViewportWidth ( )
  6. local h = application.getCurrentUserViewportHeight ( )
  7.  
  8. -- limit to 1
  9. local r = math.min ( w/h, 1 )
  10.  
  11. -- this._ar() stores the previous result
  12. if r ~= this._ar ( ) then
  13. user.sendEvent ( this.getUser ( ), "scaleme", "onARChanged", r )
  14. end
  15.  
  16. --------------------------------------------------------------------------------
  17. end
  18. --------------------------------------------------------------------------------

The onARChanged event handles the actual scaling. Since we only have one parent container, we only have one scaling operation to take care of, however if you have multiple anchored groups, like a top scale container or two for the left and right side, you would need to scale every parent container individually.

  1. --------------------------------------------------------------------------------
  2. function scaleme.onARChanged ( r )
  3. --------------------------------------------------------------------------------
  4.  
  5. local c = hud.getComponent ( this.getUser ( ), "sm.scaleBox" )
  6. if c == nil then return end
  7.  
  8. hud.setComponentSize ( c, 100, 100*r )
  9.  
  10. this._ar ( r )
  11. log.message ( "rescaled to " ..r )
  12.  
  13. --------------------------------------------------------------------------------
  14. end
  15. --------------------------------------------------------------------------------

To illustrate the result, I have chosen a green background color for the parent container. At aspect ratios bigger than 1, it fills the entire screen, while the child button bar stays aspect ratio independent.

With aspect ratios smaller than 1 however, the container dynamically scales down to the bottom of the screen, adjusting the scale of the children in the process.

Checklist

To summarize, which methods should you choose, and when?

- never design for a single aspect ratio
- no need to manually adjust your FOV for desktop monitors with aspect ratios >1
- display vital information in the center around a virtual square
- group elements that belong together logically
- anchor touch controls and non-vital info to the sides and corners of the screen
- be mindful of cutoffs and overlaps
- for portrait mode, make use of parent containers and scale them as needed