안드로이드/cocos2d-x2016. 1. 18. 02:07

시작하기에 앞서

 그 동안 쓰진 않았는데 미리 만들어 두었던 것들에서 많은 것들을 수정했다.

그래서 일단 전체소스부터 공개하고 시작하겠다.

 

CGameLayer.cpp

- 번거로워서 삭제했다. CGameLayer.h에 병합시켜 인라인 함수로 쓰기로 했다.

  인라인으로 쓰는게 좋은 버릇은 아니다.

  근데 한눈에 보기 좋게 모아놓기는 좋다.

 

CGameLayer.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
#ifndef PuzzleGame_GameLayer
#define PuzzleGame_GameLayer
 
#include "Common.h"
#include "GameObject.h"
#include <stdio.h>
#include <string.h>
 
class CGameLayer : public cocos2d::Layer {
public:
    // [ 기본 ]
    // ==============================================================================================================
    static cocos2d::Scene* createScene()  {
        auto pScene = Scene::create();
        auto pLayer = CGameLayer::create();
 
        pScene->addChild(pLayer);
 
        return pScene;
    }
 
    virtual bool init() {
        if (!Layer::init()) { return false; }
 
        // 배경 이미지 출력
        cocos2d::Sprite* pBackgroundSprite = cocos2d::Sprite::create("background.png");
        pBackgroundSprite->setPosition(cocos2d::CCPointZero);
        pBackgroundSprite->setAnchorPoint(ccp((float)0, (float)0));
        addChild(pBackgroundSprite);
 
 
        // 터치 초기화
        EventListenerTouchAllAtOnce* listener = EventListenerTouchAllAtOnce::create();
        listener->onTouchesBegan = CC_CALLBACK_2(CGameLayer::onTouchesBegan, this);
        listener->onTouchesMoved = CC_CALLBACK_2(CGameLayer::onTouchesMoved, this);
        listener->onTouchesEnded = CC_CALLBACK_2(CGameLayer::onTouchesEnded, this);
        _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
 
 
        m_winSize = Director::sharedDirector()->getWinSize();
        startGame();
        return true;
    }
 
