8. 북극곰 탈출 게임

지난번 연재 8-1, 8-2, 8-3과 이어집니다.

8.5. 스테이지 선택 화면 만들기

8.5.1. 스테이지 선택 화면 구현하기

스테이지 선택화면에 스테이지를 선택할 수 있도록 구현하도록 하겠습니다.

 

먼저 스테이지 화면의 우측하단에 곰 이미지를 추가하도록 하겠습니다.

----------StageScene.cpp----------

 

…생략…

 

// on "init" you need to initialize your instance

bool StageScene::init()

{

    //////////////////////////////

    // 1. super init first

    if (!Layer::init())

    {

        return false;

    }

 

    //code here

 

    this->addChild(BackgroundLayer::create());

 

    //화면 크기를 가져옴

    auto winSize = Director::getInstance()->getOpenGLView()->getDesignResolutionSize();

 

    auto deco = Sprite::create("stage_bear.png");

    deco->setAnchorPoint(Point(1, 0));

    deco->setPosition(Point(winSize.width + deco->getContentSize().width, -deco->getContentSize().height));

    this->addChild(deco);

 

    auto easeAction = EaseInOut::create(MoveBy::create(0.5f, Point(-deco->getContentSize().width, deco->getContentSize().height)), 1);

    deco->runAction(Sequence::createWithTwoActions(DelayTime::create(0.5f), easeAction));

 

    return true;

}

StartScene처럼 이미지를 추가하고 에니메이션을 주었습니다.

그러나 화면전환 시간이 0.5초가 걸리므로 0.5초동안 Delay시키고 에니메이션을 동작시켰습니다.

 

디버거를 실행해 확인하도록 합니다.

 

Figure 34 실행화면

화면전환이 끝나고 에니메이션이 동작하는 것을 확인할 수 있습니다.

 

이번엔 StartScene으로 돌아갈 수 있는 버튼을 만들어보겠습니다.

 

좌측상단에 버튼을 생성하고 선택하면 StartScene으로 돌아가도록 하겠습니다.

----------StageScene.h----------

#include "cocos2d.h"

 

class StageScene : 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(StageScene);

 

    void onClickBack(Ref *object);

};

 

----------StageScene.cpp----------

 

…생략…

 

// on "init" you need to initialize your instance

bool StageScene::init()

{

    //////////////////////////////

    // 1. super init first

    if (!Layer::init())

    {

        return false;

    }

 

    //code here

 

    this->addChild(BackgroundLayer::create());

 

    //화면 크기를 가져옴

    auto winSize = Director::getInstance()->getOpenGLView()->getDesignResolutionSize();

 

    auto deco = Sprite::create("stage_bear.png");

    deco->setAnchorPoint(Point(1, 0));

    deco->setPosition(Point(winSize.width + deco->getContentSize().width, -deco->getContentSize().height));

    this->addChild(deco);

 

    auto easeAction = EaseInOut::create(MoveBy::create(0.5f, Point(-deco->getContentSize().width, deco->getContentSize().height)), 1);

    deco->runAction(Sequence::createWithTwoActions(DelayTime::create(0.5f), easeAction));

 

    auto back = MenuItemImage::create("btn_back.png", "btn_back_on.png", CC_CALLBACK_1(StageScene::onClickBack, this));

    back->setPosition(Point(30, winSize.height - 20));

 

    auto menu = Menu::create(back, NULL);

    menu->setPosition(Point::ZERO);

    this->addChild(menu);

 

    return true;

}

 

void StageScene::onClickBack(Ref *object)

{

    Director::getInstance()->popScene();

}

Back버튼을 추가했습니다.

 

디버거를 실행해 확인해보도록 합니다.

 

Figure 35 실행화면

Back 버튼을 선택하면 StartScene으로 이동되는 것을 확인할 수 있습니다.

 

다음으로 스테이지 선택 메뉴를 추가하도록 하겠습니다.

먼저 LayerColor를 생성하여 스테이지 선택 메뉴가 들어갈 자리를 잡아주도록 하겠습니다.

----------StageScene.h----------

#include "cocos2d.h"

 

class StageScene : 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(StageScene);

 

    void onClickBack(Ref *object);

 

    cocos2d::LayerColor *_stageLayer;

    void setStage();

};

 

----------StageScene.cpp----------

 

…생략…

 

// on "init" you need to initialize your instance

bool StageScene::init()

{

 

…생략…

 

    auto back = MenuItemImage::create("btn_back.png", "btn_back_on.png", CC_CALLBACK_1(StageScene::onClickBack, this));

    back->setPosition(Point(30, winSize.height - 20));

 

    auto menu = Menu::create(back, NULL);

    menu->setPosition(Point::ZERO);

    this->addChild(menu);

 

    _stageLayer = NULL;

    setStage();

 

    return true;

}

 

