5. User Interface (+06.08.16)

Posted 2007/09/15 19:21 by 수달

-------------------------------------------------------------------------

튜토리얼 5 : 유저 인터페이스

--------------------------

이번 튜토리얼은 IRRLICHT 엔진의 유저 인터페이스를 만드는 방법을 보일 것이다. 게임 내 윈도우, 버튼, 스크롤 바, 텍스트(static text)와 리스트 박스를 어떻게 만드는지 간단히 설명하겠다. 프로그램은 다음과 같은 모습이 될 것이다.


-------------------------------------------------------------------------

시작

----

항상 그렇듯이 헤더 파일과 네임 스페이스를 부르자. iostream은 콘솔에서 사용자의 입력을 받기 위해서다. 또한 IRRLICHT 장치, 윈도의 새로운 위치 값을 위한 참조 변수(counter variable), 그리고 리스트 박스용 포인터를 확보해 두자.


#include <irrlicht.h>

#inlcude <iostream>


using namespace irr;


using namespace core;

using namespace scene;

using namespace video;

using namespace io;

using namespace gui;


#pragma comment(lib, "irrlicht.lib")


IrrlichtDevice * device = 0;

s32 cnt = 0;

IGUIListBox * listbox = 0;


이벤트 리시버(Event Receiver)는 키보드와 마우스 입력을 받는 것 뿐만 아니라, GUI(그래픽 유저 인터페이스)의 이벤트에도 사용된다. 버튼 클릭, 리스트 박스 선택 변화, 한 GUI 개체의 이동 등 거의 모든 발생 이벤트를 관리 한다. 이벤트 리시버는 어떤 이벤트 인지, 호출한 아이디는 뭔지, 현재 GUI가 어떤 상태인지 등을 알 수 있다.


class MyEventReceiver : public IEventReceiver

{

public:

virtual bool OnEvent(SEvent event)

{

+ GUI 이번트인지 확인해야 한다. 이는 9. MeshViewer에서 가져온 코드를 삽입하면된다. 이렇게 안하면 id를 제대로 읽지 못해 비정상 종료가 될 수 있다.

if (event.EventType == EET_GUI_EVENT) //<-- 이것 추가.

{

s32 id = event.GUIEvent.Caller->getID();

IGUIEnvironment * env = device->getGUIEnvironment();


switch(event.GUIEvent.EventType)

{


이 예에서 상단 스크롤바 위치에 따라 모든 GUI 요소들에 색상 변화가 일어난다. 이는 매우 쉬운 일로 각각의 색상을 기억하고 있는 스킨 객체가 있고, 우리는 이벤트(스크롤 변환)에 따라 저장된 스킨과 알파값만 꺼내 적용시켜 주면 된다.


case EGET_SCROLL_BAR_CHANGED:

if(id == 104)

{

s32 pos = ((IGUIScrollBar *) event.GUIEvent.Caller)->getPos();


for(s32 i = 0; i < EGDC_COUNT; ++i)

{

SColor col =

env->getSkin()->getColor((EGUI_DEFAULT_COLOR) i);

col.setAlpha(pos);

env->getSkin()->setColor((EGUI_DEFAULT_COLOR)i, col);

}

}

break;


만약 버튼이 클릭 되면, 예제에 만들어질 세개 버튼 중의 하나일 것이다. 첫번째 버튼은 엔진을 끄는 것이고, 두번째 버튼은 글이 쓰인 작은 창을 만들 것이며, 세번째 버튼은 '파일 열기 다이얼로그'를 생성할 것이다. 이때 두번째 버튼과 세번째 버튼의 경우 이벤트 발생에 대한 로그(log) 문자가 리스트 박스에 남게 될 것이다.


case EGET_BUTTON_CLICKED:

if(id == 101)

{

device->closeDevice();

return;

}

if(id == 102)

{

listbox->addItem(L"Window created");

cnt += 30;

if(cnt > 200) cnt = 0;


IGUIWindow * window =

env->addWindow(rect<s32>(100 + cnt, 100 + cnt,

300 + cnt, 200 + cnt),

false, // modal?

L"Test window");


env->addStaticText(L"Please close me", rect<s32>(35, 35, 140, 50),

true, //border?

false, //wordwrap?

window);

return true;

}

if(id == 103)

{

listbox->addItem(L"File open");

env->addFileOpenDialog(L"Please choose a File.");

return true;

}

break;

} //switch

} //<--

}

return false;

}

};


본격적으로 시작하자. 우선 IRRLICHT 장치를 만든다. 이전의 예처럼 사용자가 어떤 장치를 쓸지 물어 본다.


int main()

