Irrlicht学习之创建GUI界面
Irrlicht游戏引擎自带了GUI模块,可以在三维的场景中创建二维的覆盖(Overlay),使得游戏中图标和文字的显示更为方便,配合游戏高效的三维渲染能力,相得益彰。
这一次我将尝试用代码对GUI进行显示,我们的目标是在窗口中既要显示三维的背景也要显示二维的界面菜单,想做得和DirectX的例子CustomUI那样,但也不要制作得那么复杂,符合游戏的实际情况即可。
演示程序和源代码下载地址:这里
下面就是例子程序的截图:
例子中使用Irrlicht的IGUIEnvironment*类型作为渲染GUI控件的基础,在此基础上显示玩家名称、玩家头像、HP和MP条以及背包按钮。由背包按钮可以触发Equipment对话框,下面有三个选项。背景中使用的是Quake3的地图;创建了一个可以绕着Y轴旋转的旋转摄像机。
下面就是程序代码。程序也比较简单,没有考虑多次点击Backpack按钮出现的多个窗口的情况,但程序可以说明我们能用Irrlicht创建一个简单的GUI界面。顺便说一下,Irrlicht中没有进度条控件!要是创建血槽什么的还得自己绘制了,这多多少少给我们添加了麻烦。
开发环境:
Windows:Windows8 + msvc2008+ Visual Studio2008 + Irrlicht1.7.3
Linux:Ubuntu13.10 + gcc4.8+ Qt Creator2.8 + Irrlicht1.7.3
#include <irrlicht.h> // 针对Windows系统的配置 #ifdef _IRR_WINDOWS_ #pragma comment( lib, "Irrlicht.lib" ) #pragma comment( linker, "/subsystem:windows /ENTRY:mainCRTStartup" ) #endif // 定义空指针 #ifndef nullptr #define nullptr 0 #endif using namespace irr; using namespace core; using namespace video; using namespace scene; using namespace gui; using namespace io; enum { ID_BACKPACK_BUTTON = 101 }; class RotateYCamera// 沿着Y轴旋转的FPS摄像机 { public: RotateYCamera( ISceneManager* pSceneManager, const vector3df& pos, const vector3df& view ) { m_pCamera = pSceneManager->addCameraSceneNode( nullptr, pos, view ); m_pCamera->setTarget( view ); m_pCamera->bindTargetAndRotation( true ); m_ViewLength = view.getLength( ); m_RotateAngle = 0.0f; } void Rotate( f32 rotateSpeed ) { m_pCamera->setTarget( vector3df( m_ViewLength * cos( m_RotateAngle ), 0.0f, m_ViewLength * sin( m_RotateAngle ) ) ); m_RotateAngle += rotateSpeed; if ( m_RotateAngle > PI * 2.0f ) m_RotateAngle = 0.0f; } private: f32 m_ViewLength; f32 m_RotateAngle; ICameraSceneNode* m_pCamera; }; void CreateMap( IrrlichtDevice* pDevice ) { ISceneManager* pSMgr = pDevice->getSceneManager( ); // 获取Quake3的地图 pDevice->getFileSystem( )->addZipFileArchive( "map-20kdm2.pk3" ); IAnimatedMesh* pMesh = pSMgr->getMesh( "20kdm2.bsp" ); ISceneNode* pNode = nullptr; if ( pMesh != nullptr )// 添加八叉树渲染场景节点 { pNode = pSMgr->addOctreeSceneNode( pMesh->getMesh( 0 ) ); } if ( pNode != nullptr )// 位置偏移修正 { pNode->setPosition( vector3df( -1300, -144, -1249 ) ); } } class ProgressBar// 进度条控件 { public: ProgressBar( IGUIEnvironment* pEnv, const position2di& pos, const path& fileName ): m_Pos( pos ) { ITexture* pTexture = pEnv->getVideoDriver( )->getTexture( fileName ); m_Size = pTexture->getSize( ); m_pImage = pEnv->addImage( rect<s32>( m_Pos, m_Size ), nullptr ); m_pImage->setImage( pTexture ); } void SetProgress( f32 progress ) { s32 lower = m_Pos.Y + m_Size.Height; s32 right = s32( f32( m_Size.Width ) * progress ) + m_Pos.X; m_pImage->setRelativePosition( rect<s32>( m_Pos.X, m_Pos.Y, right, lower ) ); } private: IGUIImage* m_pImage; position2di m_Pos; dimension2di m_Size; }; class GUIFontCommand// 用于渲染字体的命令 { public: GUIFontCommand( IGUIFont* pFont, stringw text, rect<s32> rect, SColor color ): m_pFont( pFont ), m_Text( text ), m_Rect( rect ), m_Color( color ) { } inline void Draw( void ) { m_pFont->draw( m_Text, m_Rect, m_Color ); } private: IGUIFont* m_pFont; stringw m_Text; rect<s32> m_Rect; SColor m_Color; }; typedef list<GUIFontCommand> GUIFontCommandList; void CreateGUI( IrrlichtDevice* pDevice, GUIFontCommandList& cmdList ) { IGUIEnvironment* pGUIEnv = pDevice->getGUIEnvironment( ); IVideoDriver* pDriver = pDevice->getVideoDriver( ); IGUIFont* pFont = pGUIEnv->getFont( "SAO.xml" );// 字体高为12像素 // 添加字体 s32 x = 120, y = 5; cmdList.push_back( GUIFontCommand( pFont, L"Jiangcaiyang", rect<s32>( x, y, x + 10, y += 15 ), SColor( 255, 255, 255, 255 ) ) ); y += 5; cmdList.push_back( GUIFontCommand( pFont, L"HP", rect<s32>( x, y, x + 10, y += 15 ), SColor( 255, 255, 255, 255 ) ) ); cmdList.push_back( GUIFontCommand( pFont, L"MP", rect<s32>( x, y, x + 10, y += 15 ), SColor( 255, 255, 255, 255 ) ) ); // 添加玩家头像 pGUIEnv->addImage( pDriver->getTexture( "PlayerAvatar.png" ), vector2d<s32>( 10, 10 ) ); // 添加HP条和MP条 ProgressBar HPBar( pGUIEnv, position2di( 140, 44 ), L"HPBar.png" ); ProgressBar MPBar( pGUIEnv, position2di( 140, 59 ), L"MPBar.png" ); // 添加背包按钮控件 ITexture* pBackpackTex = pDriver->getTexture( L"Backpack.png" ); position2di backpackPos( 550, 400 ); dimension2di backpackSize( 32, 32 ); if ( pBackpackTex != nullptr ) { backpackSize = pBackpackTex->getSize( ); } IGUIButton* pBackpackButton = pGUIEnv->addButton( rect<s32>( backpackPos, backpackSize ), nullptr, ID_BACKPACK_BUTTON ); pBackpackButton->setUseAlphaChannel( true ); pBackpackButton->setDrawBorder( false ); pBackpackButton->setImage( pBackpackTex ); cmdList.push_back( GUIFontCommand(// 添加Backpack文字说明 pFont, L"Backpack", rect<s32>( 545, 430, 580, 450 ), SColor( 255, 255, 255, 255 ) ) ); // 添加一些图片 } class EventReceiver: public IEventReceiver { public: EventReceiver( IGUIEnvironment* pEnv ): m_pEnv( pEnv ) { } virtual bool OnEvent( const SEvent& event ) { if ( event.EventType == EET_GUI_EVENT ) { s32 id = event.GUIEvent.Caller->getID( ); switch ( event.GUIEvent.EventType ) { case EGET_BUTTON_CLICKED: switch ( id ) { case ID_BACKPACK_BUTTON: { IGUIWindow* pWindow = m_pEnv->addWindow( rect<s32>( 300, 200, 500, 300 ) ); pWindow->setText( L"Equipment" ); IGUIListBox* pList = m_pEnv->addListBox( rect<s32>( 1, 20, 196, 100 ), pWindow ); pList->addItem( L"Golden sword" ); pList->addItem( L"Magician's cloak" ); pList->addItem( L"Revive water" ); return true; } default: break; } default: break; } } return false; } private: IGUIEnvironment* m_pEnv; }; int main( int argc, char** argv ) { IrrlichtDevice* pDevice = createDevice( EDT_OPENGL, // 渲染设备 dimension2d<u32>( 640, 480 ), // 宽和高 16, // 颜色位 false, // 是否全屏 false, // 模版缓存 false, // 垂直同步 nullptr ); // 事件接收器 if ( pDevice == nullptr ) return -1; pDevice->setWindowCaption( L"鬼火引擎演示 - GUI的使用" ); IVideoDriver* pDriver = pDevice->getVideoDriver( ); ISceneManager* pSMgr = pDevice->getSceneManager( ); IGUIEnvironment* pGUIEnv = pDevice->getGUIEnvironment( ); GUIFontCommandList cmdList; CreateMap( pDevice ); CreateGUI( pDevice, cmdList ); EventReceiver receiver( pGUIEnv ); pDevice->setEventReceiver( &receiver ); // 创建一FPS摄像机 RotateYCamera camera( pSMgr, vector3df( 0.0f, 0.0f, 0.0f ), vector3df( 5.0f, 0.0f, 5.0f ) ); u32 then = pDevice->getTimer( )->getTime( ); while ( pDevice->run( ) ) { pDriver->beginScene( true, // 后台缓存 true, // Z缓存 SColor( 255, 255, 255, 255 ) ); // 填充颜色 pSMgr->drawAll( ); pGUIEnv->drawAll( ); for ( GUIFontCommandList::Iterator iter = cmdList.begin( ); iter != cmdList.end( ); iter++ ) { iter->Draw( ); } pDriver->endScene( ); // 获取时间增量 const u32 now = pDevice->getTimer( )->getTime( ); const f32 frameDeltaTime = (f32)( now - then ); then = now; camera.Rotate( frameDeltaTime * 0.0001f );// 摄像机旋转 } return 0; }