Howto: Resolution Independent GUIs
Posted: Thu Jan 15, 2004 4:57 pm
Howto: Resolution Independent GUIs
I have a co-worker that hates GUIs. He prefers Text command shells more than any fancy interface. I don't hate GUIs, I just hate games that don't think about their GUIs at different resolutions.
I have played many AAA titles that have GUI's that are unreadable at any resolution over 800x600 because they are so small. Even worse, other games "work around" this limitation by locking you to either 640x480, 800x600, or 1024x768. In this day and age where most LCD monitors have a native resolution of 1280x1024 and many normal users have 19" monitor with the resolution set to 1280x1024 or 1600x1200, developers should strive to allow the player to set the game to whatever resolution they desire and share a similiar experience. (Of course, the 3D at higher resolutions will look better.)
Lets start out by looking at the most commonly used resolutions.
I consider 1280x1024 as special because it doesn't follow similiar scaling rules as all of the other resolutions. For some reason, it is mostly used by games published in the US, while to more approriate 1280x960 comes from the Old World countries. Us crazy Americans and our inches instead of meters.
Anyhow, at this point, I quickly generated a GUI class that wraps the Irrlicht scenemanager functions to handle any scaling needed on the GUI level. This allows me to concentrate my mind on one resolution for positioning elements and everything will look the same at any resolution.
GUIHandler.h
GUIHandler.cpp
Here, we have a simple helper class that will scale GUI objects based on a resolution. In the constructor, the scale for the height and width is set in reference to a base 640x480 resolution. If you plan to make your minimal resolution higher, just change those values. In my actual code, I set 800x600 to be the minimum base and everything scales off of that.
This class only has one function at the moment which is to Scale a Value by the Screen's Height. All it does is take a value and apply the proper scaling. It is just an example and will not be used in the rest of the class.
At this point, functions need to be added to wrap all of the Irrlicht GUI functions to adjust them all for the scaling. The two points to remember to scale is the Position and the Size of the elements.
For example, lets make a wrapper for the creation of a scrollbar.
Note that both the Position and Height were multiplied by the scale. So all that would need to be done in the main application is to call createScrollBar() with the intended size and position at your base resolution.
We can then take this one step further and put some good design practices into play. When I code, I have a defines include that has all of my defines in it so that I can tweak settings there and have every class pick it up. This allows me to have a common place so that I can change one variable and make all the elements based off of it change.
This time, we can extend createScrollBar() into a new function called createSpecificToolbar(). From this, I can call the function to make a specific ToolBar type that I have created the defines for. Note that I no longer include the size.
The defines file
Of course, you can change SPECIFIC to be the name of the type of Element you are making like: STATBAR_HEIGHT, GUNSELECTOR_WIDTH...
I have a co-worker that hates GUIs. He prefers Text command shells more than any fancy interface. I don't hate GUIs, I just hate games that don't think about their GUIs at different resolutions.
I have played many AAA titles that have GUI's that are unreadable at any resolution over 800x600 because they are so small. Even worse, other games "work around" this limitation by locking you to either 640x480, 800x600, or 1024x768. In this day and age where most LCD monitors have a native resolution of 1280x1024 and many normal users have 19" monitor with the resolution set to 1280x1024 or 1600x1200, developers should strive to allow the player to set the game to whatever resolution they desire and share a similiar experience. (Of course, the 3D at higher resolutions will look better.)
Lets start out by looking at the most commonly used resolutions.
Code: Select all
Resolution Ratio(to 640x480)
640x480 1:1
800x600 1.25:1
1024x768 1.6:1
1280x960 2:1
1600x1200 2.5:1
Special
1280x1024 2:1 / 2.1333~:1
Anyhow, at this point, I quickly generated a GUI class that wraps the Irrlicht scenemanager functions to handle any scaling needed on the GUI level. This allows me to concentrate my mind on one resolution for positioning elements and everything will look the same at any resolution.
GUIHandler.h
Code: Select all
#ifndef __C_GUIHANDLER_H_INCLUDED__
#define __C_GUIHANDLER_H_INCLUDED__
#include <irrlicht.h>
class GUIHandler {
public:
GUIHandler(irr::scene::ISceneManager* iSmgr);
~GUIHandler();
irr::f32 GUIHandler::ScaleValuebyScreenHeight(irr::f32 iValue);
private:
irr::scene::ISceneManager* smgr;
irr::video::IVideoDriver* driver;
irr::dimension2d<irr::f32> scale;
};
#endif
Code: Select all
#include "GUIHandler.h"
/*----------------------------------------------------------------------------------------------
Constructor
----------------------------------------------------------------------------------------------*/
GUIHandler::GUIHandler(irr::scene::ISceneManager* iSmgr) {
smgr = iSmgr;
driver = smgr->getVideoDriver()
scale.Height = driver->getScreenSize().Height / 640;
scale.Width = driver->getScreenSize().Width / 480;
}
/*----------------------------------------------------------------------------------------------
Destructor
----------------------------------------------------------------------------------------------*/
GUIHandler::~GUIHandler() {
}
/*----------------------------------------------------------------------------------------------
Scale a Variable
----------------------------------------------------------------------------------------------*/
irr::u32 GUIHandler::ScaleValuebyScreenHeight(irr::u32 iValue) {
return (irr::u32)(iValue * scale.Height);
}
This class only has one function at the moment which is to Scale a Value by the Screen's Height. All it does is take a value and apply the proper scaling. It is just an example and will not be used in the rest of the class.
At this point, functions need to be added to wrap all of the Irrlicht GUI functions to adjust them all for the scaling. The two points to remember to scale is the Position and the Size of the elements.
For example, lets make a wrapper for the creation of a scrollbar.
Code: Select all
/*----------------------------------------------------------------------------------------------
Create a Scroll Bar
----------------------------------------------------------------------------------------------*/
irr::IGUIScrollBar* GUIHandler::createScrollBar(irr::dimension<irr::u32> Position; irr::dimension<irr::u32> Size, irr::IGUIElement* Parent = 0, irr::s32 id = -1;) {
irr::IGUIScrollBar* tempBar = smgr->addScrollBar(
true,
irr::core<irr::s32>(
Size.Width*scale.Width,
Size.Height*scale.Height),
Parent,
id);
tempBar->setPosition(
irr::core<irr::s32>(
Position.Width*scale.Width,
Position.Height*scale.Height));
return tempBar;
}
We can then take this one step further and put some good design practices into play. When I code, I have a defines include that has all of my defines in it so that I can tweak settings there and have every class pick it up. This allows me to have a common place so that I can change one variable and make all the elements based off of it change.
This time, we can extend createScrollBar() into a new function called createSpecificToolbar(). From this, I can call the function to make a specific ToolBar type that I have created the defines for. Note that I no longer include the size.
Code: Select all
/* At the top of the class */
include "defines.h"
/* In the class */
/*----------------------------------------------------------------------------------------------
Create a Specific Type of Scroll Bar
----------------------------------------------------------------------------------------------*/
irr::IGUIScrollBar* GUIHandler::createSpecificBar(irr::dimension<irr::u32> Position; irr::IGUIElement* Parent = 0, irr::s32 id = -1;) {
irr::IGUIScrollBar* tempBar = smgr->addScrollBar(
true,
irr::core<irr::s32>(
SPECIFICSCROLLBAR_WIDTH*scale.Width,
SPECIFICSCROLLBAR_HEIGHT*scale.Height),
Parent,
id);
tempBar->setPosition(
irr::core<irr::s32>(
Position.Width*scale.Width,
Position.Height*scale.Height));
return tempBar;
}
Code: Select all
//!GUI Elements
#define SPECIFICSCROLLBAR_HEIGHT 20
#define SPECIFICSCROLLBAR_WIDTH 200