기본적으로는 GameLayer의 Update()에서 주기적으로 장애물(Huddle)을 생성해주고

Huddle에서 충돌체크를 하는 것이 전부이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Huddle : public Node
{
private:
    int scorecheck;
 
public:
    Huddle();
    ~Huddle();
 
    bool init();
    void update(float dt);
    CREATE_FUNC(Huddle);
};
cs


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
#include "Huddle.h"
#include "GameLayer.h"
 
Huddle::Huddle()
{
    // 점수를 증가시킬지 체크하는 변수(플레이중인지 죽었는지)
    scorecheck = 0;
}
 
 
Huddle::~Huddle()
{
}
 
bool Huddle::init()
{
    // 위와 아래 허들을 2개 만들어준다.
    Sprite* pSprite1 = Sprite::create("res/r2.png");
    Sprite* pSprite2 = Sprite::create("res/r2.png");
    
    // 허들의 위치 랜덤을 위해 rand를 돌려줌. 
    // 0~36000까지 구한뒤 100으로 나누면 0~360까지 소수점2자리까지 랜덤가능
    float y = ((float)(rand() % 36000)) / 100.0f;
 
    // 노드 자체의 포지션을 설정
    setPosition(13000);
    //이미지 세로가 700, 가운데가 350 -> 맨아래가 350, 꼭대기는 1070, 가운데값 710
    pSprite1->setPosition(0, y + 710);    
    // 크기 720 + 거리350(간격)
    pSprite2->setPosition(0, pSprite1->getPositionY() - 1070); 
 
    addChild(pSprite1, 0"UP");
    addChild(pSprite2, 0"DOWN");
 
    scheduleUpdate();
    return true;
}
 
void Huddle::update(float dt)
{
    // 허들 전체삭제를 위해 노드의 자식으로 넣었기에 부모의 부모를 불러옴    
    auto gl = (GameLayer*)getParent()->getParent();        
    auto pHero = gl->getChildByTag(105);
    auto up = getChildByName("UP");
    auto down = getChildByName("DOWN");
 
    
    if (pHero && up && down)
    {
        Rect heroRect = pHero->getBoundingBox();
        Rect upRect = up->getBoundingBox();
        Rect downRect = down->getBoundingBox();
        // 노드 자체를 옮겨줬으므로 실제 위치를 찾기 위해 Rect에 노드의 position을 더해줌
        upRect.origin += getPosition();
        downRect.origin += getPosition();
 
        if (heroRect.intersectsRect(upRect) || heroRect.intersectsRect(downRect))
        {
            if (scorecheck == 0)
            { 
                gl->hstop();
                pHero->setVisible(false);
            }
            // 충돌 후 점수체크 안하게 변수 변경
            scorecheck = 1;
        }
 
        // 충돌이 아닐 경우
        if (scorecheck == 0)
        {
            // 캐릭터의 x값이 장애물 x값을 넘으면 점수를 올려줌.
            if (pHero->getPositionX() > upRect.origin.x)
            {
                // 같은 허들이 점수를 여러번 올려주지않도록 변수값을 바꿔줌.
                scorecheck = 1;
                gl->scoreup();
            }
        }
    }
 
    // 캐릭터가 죽지않았다면 허들을 이동.  즉 캐릭터가 죽으면 허들의 이동을 멈춤
    if(!gl->getdeath())                                
        setPositionX(getPositionX() - 350.0f * dt);
 
    // 허들(노드)의 x좌표가 -70보다 내려가면 부모로부터 삭제
    if (getPositionX() < -70)
        this->removeFromParent();
}
cs

Huddle은 생성만 해주면 스스로 이동하며 충돌시 GameLayer의 함수를 호출하여 점수증가 및 게임을 멈춘다.




기타 구현 부분

메뉴

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class menu : public Node
{
public:
    menu();
    ~menu();
 
    bool init();
    CREATE_FUNC(menu);
 
    void printstart();
    void printrestart();
    void startmenuCallback(Object *sender);
    void restartmenuCallback(Object *sender);
};
cs


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
#include "menu.h"
#include "GameLayer.h"
 
 
menu::menu()
{
}
 
 
menu::~menu()
{
}
 
bool menu::init()
{
    return true;
}
 
void menu::printstart()
{
    // 라벨을 생성
    auto label1 = Label::createWithSystemFont("PLAY""ariel"50);
    label1->setColor(Color3B::BLACK);
 
    // 라벨로 메뉴아이템을 만들어 메뉴를 생성함
    auto item1 = MenuItemLabel::create(label1, CC_CALLBACK_1(menu::startmenuCallback, this));
    auto menu = Menu::create(item1, NULL);
    menu->alignItemsVertically();
    addChild(menu, 099);
}
 
