본문 바로가기

cocos2d-x/pyocopang

cocos2d-x 포코팡 류 게임만들기!! - 9 - 사라질 코인들은 모두 위로~!

막 혼자 만들때는 코드가 지저분해도 뭐 나만보니까~ 상관하지 않았었는데요. 설명을 위해 다시 작업하려니까 시간이 많이 걸리네요. 처음부터 잘 생각해서 만들었어야 했는데요. 머리가 나쁘니 몸이 고생합니다 --;

이전 아이디어에 맞게 구현할 차례가 되었습니다. 
터치스크린에서 손이 떨어질 때, 사라질 코인들은 위로 보내주고 남은 코인은 순서에 맞게 빈 칸을 채워주도록 합시다.


- 9 - 사라질 코인들은 모두 위로~!

1. 아이디어 보충


지난 번에 생각한 아이디어를 실제 구현에 맞게 조금 구체화시킬 필요가 있을 것 같습니다. 별거는 없지만, 조금 다른 예제를 확인해보겠습니다.

맨 밑의 [5]번 부터 시작해서, 각 순서마다 선택되어 사라질 코인인지 확인을 하는 과정입니다. 이전에 빠뜨린 내용이 "남아있는 코인 아래에 사라질 코인이 없는 경우" 인데요. 이 때는 당연히 아무런 변경도 하지 않으면 될 것 같습니다.

- [5][4]번은 아래에 사라질 코인이 없으므로 아무것도 안하고,
- [1]번 순서에서 자신의 아래에 2개가 있으므로 [3]번과 교체
- [0]번 순서에서 [2]번과 교체하게 될 것입니다.

이제 진짜로 코드 구현을 해보도록 하겠습니다.



2. 하늘 위로 ... 죽은 코인을 위로 보내줍시다!


계속 말씀드린 것처럼, 터치가 끝나는 순간에 선택된 코인들을 처리합니다. 즉, ccTouchesEnded() 함수에서 처리하려고 하는데요. 일단 필요한 함수들을 정의하겠습니다.

/Classes/HelloWorldScene.h

class HelloWorld : public cocos2d::CCLayer
{
    ...
public:
    ...
    void moveUpDeadCoin();
    void changeCoin(int index1, int index2);
}

moveUpDeadCoin() 함수에서 주요동작들을 수행하겠습니다. 앞서 말한 내용을 그대로 구현하려고 하고요.
changeCoin() 함수는 두 개의 입력되는 코인 index를 바꿔주는 동작을 수행하겠습니다.


moveUpDeadCoin()은 다음과 같이 ccTouchesEnded() 안에 위치하게 될 것입니다.

/Classes/HelloWorldScene.cpp

void HelloWorld::ccTouchesEnded(CCSet* touches, CCEvent* event)
{
    CCTouch* touch = (CCTouch*)( touches->anyObject() );
    CCPoint location = touch->getLocation();

    clearSelectCoin();
    moveUpDeadCoin();
}

기억나실지 모르겠지만, clearSelectCoin()에서 선택된 코인들을 DEAD 상태로 만들고, setVisible(false)로 안보이게 만들기 때문에 moveUpDeadCoin()에서는 DEAD 상태의 코인들을 위로만 잘 올려주면 됩니다. 코드를 살펴보겠습니다.

/Classes/HelloWorldScene.cpp

void HelloWorld::moveUpDeadCoin()
{
    int deadCoinNum = 0;
    int bottomPos = 0;
    GameCoin* coin;

    for (int x = 0; x < BOARDX; x++) {
        bottomPos = ((x + 1) * BOARDY) - 1;
        deadCoinNum = 0;

        for (int y = bottomPos; y > bottomPos - BOARDY; y--) {
            coin = (GameCoin*)_gameCoins->objectAtIndex(y);
            if (coin->getState() == GameCoin::DEAD) {
                deadCoinNum++;
                continue;
            }

            if (deadCoinNum > 0)
                changeCoin(y, y + deadCoinNum);
        }
    }
}

일단 간단하게 변수들의 용도를 살펴보면,

- int deadCoinNum : 내 밑의 DEAD 상태의 코인 수
- int bottomPos : 현재 라인의 제일 밑의 Position
- GameCoin* coin : 현재 코인의 정보 확인을 위해

이렇게 사용됩니다.

전체 라인을 모두 확인해야하기 때문에, 바깥쪽 for문을 사용하고요. 실제 라인 상의 코인 변경은 내부 for문을 통해 이루어지게 됩니다. 각 라인의 코인 변경 전에 bottomPos, deadCoinNum을 초기화합니다.

전체 coin들이 1차원 배열 형태로 저장되어 있기 때문에, 초기 position을 정하는 과정이 필요합니다.

- 첫 번째 줄의 bottomPos = ((0 + 1) * 6) - 1 = 5
- 두 번째 줄의 bottomPos = ((1 + 1) * 6) - 1 = 11
- ...

이런 식으로 각 라인의 가장 아래 위치를 찾을 수 있습니다. 이 위치부터 역순으로 

5 -> 4 -> 3 -> ... -> 0
11 -> 10 -> 9 -> ... -> 6

과 같이 각 라인을 살펴봅니다. 여기서부터는 생각해놓은 아이디어와 동일하게 구현합니다. 

- 현재 코인이 DEAD 상태이면 deadCountNum을 '1' 증가시키고 끝! 다음꺼.
- 내 밑에 DEAD 상태의 코인이 1개 이상이면, 나와 그 위치의 코인을 changeCoin()으로 변경


그럼, changeCoin() 코드를 보시죠.

/Classes/HelloWorldScene.cpp

void HelloWorld::changeCoin(int index1, int index2)
{
    CCPoint tmpPos;
    GameCoin* tmpCoin1 = (GameCoin*)_gameCoins->objectAtIndex(index1);
    GameCoin* tmpCoin2 = (GameCoin*)_gameCoins->objectAtIndex(index2);

    tmpPos = tmpCoin2->getPosition();
    tmpCoin2->setPosition(tmpCoin1->getPosition());
    tmpCoin1->setPosition(tmpPos);

    _gameCoins->exchangeObjectAtIndex(index1, index2);
}

기본적인 내용은 CCArray의 exchangeObjectAtIndex()를 통해 입력된 두 개의 Object를 변경하는 것입니다. 하지만, 배열 내에서 Object index를 변경해봤자 실제 Position을 바꾸지 않으면 화면에 보이는 내용은 동일하기 때문에, 게임 상으로는 변경 내용을 확인할 수가 없습니다. 따라서, 두 코인의 실제 위치도 함께 변경해줍니다.



3. 동작확인


잘 만들어졌는지 한 번 확인해보겠습니다.

[ 전 ]   [ 후 ]

전, 후 이미지 비교입니다. 동영상은 지금 올릴 여건이 안되서 이미지로 대신합니다. [전] 이미지가 처음화면이고, 제일 아래 줄의 연속된 세 개의 '파란'코인을 선택한 이후가 [후] 이미지입니다. 애니메이션 동작이 없어서 눈 깜짝할 사이에 변경되긴 하지만 자세히 따져보니 잘 이동한 것을 확인할 수 있습니다. 



다음에는 코인이 떨어지는 것 처럼 보이게 애니메이션 동작을 추가하고, DEAD 상태의 코인 위치에 새 코인을 추가할 방법을 생각해보도록 하겠습니다.

감사합니다! 끝.