void StageScene::onClickBack(Ref *object)

{

    Director::getInstance()->popScene();

}

 

void StageScene::setStage(){

    if (_stageLayer){

        _stageLayer->removeAllChildrenWithCleanup(true);

        _stageLayer = NULL;

    }

 

    _stageLayer = LayerColor::create(Color4B(255, 0, 0, 255), 480, 150);

    _stageLayer->setPosition(Point(0, 100));

    this->addChild(_stageLayer);

}

LayerColor를 생성하였습니다.

 

디버거를 실행하여 크기와 위치를 확인하도록 합니다.

Figure 36 실행화면

적당한 위치에 LayerColor를 추가하였습니다.

 

레이어에 스테이지 선택 메뉴를 추가하고 콜백메소드를 등록하도록 하겠습니다.

----------StageScene.h----------

#include "cocos2d.h"

 

class StageScene : 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(StageScene);

 

    void onClickBack(Ref *object);

 

    cocos2d::LayerColor *_stageLayer;

    void setStage();

    void onClickStage(Ref *object);

};

 

----------StageScene.cpp----------

 

…생략…

 

 

void StageScene::setStage(){

    if (_stageLayer){

        _stageLayer->removeAllChildrenWithCleanup(true);

        _stageLayer = NULL;

    }

 

    _stageLayer = LayerColor::create(Color4B(255, 0, 0, 255), 480, 150);

    _stageLayer->setPosition(Point(0, 100));

    this->addChild(_stageLayer);

 

    //stage text추가

    auto stage1Text = Sprite::create("text_stage1.png");

    stage1Text->setPosition(Point(100, 130));

    _stageLayer->addChild(stage1Text);

 

    auto stage2Text = Sprite::create("text_stage2.png");

    stage2Text->setPosition(Point(240, 130));

    _stageLayer->addChild(stage2Text);

 

    auto stage3Text = Sprite::create("text_stage3.png");

    stage3Text->setPosition(Point(380, 130));

    _stageLayer->addChild(stage3Text);

 

    //메뉴 추가

    auto item1 = MenuItemImage::create("box_stage.png", "box_stage_on.png", "box_stage_lock.png", CC_CALLBACK_1(StageScene::onClickStage, this));

    item1->setPosition(Point(100, 55));

    item1->setTag(1);

 

    auto item2 = MenuItemImage::create("box_stage.png", "box_stage_on.png", "box_stage_lock.png", CC_CALLBACK_1(StageScene::onClickStage, this));

    item2->setPosition(Point(240, 55));

    item2->setTag(2);

 

    auto item3 = MenuItemImage::create("box_stage.png", "box_stage_on.png", "box_stage_lock.png", CC_CALLBACK_1(StageScene::onClickStage, this));

    item3->setPosition(Point(380, 55));

    item3->setTag(3);

 

    auto menu = Menu::create(item1, item2, item3, NULL);

    menu->setPosition(Point::ZERO);

    _stageLayer->addChild(menu);

}

 

void StageScene::onClickStage(Ref *object){

    auto clickItem = (MenuItemImage *)object;

    log("Click Tag = %d", clickItem->getTag());

}

onClickStage()를 추가하고 MenuItem에 Tag를 추가해 Tag를 이용해 어떤 stage가 선택됐는지 확인할 수 있습니다. MenuItem에는 disable이미지도 같이 추가하도록 합니다.

 

디버거를 실행해 화면을 확인하도록 합니다.

Figure 37 실행화면

적절한 위치에 메뉴가 추가되었습니다. stage메뉴들이 눌리고 tag가 정확히 넘어오는지 로그를 확인합니다.

Figure 38 선택된 스테이지의 테그를 확인하는 로그

버튼이 선택되면 tag가 onClickStage()에 제대로 넘어가는 것을 확인할 수 있습니다.

 

UI를 위치시켰으니 _stageLayer의 색상을 없애도록 하겠습니다.

----------StageScene.cpp----------

 

…생략…

 

void StageScene::setStage(){

   

    if (_stageLayer){

        _stageLayer->removeAllChildrenWithCleanup(true);

        _stageLayer = NULL;

    }

 

    _stageLayer = LayerColor::create(Color4B(0, 0, 0, 0), 480, 150);

    _stageLayer->setPosition(Point(0, 100));

    this->addChild(_stageLayer);

 

…생략…

 

}

 

디버거를 실행해 색상이 없어졌는지 확인합니다.

Figure 39 실행화면

스테이지 선택 화면 구현이 완료되었습니다.

 

8.5.2. 스테이지 잠금 체크 구현하기

스테이지가 잠겨있는지 체크하는 기능을 구현하도록 할 것입니다. 데이터베이스를 이용해도 되나 간단한 값 같은경우는 UserDefault를 이용하여 구현할 수 있습니다.

 