{

// ask user for driver


video::E_DRIVER_TYPE driverType;


printf("Please select the driver you want for this example:\n"\
" (a) Direct3D 9.0c\n (b) Direct3D 8.1\n (c) OpenGL 1.2\n"\
" (d) Software Renderer\n (e) NullDevice\n (otherKey) exit\n\n");


char i;
std::cin >> i;

switch(i)
{
case 'a': driverType = video::EDT_DIRECTX9; break;
case 'b': driverType = video::EDT_DIRECTX8; break;
case 'c': driverType = video::EDT_OPENGL; break;
case 'd': driverType = video::EDT_SOFTWARE; break;
case 'e': driverType = video::EDT_NULL; break;
default: return 1;
}


// create device and exit if creation failed

device = createDevice(driverType, core::dimension2d<s32>(640, 480));

if (device == 0)
return 1; // could not create selected driver.


디바이스 생성이 성공하면, 이벤트 리시버를 설정하고 드라이버와 GUI 관련 포인터를 저장해 두자.


MyEventReceiver receiver;
device->setEventReceiver(&receiver);
device->setWindowCaption(L"Irrlicht Engine - User Inferface Demo");

video::IVideoDriver* driver = device->getVideoDriver();
IGUIEnvironment* env = device->getGUIEnvironment();


버튼 세개를 더한다. 첫번째는 엔진을 종료하는 것이고, 두번째는 새 창 하나를 여는 것이고, 세번째는 파일 열기 다이얼로그를 부르는 것이다. addButton의 세번째 인자는 각 버튼의 ID이며, 이를 통해 쉽게 이벤트 리시버에서 각각을 식별하게 된다.


env->addButton(rect<s32>(10,210,100,240), 0, 101, L"Quit");
env->addButton(rect<s32>(10,250,100,290), 0, 102, L"New Window");
env->addButton(rect<s32>(10,300,100,340), 0, 103, L"File Open");


문자표시박스(static text)와 스크롤 바를 추가한다. 스크롤 바에는 모든 GUI를 변경시키는 기능을 붙인다. 스크롤 바의 최대 값으로 255를 정한다. 255인 이유는 색상 값의 최대치가 255이기 때문이다. 그런 후 또 하나의 문자표시박스와 리스트 박스를 만들자.


env->addStaticText(L"Transparent Control:", rect<s32>(150,20,350,40), true);
IGUIScrollBar* scrollbar =

env->addScrollBar(true, rect<s32>(150, 45, 350, 60), 0, 104);
scrollbar->setMax(255);

env->addStaticText(L"Logging ListBox:", rect<s32>(50,80,250,100), true);
listbox = env->addListBox(rect<s32>(50, 110, 250, 180));


좀 더 좋은 폰트를 만들기 위해 확장 폰트를 읽고 적용해 보자. 마지막으로 좌상단에 IRRLICHT 엔진의 멋진 로고를 붙이자.


IGUISkin* skin = env->getSkin();
IGUIFont* font = env->getFont("../../media/fonthaettenschweiler.bmp");
if (font)
skin->setFont(font);

IGUIImage* img = env->addImage(
driver->getTexture("../../media/irrlichtlogoalpha.tga"),
position2d<int>(10,10));


자, 이제 그리기만 하면 된다.


while(device->run() && driver)

{
if (device->isWindowActive())
{
driver->beginScene(true, true, SColor(0,200,200,200));

env->drawAll();

driver->endScene();
}

}

device->drop();

return 0;
}


-------------------------------------------------------------------------

엔진에 관한 약간의 회의가 있었었다. 단순히 생각해 게임에서 프로그래밍이란 그림 디스플레이하고 조작만 적용시키면 되는 것 아닌가 하는 생각 때문이었다. 그러나 현업에 점점 적응해가고 게이머의 눈높이를 생각해보면, 그렇게 쉽게 날로 먹을 것이 없다는 생각이 들었다. 모든게 다 필요하다.


물론 그것은 항상 깨질 준비가 된 것들이다. 문화 컨텐츠가 그런 것이듯 오늘의 각종 장치와 인터페이스는 '깨질 운명'이라고 봐도 좋을 것이다. 허나 그럼에도 불구하고 개발자는 그 모든 것을 일단은 습득하고 있어야 한다.


이번 예제를 통해 GUI를 만들어 봤다. GUI란 게임 내에 하나의 윈도 시스템을 만드는 것과 같다. 이번 예제의 예는 매우 단순한 형태이며 그 모양도 일반 윈도와 같이 규칙화 되어있다. 이것을 처음에는 그대로 쓰겠지만, 더 멋진, 더 상업적인 인터페이스를 위해선 개선이 필요하다. 특히 예제의 윈도 박스나 버튼 모양, 크기, 기능을 어디서 어떻게 변경하는지가 이후의 숙제다.


+ Recent posts