void menu::printrestart()
{
    auto gl = (GameLayer*)getParent();
    if (gl->getdeath())
    {
        auto label2 = Label::createWithSystemFont("RESTART""ariel"50);
        label2->setColor(Color3B::BLACK);
 
        auto item2 = MenuItemLabel::create(label2, CC_CALLBACK_1(menu::restartmenuCallback, this));
        auto menu2 = Menu::create(item2, NULL);
        menu2->alignItemsVertically();
        addChild(menu2,0,100);
    }
}
 
// 스타트 메뉴를 눌렀을 때
void menu::startmenuCallback(Object * sender)
{
    auto item = (MenuItem*)sender;
    auto gl = (GameLayer*)getParent();
    gl->enablestart();
 
    // 메뉴를 지워줌
    auto pmenu1 = getChildByTag(99);
    pmenu1->removeFromParent();
}
 
// 리스타트 메뉴를 눌렀을 때
void menu::restartmenuCallback(Object * sender)    
{
    auto item = (MenuItem*)sender;
    auto gl = (GameLayer*)getParent();
    // 허들 삭제, 재시작
    gl->sethuddleerase();
    gl->enablestart();
    
    auto pmenu2 = getChildByTag(100);
    pmenu2->removeFromParent();
}
 
cs

메뉴도 크게 뭐없고, 라벨을 생성하여 라벨로 메뉴를 만들어 준 뒤 해당 메뉴를 누르면 필요한 동작을 하게끔 하였다.



알파벳 출력

1
2
3
4
5
6
7
8
9
10
11
12
class PrintAlpha : public Node
{
public:
    PrintAlpha();
    ~PrintAlpha();
 
    bool init();
    void update(float dt);
    void print(char* text);
    void print2(char* text);
    CREATE_FUNC(PrintAlpha);
};
cs


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
#include "PrintAlpha.h"
 
PrintAlpha::PrintAlpha()
{
}
 
 
PrintAlpha::~PrintAlpha()
{
}
 
bool PrintAlpha::init()
{
    scheduleUpdate();
    return true;
}
 
void PrintAlpha::update(float dt)
{
}
 
// 알파벳을 출력하는 함수
void PrintAlpha::print(char* text)
{
    int i = 0;
    // char배열을 쪼개서 하나씩 해당하는 알파벳 이미지를 출력
    while (text[i])
    {
        if (text[i] != ' ')
        {
            std::string fileName = StringUtils::format("res/alphabet/%c.png", text[i]);
            Sprite* pSprite = Sprite::create(fileName);
            // 예외처리. pSprite가 널이 아닐경우에만 
            if (pSprite)
            {
                addChild(pSprite, 01 * i);
                pSprite->setPosition((1030 + 22 * i), 680);
            }
        }
        i++;
    }
}
 
// 똑같으나 하이스코어 출력용. 자리가 다름
void PrintAlpha::print2(char* text)
{
    int i = 0;
    while (text[i])
    {
        if (text[i] != ' ')
        {
            std::string fileName = StringUtils::format("res/alphabet2/%c.png", text[i]);
            Sprite* pSprite = Sprite::create(fileName);
            if (pSprite)
            {
                addChild(pSprite, 01 * i);
                pSprite->setPosition((20 + 22 * i), 680);
            }
        }
        i++;
    }
}
 
 
cs



숫자 이미지 출력

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#pragma once
#include <cocos2d.h>
USING_NS_CC;
 
class PrintNum : public Node
{
public:
    PrintNum();
    ~PrintNum();
 
    bool init();
    void update(float dt);
    void print(int num);
    void print2(int num);
    void leftP(int num, int count);
    void leftP2(int num, int count);
    void rightP(int num, int count);
 
    static PrintNum* create(int number);
    bool init(int number);
    int 자릿수(int number);
 
    CREATE_FUNC(PrintNum);
};
cs


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
#include "PrintNum.h"
#include "GameLayer.h"
 
 
PrintNum::PrintNum()
{
}
 
 
PrintNum::~PrintNum()
{
}
 
bool PrintNum::init()
{
    scheduleUpdate();
    return true;
}
 
void PrintNum::update(float dt)
{
    
}
 
// 숫자(스코어)를 출력
void PrintNum::print(int num)
{
    // 출력하기전 기존에 있던 자식(숫자 이미지)를 전부 제거하고 새로 출력
    removeAllChildren();
 
    // 0이면 0을 출력 (왼쪽정렬 기준)
    if (num == 0)
    {
        Sprite* pSprite = Sprite::create("res/number/0.png");
        addChild(pSprite, 0,0);
        pSprite->setPosition(1160680);
    }
    // 0이 아니면
    else
    {
        int n = 1;                        
        int count = 0;
        while (num / n > 0)
        {
            n *= 10;
            count++;
        }
 
        // 숫자의 자리수(count)를 구하여 왼쪽에서부터 출력
 
        leftP(num, count);
        //rightP(num, count);
    }
}
 
