기본적으로는 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(1300, 0); //이미지 세로가 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, 0, 99); } 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, 0, 1 * 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, 0, 1 * 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(1160, 680); } // 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, 0, 0); pSprite->setPosition(180, 680); } 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, 0, 1 * 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, 0, 1 * 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, 0, 1 * 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가 완성되었다.
실행화면
처음 만든 게임이라 잘 만들지는 못했지만 어느정도 기본 기능은 구현된 것 같다.
'프로그래밍 공부 > 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 |