스테이지 1의 경우 처음부터 오픈되어있고 스테이지 2와 3은 처음에 잠겨있어야 합니다.

----------StageScene.cpp----------

 

…생략…

 

void StageScene::setStage(){

 

…생략…

 

auto menu = Menu::create(item1, item2, item3, NULL);

    menu->setPosition(Point::ZERO);

    _stageLayer->addChild(menu);

 

    UserDefault *UserDefault = UserDefault::getInstance();

    bool lock2 = UserDefault->getBoolForKey("lock2", true);

    bool lock3 = UserDefault->getBoolForKey("lock3", true);

 

    if (lock2)

        item2->setEnabled(false);

    if (lock3)

        item3->setEnabled(false);

}

UserDefault를 이용하여 lock2와 lock3의 값을 가져왔습니다. 기본값은 true로 하였습니다.

UserDefault의 값을 가져와 setEnabled을 이용하여 버튼을 사용할 수 없도록 하였습니다.

 

UserDefault의 경우 쉽게 값을 변경할 수 있고 보안에 취약합니다. 그러나 쉽게 사용할 수 있기때문에 보통 설정값 같은 것을 저장하는데 많이 사용합니다.

첫번째 파라메터는 가져올 값의 키값이고 두번째 파라메터의 값은 만약 해당 키값이 없다면 기본으로 사용할 값입니다.

 

디버거를 실행해 기능이 동작하는지 확인해봅니다.

Figure 40 실행화면

스테이지 2와 스테이지 3이 잠겨있는 것을 확인할 수 있습니다. 잠겨있는 부분은 선택이 안됩니다.

 

스테이지를 해제하는 부분은 게임이 구현되면 UserDefault의 값을 변경하여 구현할 수 있습니다.

 

8.5.3. 게임 화면으로 진입 Event 구현하기

스테이지를 선택하면 게임화면으로 진입하도록 구현하도록 하겠습니다.

 

게임화면에서 스테이지의 분기를 구현해야 하므로 필요한 파라메터를 추가하여 생성하도록 하겠습니다.

 

GameScene이라는 이름의 클래스를 Classes 폴더에 생성합니다. [5.2] 참고.

Figure 41 실행화면

GameScene.h 파일과 GameScene.cpp 파일을 생성하였으면 아래와 같이 수정합니다.

 

지금까지와 다른점은 CrerateScene()에 파라메터로 넘길 값을 선언했다는 것입니다.

----------GameScene.h----------

#include "cocos2d.h"

 

USING_NS_CC;

 

class GameScene : public cocos2d::Layer

{

public:

    // there's no 'id' in cpp, so we recommend returning the class instance pointer

    static cocos2d::Scene* createScene(Node *stageScene, int stage);

 

    // 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);

};

 

----------gameScene.cpp----------

#include "GameScene.h"

#include "BackgroundLayer.h"

 

//받은 파라메터를 담은 변수

static Node *_stageScene;

static int _stage;

 

Scene* GameScene::createScene(Node *stageScene, int stage)

{

    _stageScene = stageScene;

    _stage = stage;

 

    // '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;

    }

 

    log("stageScene %d, Stage %d", _stageScene, _stage);

 

    this->addChild(BackgroundLayer::create());

 

    return true;

}

CreateScene()에 파라메터를 추가하고 받은 파라메터를 static으로 선언된 변수에 담아주었습니다.

 

StageScene에서 GameScene을 호출하도록 하겠습니다.

----------StageScene.cpp----------

#include "StageScene.h"

#include "BackgroundLayer.h"

#include "GameScene.h"

 

USING_NS_CC;

 

…생략…

 

void StageScene::onClickStage(Ref *object){

    auto clickItem = (MenuItemImage *)object;

    log("Click Tag = %d", clickItem->getTag());

 

    log("stageScene %d", this);

 

    auto Scene = TransitionCrossFade::create(0.5f, GameScene::createScene(this, clickItem->getTag()));

    Director::getInstance()->pushScene(Scene);

}

StageScene과 clickItem의 tag를 파라메터로 추가하여 호출하였습니다.

 

CreateScene() 메소드를 확인하고 받은 변수값을 확인해보도록 합니다.

 

파라메터는 StageScene과 stage번호를 받았습니다.

 

디버거를 실행해 GameScene을 호출해보고 로그를 확인하여 파라메터가 제대로 호출되는지 확인하도록 합니다.

  

Figure 42 실행화면

게임화면이 호출되었습니다.

 

 

Figure 43 파라메터 확인 로그

로그를 확인하면 tag번호와 StageScene이 GameScene에서 받은 값이 똑같은 것을 알 수 있습니다.

 

이번 장에서는 새로운 Scene을 호출할 때 파라메터를 추가하여 값을 전달 하는 방법을 배웠습니다.


Prev | Next