    void startGame()  {
        for (int x = 0; x < COLUMN_COUNT+1; x++) {
            for (int y = 0; y < ROW_COUNT+1; y++) {
                m_pBoard[x][y] = NULL;
            }
        }
 
        srand(time(NULL));
        for (int x = 0; x < COLUMN_COUNT; x++) {
            for (int y = 0; y < ROW_COUNT; y++) {
                if (x == COLUMN_COUNT - 1 && y % 2 != 0continue;
 
                int type = rand() % TYPE_COUNT;
 
                Sprite* pGameObjectBack = Sprite::create("blockBack.png");
                CGameObject* pGameObject = CGameObject::Create(type);
                m_pBoard[x][y] = pGameObject;
 
 
                // debug --------------------------------------------------------------
                // 각 블록의 좌표를 출력
                std::string str, str2, str3;
                str = std::to_string(x);
                str2 = ",";
                str3 = std::to_string(y);
                str.append(str2);
                str.append(str3);
                auto label = LabelTTF::create(str, "Arial"50);
                Point pos; pos.set(Common::ComputeXY(x, y));
                label->setPosition(pos);
                // end of debug -------------------------------------------------------
 
 
                pGameObjectBack->setAnchorPoint(ccp(0.5f, 0.5f));
                pGameObjectBack->setPosition(Common::ComputeXY(x, y));
                pGameObject->setAnchorPoint(ccp(0.5f, 0.5f));
                pGameObject->setPosition(Common::ComputeXY(x, y));
                pGameObject->SetGameLayer(this);
 
                addChild(pGameObjectBack, 1);
                addChild(pGameObject, 2);
                addChild(label, 3);
            }
        }
 
        // 변수 초기화
        m_bTouchStarted = false;
    }
    CREATE_FUNC(CGameLayer);
 
    // [ 터치 초기화 ]
    void onTouchesBegan(const std::vector<cocos2d::Touch*>&pTouches, cocos2d::Event* pEvent) {
        if (!m_bTouchStarted) {
            Touch* pTouch = (Touch*)pTouches.back();
            Point point = pTouch->getLocation();
 
            m_gestureStartBoardX = Common::ComputeBoardX(point);
            m_gestureStartBoardY = Common::ComputeBoardY(point);
 
            m_bTouchStarted = true;
        }
    }
    void onTouchesMoved(const std::vector<cocos2d::Touch*>&pTouches, cocos2d::Event* pEvent) {
        if (m_bTouchStarted) {
            Touch* pTouch = (Touch*)pTouches.back();
            Point point = pTouch->getLocation();
        }
    }
    void onTouchesEnded(const std::vector<cocos2d::Touch*>&pTouches, cocos2d::Event* pEvent) {
        if (m_bTouchStarted) {
            Touch* pTouch = (Touch*)pTouches.back();
            Point point = pTouch->getLocation();
 
            m_bTouchStarted = false;
 
            int boardX = Common::ComputeBoardX(point);
            int boardY = Common::ComputeBoardY(point);
 
            if (m_gestureStartBoardX != boardX || m_gestureStartBoardY != boardY)
            if (isAdjacent(m_gestureStartBoardX, m_gestureStartBoardY, boardX, boardY))
                swapObjects(m_gestureStartBoardX, m_gestureStartBoardY, boardX, boardY);
        }
    }
 
protected:
    CGameObject* m_pBoard[COLUMN_COUNT+1][ROW_COUNT+1]; // 기존과 달리 +1씩 더 해줬다. 매칭검사할때 범위를 벗어나서 이렇게 때웠다
    Size m_winSize;
 
    bool m_bTouchStarted;    // 터치가 시작되었는지
    int m_gestureStartBoardX;
    int m_gestureStartBoardY; // 터치가 시작된 블록이 어느 블록인지
    // ==============================================================================================================
 
 
 
 
    // [ 제어 - 블록 매칭 ]
    // ==============================================================================================================
public:
    bool isAdjacent(int x1, int y1, int x2, int y2) {
        if (y1 % 2 == 0) {
            if ((x1 == x2) && (y1 - 2 == y2 || y1 - 1 == y2 || y1 + 1 == y2 || y1 + 2 == y2))
                return true;
            if ((x1 - 1 == x2) && (y1 - 1 == y2 || y1 + 1 == y2))
                return true;
        }
        else {
            if ((x1 == x2) && (y1 - 2 == y2 || y1 - 1 == y2 || y1 + 1 == y2 || y1 + 2 == y2))
                return true;
            if ((x1 + 1 == x2) && (y1 - 1 == y2 || y1 + 1 == y2))
                return true;
        }
        return false;
    }    // 두 블록이 서로 인접한 블록인지
    void swapObjects(int x1, int y1, int x2, int y2) {
        CGameObject* pTemp = m_pBoard[x1][y1];
        m_pBoard[x1][y1] = m_pBoard[x2][y2];
        m_pBoard[x2][y2] = pTemp;
 
        m_pBoard[x1][y1]->SetTargetBoardX(x1);
        m_pBoard[x1][y1]->SetTargetBoardY(y1);
        m_pBoard[x2][y2]->SetTargetBoardX(x2);
        m_pBoard[x2][y2]->SetTargetBoardY(y2);
 
        m_pBoard[x1][y1]->ProcessSliding();
        m_pBoard[x2][y2]->ProcessSliding();
    }
    
    void SlidingCompleteEvent() {
        if (searchMacthedStreak());
        clearRemovealObject();
    }
    
    // 매칭된 블록이 있는지 검사하고 제거대상으로 설정
    bool searchMacthedStreak() {
        bool isAnyMatched = false;
        int x = 0, y = 0;
 
        // 1행에서 검사 (0,0 / 1,0, / 2,0 / 3,0 / 4,0)
        while (true) {
            if (x < 0 || x >= COLUMN_COUNT) break;
            if (y < 0 || y >= ROW_COUNT) break;
            if (!m_pBoard[x][y]) break;
 
            if (streakWay1(x, y) | streakWay2(x, y) | streakWay3(x, y)) isAnyMatched = true;
            if (streakWay3(x, y)) isAnyMatched = true;
            x++;
        }
 
        // 2행에서 검사(0,1 / 1,1 / 2,1 / 3, 1)
        x = 0; y = 1;
        while (true) {
            if (x < 0 || x >= COLUMN_COUNT) break;
            if (y < 0 || y >= ROW_COUNT) break;
            if (!m_pBoard[x][y]) break;
 
            if (streakWay2(x, y)) isAnyMatched = true;
            x++;
        }
 
        // 좌측 열에서 검사
        x = 0; y = 2;
        while (true) {
            if (x < 0 || x >= COLUMN_COUNT) break;
            if (y < 0 || y >= ROW_COUNT) break;
            if (!m_pBoard[x][y]) break;
 
            if (streakWay1(x, y)) isAnyMatched = true;
            y += 2;
        }
 
        // 우측 열에서 검사
        x = COLUMN_COUNT - 1; y = 2;
        while (true) {
            if (x < 0 || x >= COLUMN_COUNT) break;
            if (y < 0 || y >= ROW_COUNT) break;
            if (!m_pBoard[x][y]) break;
 
            if (streakWay3(x, y)) isAnyMatched = true;
            y += 2;
        }
 
        return isAnyMatched;
    }
    // 제거 대상 블록을 처리
    void clearRemovealObject() {
        for (int x = 0; x < COLUMN_COUNT; x++) {
            for (int y = 0; y < ROW_COUNT; y++) {
                if (x == COLUMN_COUNT - 1 && y % 2 != 0continue;
                if (m_pBoard[x][y]->isRemovalObject()) {
                    m_pBoard[x][y]->setVisible(false);
                }
            }
        }
    }
 
protected:
    // 매칭 라인 - 좌대각↘ 1줄 검사
    bool streakWay1(int x, int y) {
        int i = 0;
        int countMatch = 1;
        bool isAnyMatched = false;
 
        int j = 0;
        while (true) {
            j++;
            if (x < 0 || x >= COLUMN_COUNT) break;
            if (y < 0 || y >= ROW_COUNT) break;
            if (!m_pBoard[x][y]) break;
 
            int nextX = x;
            int nextY = y + 1;
            i = 1 - i;
            if (i % 2 == 0) nextX = x + 1;
 
            if (countMatch > 2) {
                m_pBoard[x][y]->setRemovalObject();
                m_pBoard[(i % 2 == 0) ? x : x - 1][y - 1]->setRemovalObject();
                m_pBoard[(i % 2 == 0) ? x-1 : x - 1][y - 2]->setRemovalObject();
                isAnyMatched = true;
            }
 
            if (checkType(m_pBoard[x][y]->GetType(), nextX, nextY))
                countMatch++;
            else countMatch = 1;
 
            x = nextX;
            y = nextY;
        }
        return isAnyMatched;
    }
    // 매칭 라인 - 수직↓ 1줄 검사
    bool streakWay2(int x, int y) {
        int i = 1;
        int countMatch = 1;
        bool isAnyMatched = false;
        while (true) {
            if (x < 0 || x >= COLUMN_COUNT) break;
            if (y < 0 || y >= ROW_COUNT) break;
            if (!m_pBoard[x][y]) break;
 
            int nextX = x;
            int nextY = y + 2;
 
            if (countMatch > 2) {
                m_pBoard[x][y]->setRemovalObject();
                m_pBoard[x][y - 2]->setRemovalObject();
                m_pBoard[x][y - 4]->setRemovalObject();
                isAnyMatched = true;
            }
 
            if (checkType(m_pBoard[x][y]->GetType(), nextX, nextY))
                countMatch++;
            else countMatch = 1;
 
            x = nextX;
            y = nextY;
        }
        return isAnyMatched;
    }
    // 매칭 라인 - 우대각↙ 1줄 검사
    bool streakWay3(int x, int y) {
        int i = 1;
        int countMatch = 1;
        bool isAnyMatched = false;
        while (true) {
            if (x < 0 || x >= COLUMN_COUNT) break;
            if (y < 0 || y >= ROW_COUNT) break;
            if (!m_pBoard[x][y]) break;
 
            int nextX = x;
            int nextY = y + 1;
            i = 1 - i;
            if (i % 2 == 0) nextX = x - 1;
 
            if (countMatch > 2) {
                m_pBoard[x][y]->setRemovalObject();
                m_pBoard[(i % 2 == 0) ? x : x + 1][y - 1]->setRemovalObject();
                m_pBoard[(i % 2 == 0) ? x+1 : x + 1][y - 2]->setRemovalObject();
                isAnyMatched = true;
            }
 
            if (checkType(m_pBoard[x][y]->GetType(), nextX, nextY))
                countMatch++;
            else countMatch = 1;
 
            x = nextX;
            y = nextY;
        }
        return isAnyMatched;
    }
    // 블록 타입 검사
    bool checkType(int type, int x, int y) {
        if (x < 0 || x >= COLUMN_COUNT) return false;
        if (y < 0 || y >= ROW_COUNT) return false;
        if (!m_pBoard[x][y]) return 0;
 
        return (type == m_pBoard[x][y]->GetType());
    }
    // ==============================================================================================================
 
};
#endif

cs

 

 

CGameObject.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include "GameObject.h"
#include "GameLayer.h"
 
USING_NS_CC;
 
CGameObject* CGameObject::Create(int type) {
    static std::string objectNames[TYPE_COUNT] = {
        "blockBlue.png",
        "blockBrown.png",
        "blockGreen.png",
        "blockPink.png",
        "blockWhite.png", // 기존엔 흰색을 Green으로 바꿔놨는데 이재  다시 돌려놨다.
        "blockYellow.png"
    };
    if (type<0 || type>TYPE_COUNT - 1return NULL;
    CGameObject* pGameObject = CGameObject::create(objectNames[type].c_str(),
        CCRectMake(0.0f, 0.0f, OBJECT_WIDTH, OBJECT_HEIGHT));
    pGameObject->SetType(type); // 이걸 빠뜨렸었다.
    return pGameObject;
}
 
CGameObject* CGameObject::create(const char* pszFileName, const CCRect& rect) {
    CGameObject* pSprite = new CGameObject();
    if (pSprite && pSprite->initWithFile(pszFileName, rect)) {
        pSprite->autorelease();
        return pSprite;
    }
    CC_SAFE_DELETE(pSprite);
    return NULL;
}
 
 
void CGameObject::ProcessSliding() {
    Point position = getPosition();
 
    m_prevBoardX = Common::ComputeBoardX(position);
    m_prevBoardY = Common::ComputeBoardY(position);
 
    Point targetPosition = Common::ComputeXY(m_targetBoardX, m_targetBoardY);
    MoveBy* pMoveBy = MoveBy::create(0.15f, ccp(targetPosition.x - position.x, targetPosition.y - position.y));
 
    FiniteTimeAction* pAction = Sequence::create(pMoveBy,
        CallFunc::create(this, callfunc_selector(CGameObject::SlidingCompleteHandler)), NULL);
    runAction(pAction);
}
void CGameObject::SlidingCompleteHandler(){
    m_pGameLayer->SlidingCompleteEvent();
};
 
cs

 

 

CGameObject.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
 
#ifndef PuzzleGame_GameObject
#define PuzzleGame_GameObject
 
#include "Common.h"
 
class CGameLayer;
 
class CGameObject : public Sprite {
public:
    CGameObject() {
        m_removalObject = false;
        m_pGameLayer = NULL;
    }
    virtual ~CGameObject() {}
 
    static CGameObject* Create(int type);
    
    int GetType()            { return m_type; }
    void SetType(int type)    { m_type = type; }
    void SetGameLayer(CGameLayer* pGameLayer) { m_pGameLayer = pGameLayer; }
 
protected:
    int m_type;
    CGameLayer* m_pGameLayer;
 
    static CGameObject* create(const char* pszFileName, const CCRect& rect);
 
 
 
    // [ 제어 - 블록 매칭 ]
    // ==============================================================================================================
public:
    int GetTargetBoardX() { return m_targetBoardX; }
    void SetTargetBoardX(int x) { m_targetBoardX = x; }
    int GetTargetBoardY() { return m_targetBoardY; }
    void SetTargetBoardY(int y) { m_targetBoardY = y; }
    void ProcessSliding();
    void SlidingCompleteHandler();
 
    bool isRemovalObject() { return m_removalObject; }
    void setRemovalObject() { m_removalObject = true; }
 
private:
    int m_prevBoardX;
    int m_prevBoardY;        // 액션-이동 이동 전 좌표
    int m_targetBoardX;
    int m_targetBoardY;        // 액션-이동 목표 좌표
    
    bool m_removalObject;        // 매칭 - 삭제대상
    // ==============================================================================================================
};
 
 
#endif
cs

 

 

작동 순서

CGameLayer::swapObjects()에서 블록 교환을 시도

CGameObject::ProcessSliding()이 호출되어 블록을 교환하고 이동 액션을 재생

FiniteTimeAction* pAction = Sequence::create()에 의해 액션이 끝나면 CGameObject::SlidingCompleteHandler()가 호출됨

 

CGameObject::SlidingCompleteHandler()가 바로 이어서

CGameLayer::SlidingCompleteEvent()를 호출하므로써 블록 교환이 끝났음을 콜백함.

 

이재부터 블록 매칭 검사

콜백을 받은 SlidingCompleteEvent()에서 searchMacthedStreak()를 호출하는데

1행, 2행, 좌측열, 우측열에서 검사라는게 있다.

그 전에 거기서 사용하는 streakWay1, 2, 3을 먼저 봐야하는데

 

각각 인자로 받은 x,y에서부터 좌대각↘ , 수직↓, 우대각↙방향으로 1줄을 검사하는 것이다.

해당 방향으로 움직이면서 연속으로 3개 이상 같은 블록이 나오면 해당 블록들을 "제거 대상"으로 만든다.

 

다시 1행, 2행, 좌측열, 우측열검사로 돌아가서

streakWay1, 2, 3은 한번에 1줄만 검사하게 되있는데

streakWay에 전달하는 인자 x,y는 모든 구역을 다 넣는 대신

가장 바깥쪽 테두리만에서만 수행하면 된다.

 

이런 느낌이다.

 

다시 SlidingCompleteEvent()로 돌아가서

searchMacthedStreak()다음에 있는 clearRemovealObject()는

제거 대상이 된 블록들을 제거하는 역할을 한다.

제거 대상 정보는 CGameObjeck즉 블록 본인이 가지고 있으며

 

이 예제에서는 실제로 제거하는 것이 아니라

setVisible(false)를 통해 눈에만 안보이게 해놨다.

 

결과물

 

 

 

 

문제점

위 GIF를 보면 블록들이 한번에 와장창 없어지는 경우가 있다.

바로 처음 블록이 생성될 때 부터 3개 이상이 연결되 있는 경우다.

해당 경우를 제외하곤 5개가 연결되든 X자로 연결되든 문제없이 지워짐을 볼 수 있다.

 

다음 게시물에서 계속...

Posted by gharlic