이제 StartScene 화면의 UI를 작성하도록 하겠습니다. StartScene.cpp 파일에서 init() 메서드를 찾아 다음과 같이 수정합니다. ‘init’는 initialize의 약자로 초기화한다는 뜻입니다.
----------StartScene.cpp----------
// on "init" you need to initialize your instance
bool StartScene::init()
{
//////////////////////////////
// 1. super init first
if (!Layer::init())
{
return false;
}
// 추후 코드 넣을 곳
/*****디바이스의 크기를 가져옵니다.*****/
//Director를 가져옵니다.
auto director = Director::getInstance();
//OpenGLView를 가져옵니다.
auto glview = director->getOpenGLView();
//OpenGLView에서 DesignResolutionSize를 가져옵니다.
auto winSize = glview->getDesignResolutionSize();
/*****배경 이미지 back을 넣습니다.*****/
//Sprite를 생성하여 이미지를 불러옵니다.
auto back = Sprite::create("title_bg.png");
//back을 해당 포인트에 위치시킵니다. 화면의 정중앙에 위치하도록 했습니다.
back->setPosition(Point(winSize.width / 2, winSize.height / 2));
//this에 back을 자식 노드로 추가하였습니다.
this->addChild(back);
return true;
}
init()를 수정하였으면 디버거를 실행합니다. 배경(title_bg.png)이 추가된 것을 확인할 수 있습니다.
Figure 5‑12 배경을 추가한 실행 화면
위 소스 중 중요한 부분은 auto 키워드, create(), setPosition(), addChild() 부분입니다. 그리고 코드에 직접 나오지는 않지만 노드(Node)에 대해서도 살펴보고, 이후 사용할 setAnchorPoint()도 미리 알아보고 넘어가도록 하겠습니다. 앞으로도 계속 나오는 부분이니 꼭 숙지하기 바랍니다.
auto 키워드는 C++ 11 버전부터 사용할 수 있는 키워드입니다. Cocos2d-x 3.0 버전에서부터 C++ 11을 지원합니다. auto 키워드를 사용하지 않아도 에러는 발생하지 않으나, auto 키워드를 사용하면 매우 편하고 소스 코드도 간결해지므로 꼭 숙지하고 사용하도록 합시다.
auto 키워드가 없는 기존의 방식은, 예를 들어 Sprite 객체의 경우 반드시 Sprite로 선언해야 했습니다. 즉 이런 식으로 선언해야만 했습니다.
Sprite *test = Sprite::create("test.png");
auto 키워드는 생성된 객체의 클래스에 따라 자료형을 자동으로 정해주기 때문에 매우 간편하게 사용할 수 있습니다.
auto test = Sprite:::create("test.png");
주의할 점은, auto 키워드는 선언과 초기화를 동시에 하는 경우에만 사용할 수 있다는 점입니다. 따라서 다음과 같이 사용할 수는 없다는 것을 알아두도록 합니다. 기존의 2.x 버전을 사용했던 분들은 헷갈릴 수 있으므로 참고하기 바랍니다.
//이렇게 사용하면 안 됩니다.
auto test;
test = Sprite:::create("test.png");
노드(Node)는 Scene을 구성하는 기본 객체입니다. cocos2d-x의 Scene을 구성하는 대부분의 객체들은 이 Node를 상속받습니다. 예를 들어 화면을 구성하는 Sprite나 Layer 등 화면에 나타나야 하는 클래스들이 이 Node를 상속받습니다.
create()는 노드나 액션 등 Cocos2d-x에서 사용하는 대다수의 클래스를 생성하는 메서드입니다. create()를 사용하여 객체를 생성하?메모리 해제를 수동으로 해주지 않아도 됩니다. 내부에서 자동으로 메모리를 해제해주는 autorelease() 코드가 들어 있기 때문입니다.
create()는 객체를 생성할 때 사용하는 메서드로 각 클래스마다 매개변수가 다르므로 형식에 맞추어 사용해야 합니다. Sprite의 경우 파일명을 매개변수로 넘깁니다.
setPosition()은 노드의 위치를 지정해주는 메서드입니다. 노드마다 사용하므로 꼭 알고 넘어갑시다. setPosition()은 Point를 매개변수를 받으며 Point는 x좌표와 y좌표를 가질 수 있습니다. 좌측 상단을 원점으로 잡는 언어들도 있지만, Cocos2d-x의 좌표계는 좌측 하단이 (0, 0)이고, 우측 상단이 (320, 480)이 됩니다.
(320, 480) |
(0, 0) |
Figure 5‑13 Cocos2d-x 좌표계
setAnchorPoint()는 앵커 포인트를 설정한다는 뜻이며, 앵커 포인트란 노드가 화면에 표시될 때 기준이 되는 점을 말합니다. 노드는 기본적으로 노드의 가운데를 기준으로 위치됩니다. x 및 y가 0에서 1 사이인 Point를 매개변수로 받습니다. Point(0.5, 0.5)는 노드의 정중앙입니다. 대부분의 노드들은 이 Point(0.5, 0.5)를 초기값으로 갖습니다. 하지만 Layer는 Point(0, 0)을 기본으로 갖습니다. 이 이유는 씬은 Layer를 상속받아 만들어집니다. 이 씬은 좌측 하단을 기준으로 위치하기에 Point(0, 0)을 기본값으로 갖습니다. 우리는 앞에서 배경 이미지(title_bg.png)를 화면 정가운데에 위치시켰습니다. 이 그림은 크기가 정확히 320 * 480인데, 어떻게 화면에 딱 맞게 들어갔을까요? back의 앵커 포인트가 기본값인 Point(0.5, 0.5)로서 이미지의 정중앙이므로, setPosition()을 사용하여 Point(winSize.width / 2, winSize.height / 2)로 위치시켰을 때 화면에 딱 맞게 표시된 것입니다.
//back을 해당 포인트에 위치시킵니다. 화면의 정중앙에 위치하도록 했습니다.
back->setPosition(Point(winSize.width / 2, winSize.height / 2));
이 부분은 뒤에 실전에서 사용하는 부분에서 다시 한 번 설명하겠습니다.
addChild() 메서드의 매개변수는 세 가지 종류가 있습니다.
virtual void addChild(Node *child);
virtual void addChild(Node *child, int localZOrder);
virtual void addChild(Node *child, int localZOrder, int tag);
하나씩 살펴보겠습니다. 첫 번째로 Node만 받는 경우입니다. 이때는 해당 노드만 자식으로 추가됩니다.
addChild(child);
두 번째는 Node와 ZOrder를 받습니다. 일반적으로 ZOrder를 입력하지 않은 경우 나중에 추가된 자식 노드가 위로 올라오게 되지만 ZOrder를 사용하면 순서를 조절할 수 있습니다.
addChild(child, 1);
세 번째는 Node, ZOrder, Tag를 입력으로 받습니다. Tag의 경우 setTag()를 이용하여 추가할 수도 있는데, 이렇게 addChild() 시 추가할 수도 있습니다. Tag 사용법은 추후에 설명하도록 하겠습니다.
addChild(child, 1, 3);
이상 세 가지를 필요한 상황에 맞추어 사용할 것입니다.
UI 작업을 진행하다 보면 경로나 파일명이 잘못됐을 경우 에러 메시지를 볼 수 있습니다. 이런 자주 발생하는 에러의 경우 비주얼 스튜디오의 출력창을 살펴보면 아래와 같은 메시지가 있을 겁니다. ErrorTest.png는 에러가 난 파일명입니다.
Get data from file(ErrorTest.png) failed
위와 같은 에러가 나오면 해당 경로나 파일명이 잘못되진 않았는지 확인해봅시다.
배경 이미지만 있는 메인 화면은 썰렁하죠? 이제 init() 메서드 내부를 수정하여 타이틀과 캐릭터를 추가하겠습니다.
----------StartScene.cpp----------
//this에 back을 자식 노드로 추가하였습니다.
this->addChild(back);
/*****타이틀 title을 넣습니다.*****/
//Sprite를 생성하여 이미지를 불러옵니다.
auto title = Sprite::create("title_text.png");
//앵커 포인트를 Point(0.5f, 1)로 변경
title->setAnchorPoint(Point(0.5f, 1));
//title을 해당 포인트에 위치시킵니다. 화면의 가로 중앙에 위치하도록 했습니다.
title->setPosition(Point(winSize.width / 2, winSize.height - 30));
//this에 title을 자식 노드로 추가하였습니다.
this->addChild(title);
/*****캐릭터 character을 넣습니다.*****/
//Sprite를 생성하여 이미지를 불러옵니다.
auto character = Sprite::create("title_character.png");
//character를 해당 포인트에 위치시킵니다. 화면의 정중앙에 위치하도록 했습니다.
character->setPosition(Point(winSize.width / 2, winSize.height / 2));
//this에 character를 자식 노드로 추가하였습니다.
this->addChild(character);
return true;
}
배경 이미지와 마찬가지로 Sprite를 이용하여 생성해주었습니다. 여기서는 title만 setAnchorPoint()로 앵커 포인트를 기본값인 정중앙 대신 Point(0.5f, 1)로 변경해봤습니다. setAnchorPoint()로 즉 다음 그림에서 화살표로 표시된 부분으로 앵커 포인트를 변경한 것입니다. 여기서는 차이를 확인하기 어렵지만 뒤에서 타이틀을 회전할 때는 앵커 포인트에 따른 차이를 시각적으로 확인할 수 있습니다.
Figure 5‑14 앵커 포인트
앵커 포인트의 매개변수는 0에서 1 사이의 값을 사용하며 0은 처음, 1은 끝을 나타냅니다. 0.5는 물론 그 가운데를 나타냅니다. 이 값을 퍼센트라고 생각하면 이해하기 쉽습니다. 1=100%, 0=0%, 0.5=50%처럼 말이죠.
Figure 5‑15 앵커 포인트
title->setPosition을 보면 y 위치가 winSize.height에서 30만큼 뺀 곳입니다. 이것은 곧 화면 위에서 30만큼 아래로 떨어진 곳에 위치하라는 뜻입니다.
이제 디버거를 실행해보면 배경 이미지 위에 타이틀과 캐릭터가 추가된 것을 볼 수 있습니다.
Figure 5‑16 타이틀과 캐릭터를 넣은 실행 화면
지금까지 본 실행 화면 좌측 하단을 살펴보면 GL verts, GL calls, 그리고 숫자가 있습니다. GL verts란 3D에서 사용하는 버텍스의 개수 입니다. 버텍스란 폴리곤의 꼭짓점이 만나는 곳을 뜻하며, 이 버텍스가 18개가 사용되었다는 의미입니다. GL calls란 OpenGL이 호출되는 횟수입니다. 즉 노드의 개수라고 볼 수 있습니다. 맨 아래 숫자는 FPSframes per second, 즉 초당 화면이 갱신되는 횟수입니다. 60.1이란 60 FPS라는 뜻이죠. 디버깅 시 이 숫자를 모니터링하며 현재 화면의 노드 개수와 FPS를 확인하고 퍼포먼스에 문제가 없는지 살펴볼 수 있습니다.
물론 프로그램을 배포할 때는 저런 표시가 남아 있으면 안 되겠죠. 이를 표시하지 않기 위해서는 AppDelegate.cpp 파일을 수정해야 합니다. 다음 라인을 찾아 true를 false로 바꿔주면 됩니다.
// turn on display FPS
director->setDisplayStats(false);