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