// 하이스코어 출력용. 위치만 다름
void PrintNum::print2(int num)
{
    removeAllChildren();
 
    if (num == 0)
    {
        Sprite* pSprite = Sprite::create("res/number2/0.png");
        addChild(pSprite, 00);
        pSprite->setPosition(180680);
    }
    else
    {
        int n = 1;
        int count = 0;
        while (num / n > 0)
        {
            n *= 10;
            count++;
        }
 
        leftP2(num, count);
    }
}
 
// 왼쪽부터 출력하는 함수
void PrintNum::leftP(int num, int count)
{
    // 감소되기 전 숫자길이
    int pcount = count;
 
    for (int i = count; i > 0; i--)
    {
        int devide = num / pow(10, i - 1);            // 5137이면 1000으로 나눠 첫자리인 5를 뽑아냄.
        num -= devide * (pow(10, i - 1));            // 이후 5000을 빼줘서 137만남김. 이런방식으로 반복
 
        std::string fileName = StringUtils::format("res/number/%d.png", devide);
        Sprite* pSprite = Sprite::create(fileName);
        addChild(pSprite, 01 * i);
        pSprite->setPosition(1160 + 20 * (pcount - i), 680);
    }
}
 
// 하이스코어용
void PrintNum::leftP2(int num, int count)
{
    int pcount = count;
 
    for (int i = count; i > 0; i--)
    {
        int devide = num / pow(10, i - 1);
        num -= devide * (pow(10, i - 1));
 
        std::string fileName = StringUtils::format("res/number2/%d.png", devide);
        Sprite* pSprite = Sprite::create(fileName);
        addChild(pSprite, 01 * i);
        pSprite->setPosition(180 + 20 * (pcount - i), 680);
    }
}
 
// 오른쪽부터 출력하는 함수
void PrintNum::rightP(int num, int count)
{
    // 10을 나눈 나머지를 빼고 다시 돌림
    for (int i = 1; i <= count; i++)
    {
        int devide = num % 10;
        num /= 10;
 
        std::string dev = std::to_string(devide);
        std::string add = "res/number/" + dev + ".png";
 
        Sprite* pSprite = Sprite::create(add);
        addChild(pSprite, 01 * i);
        pSprite->setPosition(1240 - 20 * i, 680);
    }
}
 
// 아래부터는 교수님이 짜신 방식. create를 오버로딩해 인자를 받게끔함.
PrintNum * PrintNum::create(int number)
{
    PrintNum* pNumber = new PrintNum;
    bool bReturn = pNumber->init(number);
    if (bReturn == false)
        return NULL;
    // new로 만들었기에 delete를 해줘야하지만 autorelease()를 하면 따로 delete 하지않아도 부모가 죽으면 죽게함.
    pNumber->autorelease();    
    return pNumber;
}
 
bool PrintNum::init(int number)
{
    int count = 자릿수(number);
    int dev = pow(10, count - 1);
    // 이미지 위치 조정을 위해
    float posX = 0;
    while (count != 0)
    {
        int num = number / dev;
        // 이렇게하면 printf처럼 %d에 뒤에있는 숫자가들어감
        std::string fileName = StringUtils::format("res/number/%d.png", num);
        Sprite* pNumSprite = Sprite::create(fileName);
        addChild(pNumSprite);
        pNumSprite->setPositionX(posX);
 
        number -= num * pow(10, count - 1);
        count--;
        dev = pow(10, count - 1);
        posX += 16.0f;
    }
    
    return true;
}
 
int PrintNum::자릿수(int number)
{
    // 0이면 1자리를 리턴
    if (number <= 0)
        return 1;
    int count = 1;
    int p = pow(10, count);
    while (number / p)
    {
        count++;
        p = pow(10, count);
    }
    return count;
}
 
 
cs





이렇게 Flappy Bird가 완성되었다.

실행화면

처음 만든 게임이라 잘 만들지는 못했지만 어느정도 기본 기능은 구현된 것 같다.


flappybird.mp4

'프로그래밍 공부 > cocos2d-x' 카테고리의 다른 글

애니메이션 사용하기  (0) 2018.03.28
라벨, 메뉴 사용하기  (0) 2018.03.28
Flappy Bird 게임 만들기 - 1  (0) 2018.03.27
충돌체크하기  (0) 2018.03.27
터치, 마우스, 키보드 사용하기  (0) 2018.03.27
Posted by misty_
,