이제 앞에서 만든 이미지들을 게임에서 활용해보겠습니다.
정해진 그림 찾기 게임의 경우 서로다른 20개의 이미지중에서 주어진 이미지를 빠르게 찾는 게임입니다.
모든 카드가 오픈되면 빠른시간에 주어진 이미지를 찾으면 되는 게임입니다.
스테이지는 3스테이지까지 구현하며 점수를 표시하도록 할 것 입니다.
해당 기능들을 차례차례 구현해보겠습니다.
GameScene이라는 이름의 클래스를 Classes 폴더에 생성합니다. 연재물 [5.2.4] 참고.
Figure 7‑1 클래스 생성
GameScene.h 파일과 GameScene.cpp 파일을 생성하였으면 아래와 같이 수정합니다.
----------GameScene.h----------
#include "cocos2d.h"
class GameScene : public cocos2d::Layer
{
public:
// there's no 'id' in cpp, so we recommend returning the class instance pointer
static cocos2d::Scene* createScene();
// Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
virtual bool init();
// implement the "static create()" method manually
CREATE_FUNC(GameScene);
cocos2d::LabelTTF * _labelScore;
void onClickBack(Ref *object);
};
----------GameScene.cpp----------
#include "GameScene.h"
USING_NS_CC;
Scene* GameScene::createScene()
{
// 'scene' is an autorelease object
auto scene = Scene::create();
// 'layer' is an autorelease object
auto layer = GameScene::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool GameScene::init()
{
//////////////////////////////
// 1. super init first
if (!Layer::init())
{
return false;
}
//code here
Size winSize = Director::getInstance()->getOpenGLView()->getDesignResolutionSize();
//배경
auto bg = Sprite::create("game_bg.png");
bg->setPosition(Point(winSize.width / 2, winSize.height / 2));
this->addChild(bg);
//타이틀
auto title = Sprite::create("game_title.png");
title->setPosition(Point(winSize.width / 2, winSize.height - 20));
this->addChild(title);
//돌아가기 버튼
auto back = MenuItemImage::create("btn_back.png", "btn_back_on.png", CC_CALLBACK_1(GameScene::onClickBack, this));
back->setPosition(Point(30, winSize.height - 20));
auto menu = Menu::create(back, NULL);
menu->setPosition(Point::ZERO);
this->addChild(menu);
//스코어 창
auto score = Sprite::create("box_scroe.png");
score->setPosition(Point(250, 410));
this->addChild(score);
//스코어 표시 Label
_labelScore = LabelTTF::create("0", "arial", 20);
_labelScore->setPosition(Point(score->getContentSize().width / 2, 22));
//score Sprite에 추가해주었다.
score->addChild(_labelScore);
return true;
}
void GameScene::onClickBack(Ref* object)
{
log("onClickBack");
Director::getInstance()->popScene();
}
GameScene 클레스를 생성하였으면 StartScene에서 GameScene를 호출해줍니다.
----------StartScene.cpp----------
#include "StartScene.h"
#include "CharacterScene.h"
#include "GameScene.h"
USING_NS_CC;
…생략…
void StartScene::onClickButton2(Ref *object){
log("onClickButton2");
//%s는 String을 받아서 출력합니다.
log("%s", "string test");
//log()는 여러 파라메터를 받을 수 있습니다.
log("%s / %s", "string test1", "string test2");
//%d는 int형으로 출력합니다.
log("%d", 1234567890);
//%f는 float형을 받아 출력이 가능합니다.
log("%f", 1234.56789f);
//섞어서 사용할 수도 있습니다.
log("%s / %d / %f", "test", 123, 0.25f);
auto Scene = TransitionCrossFade::create(0.5f, GameScene::createScene());
CCDirector::getInstance()->pushScene(Scene);
}
StartScene에서 GameScene을 호출하도록 수정하였으면 디버거를 실행해 확인합니다.
Figure 7‑2 실행화면
게임화면의 기본 구조가 완성되었습니다.
back 버튼을 선택하면 StartScene으로 이동됩니다.
Figure 7‑3 실행화면
하지만 게임을 하기위해선 20개의 이미지가 필요하기 때문에 캐릭터 생성하기에서 20개의 이미지를 생성하지 않았으면 실행하지 않도록 수정합니다.
먼저 DatabaseManager에서 저장된 이미지 개수를 체크 할 수 있는 함수를 생성하도록 합니다.
----------DatabaseManager.h----------
…생략…
class DatabaseManager
{
private:
bool openDB();
void closeDB();
sqlite3 *_sqlite;
public:
DatabaseManager();
~DatabaseManager();
//에러메시지를 담을 변수
char *_errorMSG;
//결과의 상태를 담을 변수
int _result;
static DatabaseManager *getInstance();
void createDB();
void insertDB();
void selectDB();
list<head*> selectDB(string table, int no);
int insertCharacterDB(character *characterInfo);
void deleteCharacterDB(int no);
std::list<character*> selectGalleryDB();
int checkCount();
};
----------DatabaseManager.cpp----------
…생략…
int DatabaseManager::checkCount(){
int cnt = 0;
{
sqlite3_stmt *pStmt = NULL;
string query = "select count(*) from TB_USER_CHARACTER";
_result = sqlite3_prepare_v2(_sqlite, query.c_str(), query.length(), &pStmt, NULL);
if (_result == SQLITE_OK){
log("selectDB() SUCCESS");
if (sqlite3_step(pStmt) == SQLITE_ROW){
cnt = sqlite3_column_int(pStmt, 0);
}
}
sqlite3_finalize(pStmt);
}
return cnt;
}
row의 개수를 return하는 메소드를 생성하였습니다.
StartScene에서 사용하도록 합니다.
----------StartScene.cpp----------
#include "StartScene.h"
#include "CharacterScene.h"
#include "GameScene.h"
#include "DatabaseManager.h"
#include "TextPopup.h"
USING_NS_CC;
…생략…
void StartScene::onClickButton2(Ref *object){
log("onClickButton2");
//%s는 String을 받아서 출력합니다.
log("%s", "string test");
//log()는 여러 파라메터를 받을 수 있습니다.
log("%s / %s", "string test1", "string test2");
//%d는 int형으로 출력합니다.
log("%d", 1234567890);
//%f는 float형을 받아 출력이 가능합니다.
log("%f", 1234.56789f);
//섞어서 사용할 수도 있습니다.
log("%s / %d / %f", "test", 123, 0.25f);
if (DatabaseManager::getInstance()->checkCount() < 20){
this->addChild(TextPopup::create("저장된 이미지가 20개가 안됩니다.\n이미지를 먼저 생성하세요.", this, NULL, false), 99);
return;
}
auto Scene = TransitionCrossFade::create(0.5f, GameScene::createScene());
CCDirector::getInstance()->pushScene(Scene);
}
checkCount()의 값이 20개 미만이면 팝업을 출력하고 return되어 함수를 빠져나옵니다.
디버거를 실행하여 확인해보도록 합니다.
Figure 7‑4 이미지 20개 미만일시 실행화면
Figure 7‑5 이미지 20개 일시 실행화면
타겟이 되는 그림을 랜덤하게 가져올 수 있는 모듈을 구현하도록 하겠습니다.
먼저 DatabaseManager에서 값을 가져올 수 있는 함수를 만들도록 하겠습니다.
----------DatabaseManager.h----------
…생략…
class DatabaseManager
{
private:
bool openDB();
void closeDB();
sqlite3 *_sqlite;
public:
DatabaseManager();
~DatabaseManager();
//에러메시지를 담을 변수
char *_errorMSG;
//결과의 상태를 담을 변수
int _result;
static DatabaseManager *getInstance();
void createDB();
void insertDB();
void selectDB();
list<head*> selectDB(string table, int no);
int insertCharacterDB(character *characterInfo);
void deleteCharacterDB(int no);
std::list<character*> selectGalleryDB();
int checkCount();
character* selectRandomGalleryDB();
};
----------DatabaseManager.cpp----------
…생략…
character* DatabaseManager::selectRandomGalleryDB(){
string query = "select * from TB_USER_CHARACTER ORDER BY random() limit 1";
sqlite3_stmt *pStmt = NULL;
_result = sqlite3_prepare_v2(_sqlite, query.c_str(), query.length(), &pStmt, NULL);
character * item = new character();
if (_result == SQLITE_OK){
log("selectDB() SUCCESS");
if (sqlite3_step(pStmt) == SQLITE_ROW){
item->no = sqlite3_column_int(pStmt, 0);
item->headNo = sqlite3_column_int(pStmt, 1);
item->headColorNo = sqlite3_column_int(pStmt, 2);
item->hair1No = sqlite3_column_int(pStmt, 3);
item->hair1ColorNo = sqlite3_column_int(pStmt, 4);
item->hair2No = sqlite3_column_int(pStmt, 5);
item->hair2ColorNo = sqlite3_column_int(pStmt, 6);
item->eyeNo = sqlite3_column_int(pStmt, 7);
item->eyeColorNo = sqlite3_column_int(pStmt, 8);
item->mouthNo = sqlite3_column_int(pStmt, 9);
item->mouthColorNo = sqlite3_column_int(pStmt, 10);
item->etcNo = sqlite3_column_int(pStmt, 11);
item->etcColorNo = sqlite3_column_int(pStmt, 12);
item->bgNo = sqlite3_column_int(pStmt, 13);
item->bgColorNo = sqlite3_column_int(pStmt, 14);
}
}
else
log("ERROR CODE : %d", _result);
sqlite3_finalize(pStmt);
return item;
}
랜덤하게 저장된 값중 하나를 가져오는 메소드를 만들었습니다.
이 메소드를 이용해 타겟 이미지를 설정하도록 하겠습니다.
StartScene에서 GalleryScene의 getImage()를 이용하기위해 함수를 getImage()를 static으로 변경합니다.
----------GalleryScene.h----------
#include "cocos2d.h"
class GalleryScene : public cocos2d::Layer
{
public:
// there's no 'id' in cpp, so we recommend returning the class instance pointer
static cocos2d::Scene* createScene();
// Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
virtual bool init();
// implement the "static create()" method manually
CREATE_FUNC(GalleryScene);
void onClickHome(Ref *object);
void onClickMake(Ref *object);
//아이템들을 추가할 메소드
void setItems();
//아이템의 이미지를 번호와 색상에 맞추어 가져옴, CharacterScene의 setImage()와 비슷하게 동작하나 Sprite를 return하도록 구현
static cocos2d::Sprite* getImage(std::string tableName, int rowNo, int colorNo);
void onClickItemCallback(Ref *object);
int _removeNo;
void removeItem();
};
static으로 지정해주어야 다른 클래스에서 호출이 가능합니다.
GameScene에서 타겟이미지를 지정해주는 메소드를 정의합니다.
----------GameScene.h----------
#include "cocos2d.h"
class GameScene : public cocos2d::Layer
{
public:
// there's no 'id' in cpp, so we recommend returning the class instance pointer
static cocos2d::Scene* createScene();
// Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
virtual bool init();
// implement the "static create()" method manually
CREATE_FUNC(GameScene);
cocos2d::LabelTTF * _labelScore;
void onClickBack(Ref *object);
cocos2d::Sprite* _targetBack;
void setTarget();
};
----------GameScene.cpp----------
#include "GameScene.h"
#include "DatabaseManager.h"
#include "GalleryScene.h"
USING_NS_CC;
…생략…
// on "init" you need to initialize your instance
bool GameScene::init()
{
//////////////////////////////
// 1. super init first
if (!Layer::init())
{
return false;
}
//code here
Size winSize = Director::getInstance()->getOpenGLView()->getDesignResolutionSize();
//배경
auto bg = Sprite::create("game_bg.png");
bg->setPosition(Point(winSize.width / 2, winSize.height / 2));
this->addChild(bg);
//타이틀
auto title = Sprite::create("game_title.png");
title->setPosition(Point(winSize.width / 2, winSize.height - 20));
this->addChild(title);
//돌아가기 버튼
auto back = MenuItemImage::create("btn_back.png", "btn_back_on.png", CC_CALLBACK_1(GameScene::onClickBack, this));
back->setPosition(Point(30, winSize.height - 20));
auto menu = Menu::create(back, NULL);
menu->setPosition(Point::ZERO);
this->addChild(menu);
//스코어 창
auto score = Sprite::create("box_scroe.png");
score->setPosition(Point(250, 410));
this->addChild(score);
//스코어 표시 Label
_labelScore = LabelTTF::create("0", "arial", 20);
_labelScore->setPosition(Point(score->getContentSize().width / 2, 22));
//score Sprite에 추가해주었다.
score->addChild(_labelScore);
_targetBack = NULL;
setTarget();
return true;
}
…생략…
void GameScene::setTarget(){
if (_targetBack)
_targetBack->removeFromParentAndCleanup(true);
character *item = DatabaseManager::getInstance()->selectRandomGalleryDB();
_targetBack = Sprite::create("s_bg_1.png");
auto head = GalleryScene::getImage("TB_FACE", item->headNo, item->headColorNo);
auto hair1 = GalleryScene::getImage("TB_HAIR1", item->hair1No, item->hair1ColorNo);
auto hair2 = GalleryScene::getImage("TB_HAIR2", item->hair2No, item->hair2ColorNo);
auto eye = GalleryScene::getImage("TB_EYE", item->eyeNo, item->eyeColorNo);
auto mouth = GalleryScene::getImage("TB_MOUTH", item->mouthNo, item->mouthColorNo);
auto etc = GalleryScene::getImage("TB_ETC", item->etcNo, item->etcColorNo);
auto bg = GalleryScene::getImage("TB_BG", item->bgNo, item->bgColorNo);
//getImage()에서 setPosition이 되어있기에 따로 지정하지 않아도 된다.
_targetBack->addChild(bg, 0);
_targetBack->addChild(hair2, 1);
_targetBack->addChild(head, 2);
_targetBack->addChild(eye, 3);
_targetBack->addChild(mouth, 3);
_targetBack->addChild(hair1, 4);
_targetBack->addChild(etc, 5);
auto frame = Sprite::create("box_2.png");
frame->setPosition(Point(_targetBack->getContentSize().width / 2, _targetBack->getContentSize().height / 2));
_targetBack->addChild(frame, 6);
//50px 로 크기를 줄여줌
float scale = 50 / _targetBack->getContentSize().width;
_targetBack->setScale(scale);
_targetBack->setPosition(Point(85, 410));
this->addChild(_targetBack);
}
GalleryScene에서 사용했던 코드를 약간 변형하여 만들었습니다.
디버거를 실행해 확인합니다.
Figure 7‑6 실행화면
저장해놓았던 이미지중에 하나가 타겟에 랜덤하게 나오는 것을 볼 수 있습니다.
이번엔 저장된 이미지들을 하단영역에 위치시키도록 하겠습니다.
----------GameScene.h----------
#include "cocos2d.h"
class GameScene : public cocos2d::Layer
{
public:
// there's no 'id' in cpp, so we recommend returning the class instance pointer
static cocos2d::Scene* createScene();
// Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
virtual bool init();
// implement the "static create()" method manually
CREATE_FUNC(GameScene);
cocos2d::LabelTTF * _labelScore;
void onClickBack(Ref *object);
cocos2d::Sprite* _targetBack;
void setTarget();
cocos2d::LayerColor* _imagesBack;
void setImages();
};
----------GameScene.cpp----------
…생략…
// on "init" you need to initialize your instance
bool GameScene::init()
{
//////////////////////////////
// 1. super init first
if (!Layer::init())
{
return false;
}
//code here
Size winSize = Director::getInstance()->getOpenGLView()->getDesignResolutionSize();
//배경
auto bg = Sprite::create("game_bg.png");
bg->setPosition(Point(winSize.width / 2, winSize.height / 2));
this->addChild(bg);
//타이틀
auto title = Sprite::create("game_title.png");
title->setPosition(Point(winSize.width / 2, winSize.height - 20));
this->addChild(title);
//돌아가기 버튼
auto back = MenuItemImage::create("btn_back.png", "btn_back_on.png", CC_CALLBACK_1(GameScene::onClickBack, this));
back->setPosition(Point(30, winSize.height - 20));
auto menu = Menu::create(back, NULL);
menu->setPosition(Point::ZERO);
this->addChild(menu);
//스코어 창
auto score = Sprite::create("box_scroe.png");
score->setPosition(Point(250, 410));
this->addChild(score);
//스코어 표시 Label
_labelScore = LabelTTF::create("0", "arial", 20);
_labelScore->setPosition(Point(score->getContentSize().width / 2, 22));
//score Sprite에 추가해주었다.
score->addChild(_labelScore);
_targetBack = NULL;
setTarget();
_imagesBack = LayerColor::create(Color4B(0, 0, 0, 0), winSize.width, 340);
_imagesBack->setPosition(Point(0, 0)); //LayerColor는 기본 AnchorPoint가 (0, 0)이다.
this->addChild(_imagesBack);
setImages();
return true;
}
…생략…
void GameScene::setImages(){
_imagesBack->removeAllChildrenWithCleanup(true);
auto galleryList = DatabaseManager::getInstance()->selectGalleryDB();
int size = galleryList.size();
Vector<MenuItem*> itemArray;
for (int i = 1; i <= size; i++){
character *item = galleryList.front();
auto back = Sprite::create("s_bg_1.png");
auto head = GalleryScene::getImage("TB_FACE", item->headNo, item->headColorNo);
auto hair1 = GalleryScene::getImage("TB_HAIR1", item->hair1No, item->hair1ColorNo);
auto hair2 = GalleryScene::getImage("TB_HAIR2", item->hair2No, item->hair2ColorNo);
auto eye = GalleryScene::getImage("TB_EYE", item->eyeNo, item->eyeColorNo);
auto mouth = GalleryScene::getImage("TB_MOUTH", item->mouthNo, item->mouthColorNo);
auto etc = GalleryScene::getImage("TB_ETC", item->etcNo, item->etcColorNo);
auto bg = GalleryScene::getImage("TB_BG", item->bgNo, item->bgColorNo);
//getImage()에서 setPosition이 되어있기에 따로 지정하지 않아도 된다.
back->addChild(bg, 0);
back->addChild(hair2, 1);
back->addChild(head, 2);
back->addChild(eye, 3);
back->addChild(mouth, 3);
back->addChild(hair1, 4);
back->addChild(etc, 5);
auto frame = Sprite::create("box_face.png");
frame->setPosition(Point(back->getContentSize().width / 2, back->getContentSize().height / 2));
back->addChild(frame, 6);
auto menuItem = MenuItemSprite::create(back, NULL);
//기능을 위해 번호를 넣어준다.
menuItem->setTag(item->no);
//55px 로 크기를 줄여줌
float scale = 55 / back->getContentSize().width;
menuItem->setScale(scale);
int row = (i - 1) / 4; //0~3
int col = (i - 1) % 4; //0~3
menuItem->setAnchorPoint(Point(0, 1));
//좌표를 동적할당
float x = col * menuItem->getContentSize().width * scale + (col + 1) * 20;
float y = _imagesBack->getContentSize().height - (row * menuItem->getContentSize().height * scale + (row + 1) * 10);
menuItem->setPosition(Point(x, y));
itemArray.pushBack(menuItem);
galleryList.pop_front();
}
auto menu = Menu::createWithArray(itemArray);
menu->setPosition(Point::ZERO);
_imagesBack->addChild(menu);
}
GalleryScene의 내용을 수정하여 사용했습니다.
디버거를 실행해 확인합니다.
Figure 7‑7 실행화면
저장된 그림들이 위치營윱求?span>. 하지만 순서가 일정하게 나옵니다. 랜덤하게 나오도록 섞어주도록 하겠습니다.
----------DatabaseManager.h----------
…생략…
class DatabaseManager
{
private:
bool openDB();
void closeDB();
sqlite3 *_sqlite;
public:
DatabaseManager();
~DatabaseManager();
//에러메시지를 담을 변수
char *_errorMSG;
//결과의 상태를 담을 변수
int _result;
static DatabaseManager *getInstance();
void createDB();
void insertDB();
void selectDB();
list<head*> selectDB(string table, int no);
int insertCharacterDB(character *characterInfo);
void deleteCharacterDB(int no);
std::list<character*> selectGalleryDB(bool isRandom);
int checkCount();
character* selectRandomGalleryDB();
};
----------DatabaseManager.cpp----------
…생략…
list<character*> DatabaseManager::selectGalleryDB(bool isRandom){
list<character *> galleryItemList;
string query = "select * from TB_USER_CHARACTER";
if (isRandom)
query += " order by random()";
sqlite3_stmt *pStmt = NULL;
_result = sqlite3_prepare_v2(_sqlite, query.c_str(), query.length(), &pStmt, NULL);
if (_result == SQLITE_OK)
{
log("selectDB() SUCCESS");
while (sqlite3_step(pStmt) == SQLITE_ROW)
{
character *item = new character;
item->no = sqlite3_column_int(pStmt, 0);
item->headNo = sqlite3_column_int(pStmt, 1);
item->headColorNo = sqlite3_column_int(pStmt, 2);
item->hair1No = sqlite3_column_int(pStmt, 3);
item->hair1ColorNo = sqlite3_column_int(pStmt, 4);
item->hair2No = sqlite3_column_int(pStmt, 5);
item->hair2ColorNo = sqlite3_column_int(pStmt, 6);
item->eyeNo = sqlite3_column_int(pStmt, 7);
item->eyeColorNo = sqlite3_column_int(pStmt, 8);
item->mouthNo = sqlite3_column_int(pStmt, 9);
item->mouthColorNo = sqlite3_column_int(pStmt, 10);
item->etcNo = sqlite3_column_int(pStmt, 11);
item->etcColorNo = sqlite3_column_int(pStmt, 12);
item->bgNo = sqlite3_column_int(pStmt, 13);
item->bgColorNo = sqlite3_column_int(pStmt, 14);
galleryItemList.push_back(item);
}
}
else{
log("ERROR CODE : %d", _result);
}
sqlite3_finalize(pStmt);
return galleryItemList;
}
기존에 만들어둔 selectGalleryDB()에 isRandom 이라는 파라메터를 추가하여 order by random() 을 추가하는지 제어합니다. 띄어쓰기에 주의하여 쿼리를 작성합니다.
order by random()을 추가하면 데이터베이스에서 row를 가져오는데 랜덤하게 가져옵니다.
파라메터를 추가했으니 기존에 selectGalleryDB()를 호출했던 부분에 파라메터를 추가해줍니다.
----------GalleryScene.cpp----------
…생략…
void GalleryScene::setItems(){
//galleryLayer를 this에 tag가 1로 addchild 해주었다. getChildByTag는 해당 노드의 tag번호에 맞는 자식 노드를 반환한다.
auto galleryLayer = (LayerColor *)this->getChildByTag(1);
//removeAllChildrenWithCleanup()는 자식노드 전부를 삭제한다.
galleryLayer->removeAllChildrenWithCleanup(true);
std::list<character *>galleryList = DatabaseManager::getInstance()->selectGalleryDB(false);
int size = galleryList.size();
Vector<MenuItem*> itemArray;
for (int i = 1; i <= size; i++){
character *item = galleryList.front();
auto back = Sprite::create("s_bg_1.png");
auto head = getImage("TB_FACE", item->headNo, item->headColorNo);
auto hair1 = getImage("TB_HAIR1", item->hair1No, item->hair1ColorNo);
auto hair2 = getImage("TB_HAIR2", item->hair2No, item->hair2ColorNo);
auto eye = getImage("TB_EYE", item->eyeNo, item->eyeColorNo);
auto mouth = getImage("TB_MOUTH", item->mouthNo, item->mouthColorNo);
auto etc = getImage("TB_ETC", item->etcNo, item->etcNo);
auto bg = getImage("TB_BG", item->bgNo, item->bgColorNo);
//getImage()에서 setPosition이 되어있기에 따로 지정하지 않아도 된다.
back->addChild(bg, 0);
back->addChild(hair2, 1);
back->addChild(head, 2);
back->addChild(eye, 3);
back->addChild(mouth, 3);
back->addChild(hair1, 4);
back->addChild(etc, 5);
auto frame = Sprite::create("box_gallery.png");
frame->setPosition(Point(back->getContentSize().width / 2, back->getContentSize().height / 2));
back->addChild(frame, 6);
auto MenuItem = MenuItemSprite::create(back, NULL, CC_CALLBACK_1(GalleryScene::onClickItemCallback, this));
MenuItem->setTag(item->no); //삭제 기능을 위해 번호를 넣어준다.
//55px 로 크기를 줄이기 위해 scale을 구함
float scale = 55 / back->getContentSize().width;
MenuItem->setScale(scale);
int row = (i - 1) / 4; //0~3
int col = (i - 1) % 4; //0~3
//row와 col을 구한다.
MenuItem->setAnchorPoint(Point(0, 1));
//좌표를 동적할당
float x = col * MenuItem->getContentSize().width * scale + (col + 1) * 20;
float y = galleryLayer->getContentSize().height - (row * MenuItem->getContentSize().height * scale + (row + 1) * 15);
MenuItem->setPosition(Point(x, y));
itemArray.pushBack(MenuItem);
galleryList.pop_front();
}
auto menu = Menu::createWithArray(itemArray);
menu->setPosition(Point::ZERO);
galleryLayer->addChild(menu);
}
----------GameScene.cpp----------
…생략…
void GameScene::setImages(){
_imagesBack->removeAllChildrenWithCleanup(true);
auto galleryList = DatabaseManager::getInstance()->selectGalleryDB(true);
int size = galleryList.size();
Vector<MenuItem*> itemArray;
for (int i = 1; i <= size; i++){
character *item = galleryList.front();
auto back = Sprite::create("s_bg_1.png");
auto head = GalleryScene::getImage("TB_FACE", item->headNo, item->headColorNo);
auto hair1 = GalleryScene::getImage("TB_HAIR1", item->hair1No, item->hair1ColorNo);
auto hair2 = GalleryScene::getImage("TB_HAIR2", item->hair2No, item->hair2ColorNo);
auto eye = GalleryScene::getImage("TB_EYE", item->eyeNo, item->eyeColorNo);
auto mouth = GalleryScene::getImage("TB_MOUTH", item->mouthNo, item->mouthColorNo);
auto etc = GalleryScene::getImage("TB_ETC", item->etcNo, item->etcColorNo);
auto bg = GalleryScene::getImage("TB_BG", item->bgNo, item->bgColorNo);
//getImage()에서 setPosition이 되어있기에 따로 지정하지 않아도 된다.
back->addChild(bg, 0);
back->addChild(hair2, 1);
back->addChild(head, 2);
back->addChild(eye, 3);
back->addChild(mouth, 3);
back->addChild(hair1, 4);
back->addChild(etc, 5);
auto frame = Sprite::create("box_face.png");
frame->setPosition(Point(back->getContentSize().width / 2, back->getContentSize().height / 2));
back->addChild(frame, 6);
auto menuItem = MenuItemSprite::create(back, NULL);
//기능을 위해 번호를 넣어준다.
menuItem->setTag(item->no);
//55px 로 크기를 줄여줌
float scale = 55 / back->getContentSize().width;
menuItem->setScale(scale);
int row = (i - 1) / 4; //0~3
int col = (i - 1) % 4; //0~3
menuItem->setAnchorPoint(Point(0, 1));
//좌표를 동적할당
float x = col * menuItem->getContentSize().width * scale + (col + 1) * 20;
float y = _imagesBack->getContentSize().height - (row * menuItem->getContentSize().height * scale + (row + 1) * 10);
menuItem->setPosition(Point(x, y));
itemArray.pushBack(menuItem);
galleryList.pop_front();
}
auto menu = Menu::createWithArray(itemArray);
menu->setPosition(Point::ZERO);
_imagesBack->addChild(menu);
}
GalleryScene에선 false를 GameScene에선 true를 추가해줍니다.
디버거를 실행해 잘 섞여 나오는지 확인합니다.
Figure 7‑8 실행화면
이미지가 섞여나오는 것을 확인 할 수 있습니다.
게임을 시작하기전에 준비를 할 수 있는 CountDown을 추가하도록 하겠습니다.
----------GameScene.h----------
#include "cocos2d.h"
class GameScene : public cocos2d::Layer
{
public:
// there's no 'id' in cpp, so we recommend returning the class instance pointer
static cocos2d::Scene* createScene();
// Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
virtual bool init();
// implement the "static create()" method manually
CREATE_FUNC(GameScene);
cocos2d::LabelTTF * _labelScore;
void onClickBack(Ref *object);
cocos2d::Sprite* _targetBack;
void setTarget();
cocos2d::LayerColor* _imagesBack;
void setImages();
//카운트 다운중이면 터치를 취소하기 위한 변수
bool _isCountDown;
void setCountDown();
//카운트 다운이 끝나는 callback
void setCountDownEnd(Ref *object);
};
----------GameScene.cpp----------
…생략…
// on "init" you need to initialize your instance
bool GameScene::init()
{
//////////////////////////////
// 1. super init first
if (!Layer::init())
{
return false;
}
//code here
Size winSize = Director::getInstance()->getOpenGLView()->getDesignResolutionSize();
//배경
auto bg = Sprite::create("game_bg.png");
bg->setPosition(Point(winSize.width / 2, winSize.height / 2));
this->addChild(bg);
//타이틀
auto title = Sprite::create("game_title.png");
title->setPosition(Point(winSize.width / 2, winSize.height - 20));
this->addChild(title);
//돌아가기 버튼
auto back = MenuItemImage::create("btn_back.png", "btn_back_on.png", CC_CALLBACK_1(GameScene::onClickBack, this));
back->setPosition(Point(30, winSize.height - 20));
auto menu = Menu::create(back, NULL);
menu->setPosition(Point::ZERO);
this->addChild(menu);
//스코어 창
auto score = Sprite::create("box_scroe.png");
score->setPosition(Point(250, 410));
this->addChild(score);
//스코어 표시 Label
_labelScore = LabelTTF::create("0", "arial", 20);
_labelScore->setPosition(Point(score->getContentSize().width / 2, 22));
//score Sprite에 추가해주었다.
score->addChild(_labelScore);
_targetBack = NULL;
setTarget();
_imagesBack = LayerColor::create(Color4B(0, 0, 0, 0), winSize.width, 340);
_imagesBack->setPosition(Point(0, 0)); //LayerColor는 기본 AnchorPoint가 (0, 0)이다.
this->addChild(_imagesBack);
setImages();
setCountDown();
return true;
}
void GameScene::onClickBack(Ref* object)
{
if (_isCountDown)
return;
log("onClickBack");
Director::getInstance()->popScene();
}
…생략…
void GameScene::setCountDown(){
_isCountDown = true;
Size winSize = Director::getInstance()->getOpenGLView()->getDesignResolutionSize();
auto CountDownLayer = LayerColor::create(Color4B(0, 0, 0, 180), winSize.width, winSize.height);
this->addChild(CountDownLayer, 10);
//라벨 생성
auto count3 = LabelTTF::create("3", "Arial", 60);
auto count2 = LabelTTF::create("2", "Arial", 60);
auto count1 = LabelTTF::create("1", "Arial", 60);
//처음에 투명하게 지정
count3->setOpacity(0);
count2->setOpacity(0);
count1->setOpacity(0);
count3->setPosition(Point(winSize.width / 2, winSize.height / 2));
//node의 포지션을 가져올 수 있다.
count2->setPosition(count3->getPosition());
count1->setPosition(count3->getPosition());
CountDownLayer->addChild(count3);
CountDownLayer->addChild(count2);
CountDownLayer->addChild(count1);
auto action = Sequence::create(FadeIn::create(0), FadeOut::create(1), NULL);
count3->runAction(action);
count2->runAction(Sequence::create(DelayTime::create(1), action->clone(), NULL));
count1->runAction(Sequence::create(DelayTime::create(2), action->clone(), NULL));
CountDownLayer->runAction(Sequence::create(DelayTime::create(3), CallFuncN::create(CC_CALLBACK_1(GameScene::setCountDownEnd, this)), NULL));
}
void GameScene::setCountDownEnd(Ref *object){
_isCountDown = false;
((Node*)object)->removeFromParentAndCleanup(true);
}
레이어 하나와 라벨 3개를 추가하였고 각각 라벨과 레이어에 시간차를 두어 액션을 추가해주었습니다.
레이어는 엑션이 끝나면 자기자신을 제거 해주었습니다.
CallFuncN 메소드는 selector로 등록된 콜백메소드를 호출하는데 자기자신의 노드를 파라메터로 넘깁니다.
_isCountDown이란 변수를 두어 카운트 다운중인지를 체크하여 back버튼이 동작하지 않도록 추가해주었습니다.
디버거를 실행해 확인해봅니다.
Figure 7‑9 실행화면
카운트다운 기능이 추가된 것을 확인할 수 있습니다.
카운트 다운도중 레이어 아래에 이미지들이 보이므로 이미지들이 보이지 않도록 수정하고 에니메이션을 추가하도록 하겠습니다.
----------GameScene.cpp----------
...생략...
void GameScene::setImages(){
_imagesBack->removeAllChildrenWithCleanup(true);
auto galleryList = DatabaseManager::getInstance()->selectGalleryDB(true);
int size = galleryList.size();
Vector<MenuItem*> itemArray;
for (int i = 1; i <= size; i++){
character *item = galleryList.front();
auto back = Sprite::create("s_bg_1.png");
auto head = GalleryScene::getImage("TB_FACE", item->headNo, item->headColorNo);
auto hair1 = GalleryScene::getImage("TB_HAIR1", item->hair1No, item->hair1ColorNo);
auto hair2 = GalleryScene::getImage("TB_HAIR2", item->hair2No, item->hair2ColorNo);
auto eye = GalleryScene::getImage("TB_EYE", item->eyeNo, item->eyeColorNo);
auto mouth = GalleryScene::getImage("TB_MOUTH", item->mouthNo, item->mouthColorNo);
auto etc = GalleryScene::getImage("TB_ETC", item->etcNo, item->etcColorNo);
auto bg = GalleryScene::getImage("TB_BG", item->bgNo, item->bgColorNo);
//getImage()에서 setPosition이 되어있기에 따로 지정하지 않아도 된다.
back->addChild(bg, 0);
back->addChild(hair2, 1);
back->addChild(head, 2);
back->addChild(eye, 3);
back->addChild(mouth, 3);
back->addChild(hair1, 4);
back->addChild(etc, 5);
auto frame = Sprite::create("box_face.png");
frame->setPosition(Point(back->getContentSize().width / 2, back->getContentSize().height / 2));
back->addChild(frame, 6);
auto menuItem = MenuItemSprite::create(back, NULL);
//기능을 위해 번호를 넣어준다.
menuItem->setTag(item->no);
//55px 로 크기를 줄여줌
float scale = 55 / back->getContentSize().width;
menuItem->setScale(scale);
int row = (i - 1) / 4; //0~3
int col = (i - 1) % 4; //0~3
menuItem->setAnchorPoint(Point(0, 1));
//좌표를 동적할당
float x = col * menuItem->getContentSize().width * scale + (col + 1) * 20;
float y = _imagesBack->getContentSize().height - (row * menuItem->getContentSize().height * scale + (row + 1) * 10);
menuItem->setPosition(Point(x, y));
itemArray.pushBack(menuItem);
galleryList.pop_front();
auto blindImg = Sprite::create("box_back.png");
blindImg->setPosition(Point(menuItem->getContentSize().width / 2, menuItem->getContentSize().height / 2));
menuItem->addChild(blindImg);
back->setScaleX(0);
//MenuItem에 추가되는 sprite는 앵커포인트가 0, 0으로 추가된다.
back->setAnchorPoint(Point(0.5f, 0.5f));
//따라서 앵커포인트를 수정하고 위치를 수정해주었다.
back->setPosition(Point(back->getContentSize().width / 2, back->getContentSize().height / 2));
//카운트다운 시간인 3초간 지연시키고 가로Scale을 0으로 에니메이션
blindImg->runAction(Sequence::create(DelayTime::create(3), ScaleTo::create(0.2f, 0, 1), NULL));
//카운트다운 시간 + 이전 에니메이션 동작시간인 0.2 초를 더해준값만큼 딜레이
back->runAction(Sequence::create(DelayTime::create(3 + 0.2f), ScaleTo::create(0.2f, 1, 1), NULL));
}
auto menu = Menu::createWithArray(itemArray);
menu->setPosition(Point::ZERO);
_imagesBack->addChild(menu);
}
menuItem에 blindImg를 추가하고 기존의 캐릭터 이미지가 들어있는 back의 가로 ScaleX을 0 으로 수정하여 에니메이션을 시간차를 두고 동작시켰습니다.
디버거를 실행해 확인해보겠습니다.
Figure 7‑10 실행화면
3초간 카운트다운이 끝나고 이미지가 뒤집어져 보이는 것을 확인할 수 있습니다.