원글 주소 : http://horns.tistory.com/9
-6- 인접 코인 인식
1. 처음 터치된 위치의 코인을 확인
HelloWorldScene.cpp
checkPushGameCoin에서 아래 내용을 삭제합니다.
int HelloWorld::checkPushGameCoin(Point &touchPos) { int index; GameCoin *tmpCoin; for (index = 0; index < _gameCoins->count(); index++) { tmpCoin = (GameCoin*) _gameCoins->objectAtIndex(index); if (tmpCoin->boundingBox().containsPoint(touchPos)) { if (tmpCoin->isVisible() == true) { tmpCoin->setVisible(false); } else { tmpCoin->setVisible(true); } return index; } } return -1; }
HelloWorldScene.h
다음과 같이 변수를 추가합니다.
class HelloWorld : public cocos2d::Layer { private: Size _screenSize; Action* _enemyMove; Action* _treeMove; Array* _gameCoins; int _lastCoin; //마지막 코인 위치를 저장하기 위한 변수 추가
다시 cpp로 돌아와 변수 초기값 및 터치 함수를 수정합니다.
HelloWorldScene.cpp
void HelloWorld::initGameCoin() { int coinX = 0; int coinY = 0; int diffX = _screenSize.width * 0.135f; //코인 x 간격 int diffY = _screenSize.height * 0.097f; //코인 y 간격 int initCoinX = _screenSize.width * 0.095f; //초기 코인 x 위치 int initCoinY = _screenSize.height * 0.613f; //초기 코인 y 위치 GameCoin* gameCoin; _lastCoin = -1; ... }
bool HelloWorld::onTouchBegan(Touch* touch, Event* event) { //getCurrentTarget은 Node를 반환한다. 이것이 터치한 오브젝트가 된다. auto target = event->getCurrentTarget(); Point location = target->convertToNodeSpace(touch->getLocation()); _lastCoin = checkPushGameCoin(location); if(_lastCoin>=0){ addSelectCoins(_lastCoin); } return true; }
2. 터치 상태에서 움직이는 동안 선택된 코인이 1에서 선택된 것과 같은지 확인
HelloWorldScene.h
인접 코인을 검사하기 위해 enum변수 위치 변경 밑 배열 추가 합니다.
private: enum{ BOARD_X = 7, //코인 x 개수 BOARD_Y = 6, //코인 y 개수 TOTAL_COIN_TYPE = 4, //코인 종류의 수 TOTAL_ADJ_COIN = 6, //검사할 최대 주변 코인의 개수 }; Size _screenSize; Action* _enemyMove; Action* _treeMove; Array* _gameCoins; int _lastCoin; //마지막 코인 위치를 저장하기 위한 변수 추가 static int _adjCoin[BOARD_X * BOARD_Y][TOTAL_ADJ_COIN]; public: // there's no 'id' in cpp, so we recommend returning the class instance pointer static cocos2d::Scene* createScene(); ...
HelloWorldScene.cpp
인접 코인을 검사하기 위한 배열을 다음 값으로 초기화 합니다.
숫자에 대해서는 따로 설명하지 않습니다.
#include "HelloWorldScene.h" USING_NS_CC; int HelloWorld::_adjCoin[BOARD_X * BOARD_Y][TOTAL_ADJ_COIN] = { {-1, -1, 1, 7, 6, -1}, // 0, line 1 {-1, -1, 2, 8, 7, 0}, {-1, -1, 3, 9, 8, 1}, {-1, -1, 4, 10, 9, 2}, {-1, -1, 5, 11, 10, 3}, {-1, -1, -1, -1, 11, 4}, {-1, 0, 7, 12, -1, -1}, // 6, line 2 { 0, 1, 8, 13, 12, 6}, { 1, 2, 9, 14, 13, 7}, { 2, 3, 10, 15, 14, 8}, { 3, 4, 11, 16, 15, 9}, { 4, 5, -1, 17, 16, 10}, { 6, 7, 13, 19, 18, -1}, // 12, line 3 { 7, 8, 14, 20, 19, 12}, { 8, 9, 15, 21, 20, 13}, { 9, 10, 16, 22, 21, 14}, {10, 11, 17, 23, 22, 15}, {11, -1, -1, -1, 23, 16}, {-1, 12, 19, 24, -1, -1}, // 18, line 4 {12, 13, 20, 25, 24, 18}, {13, 14, 21, 26, 25, 19}, {14, 15, 22, 27, 26, 20}, {15, 16, 23, 28, 27, 21}, {16, 17, -1, 29, 28, 22}, {18, 19, 25, 31, 30, -1}, // 24, line 5 {19, 20, 26, 32, 31, 24}, {20, 21, 27, 33, 32, 25}, {21, 22, 28, 34, 33, 26}, {22, 23, 29, 35, 34, 27}, {23, -1, -1, -1, 35, 28}, {-1, 24, 31, 36, -1, -1}, // 30, line 6 {24, 25, 32, 37, 36, 30}, {25, 26, 33, 38, 37, 31}, {26, 27, 34, 39, 38, 32}, {27, 28, 35, 40, 39, 33}, {28, 29, -1, 41, 40, 34}, {30, 31, 37, -1, -1, -1}, // 36, line 7 {31, 32, 38, -1, -1, 36}, {32, 33, 39, -1, -1, 37}, {33, 34, 40, -1, -1, 38}, {34, 35, 41, -1, -1, 39}, {35, -1, -1, -1, -1, 40}, };
3. 같은 타입의 코인인 경우 하얗게 이미지를 덮어씌워 표시
이제 코인을 터치하여 쭉 움직였을 때, 같은 타입인 경우엔 코인 위에 하햫고 반투명한 이미지를 덮어씌워 표시하도록 합니다.
HelloWorldScene.h
헤더파일에 아래와 같이 두개의 변수를 추가합니다.
class HelloWorld : public cocos2d::Layer { private: enum{ BOARD_X = 7, //코인 x 개수 BOARD_Y = 6, //코인 y 개수 TOTAL_COIN_TYPE = 4, //코인 종류의 수 TOTAL_ADJ_COIN = 6, //검사할 최대 주변 코인의 개수 }; Size _screenSize; Action* _enemyMove; Action* _treeMove; Array* _gameCoins; Array* _selectMask; Array* _selectCoins; int _lastCoin; //마지막 코인 위치를 저장하기 위한 변수 추가 static int _adjCoin[BOARD_X * BOARD_Y][TOTAL_ADJ_COIN];
HelloWorldScene.cpp
HelloWorld::~HelloWorld() { CC_SAFE_RELEASE(_enemyMove); CC_SAFE_RELEASE(_treeMove); CC_SAFE_RELEASE(_gameCoins); CC_SAFE_RELEASE(_selectCoins); CC_SAFE_RELEASE(_selectMask); }
void HelloWorld::initGameCoin() { .. //배열 설정 _gameCoins = Array::createWithCapacity(BOARD_X * BOARD_Y); _gameCoins->retain(); _selectCoins = Array::createWithCapacity(BOARD_X * BOARD_Y); _selectCoins->retain(); _selectMask = Array::createWithCapacity(BOARD_X * BOARD_Y); _selectMask->retain(); .. //selectMask 설정 Sprite* selectMask; for (int xIndex = 0; xIndex < BOARD_X; xIndex++) { coinX = initCoinX + (xIndex * diffX); coinY = initCoinY; if (xIndex % 2 == 0) { coinY -= diffY / 2; } for (int yIndex = 0; yIndex < BOARD_Y; yIndex++) { selectMask = Sprite::create("selectTile.png"); selectMask->setVisible(false); selectMask->setScale(_screenSize.width * 0.0011f); selectMask->setPosition(coinX, coinY); this->addChild(selectMask); _selectMask->addObject(selectMask); coinY -= diffY; } } }
setScale 부분은 기기 해당도마다 달라질 수 있으니 적절하게 변경해 사용하세요.
제가 사용하는 기기는 1024*600의 태블릿이고, 좀 더 작은 스마트폰에 빌드해보니 크기가 맞지 않더군요.
하지만 일단 이대로 진행하고, 차후에 해상도 관련해서 수정해야겠습니다.
HelloWorldScene.h
다음과 같은 순서대로 동작하기 위해 헤더파일에 함수를 추가합니다.
int checkPushGameCoin(Point &touchPos); bool inLastCoin(Point &touchPos); //1. 이동한 위치의 코인이 처음 코인과 다른 경우 int checkAdjacentCoin(Point &touchPos); //2. 이동한 위치의 코인이 처음 코인의 인접 코인인지 확인 bool compareCoinType(int index1, int index2); //3. 처음 코인과 인접 코인의 종류가 같으면 int addSelectCoins(int index); //4. 선택된 코인, 코인 갯수등의 정보를 업데이트한다. };
HelloWorldScene.cpp
이제 각 함수를 작성합니다.
1) inLastCoin()
bool HelloWorld::inLastCoin(Point &touchPos) { if (_lastCoin == -1) { return false; } GameCoin* lastCoin = (GameCoin*) _gameCoins->objectAtIndex(_lastCoin); if (lastCoin->boundingBox().containsPoint(touchPos)) { return true; } return false; }
2) checkAdjacentCoin()
int HelloWorld::checkAdjacentCoin(Point &touchPos) { int index; GameCoin* tmpCoin; if (_lastCoin < 0) { return -1; } for (index = 0; index < 6; index++) { if (_adjCoin[_lastCoin][index] == -1) { continue; } tmpCoin = (GameCoin*) _gameCoins->objectAtIndex(_adjCoin[_lastCoin][index]); if (tmpCoin->boundingBox().containsPoint(touchPos)) { return _adjCoin[_lastCoin][index]; } } return -1; }
3) compateCoinTyrp()
bool HelloWorld::compareCoinType(int index1, int index2) { if (index1 < 0 || index2 < 0) { return false; } GameCoin* tmpCoin1 = (GameCoin*) _gameCoins->objectAtIndex(index1); GameCoin* tmpCoin2 = (GameCoin*) _gameCoins->objectAtIndex(index2); if (tmpCoin1->getType() == tmpCoin2->getType()) { return true; } return false; }
4) addSelectCoins()
int HelloWorld::addSelectCoins(int index) { if (index < 0) { return -1; } GameCoin* tmpCoin = (GameCoin*) _gameCoins->objectAtIndex(index); Sprite* selectMask = (Sprite*) _selectMask->objectAtIndex(index); if (tmpCoin->getState() != GameCoin::SELECT) { tmpCoin->setState(GameCoin::SELECT); _selectCoins->addObject(tmpCoin); selectMask->setVisible(true); } return 0; }
각 함수의 동작에 대한 설명은 원글을 봐주세요.
이제 실제로 동작할 수 있도록 onTouchMoved에 내용을 추가하도록 합니다.
HelloWorldScene.cpp
void HelloWorld::onTouchMoved(Touch* touch, Event* event) { auto target = event->getCurrentTarget(); Point location = target->convertToNodeSpace(touch->getLocation()); int newCoin = -1; if (!inLastCoin(location)) { newCoin = checkAdjacentCoin(location); if (compareCoinType(_lastCoin, newCoin)) { _lastCoin = newCoin; addSelectCoins(_lastCoin); } } }
그리고 반드시 잊지 말고 리스너에 콜백을 추가해줍니다. 이걸 하지 않으면 터치가 전혀 동작하지 않습니다.
bool HelloWorld::init() { ////////////////////////////// // 1. super init first if (!Layer::init()) { return false; } _screenSize = Director::sharedDirector()->getWinSize(); this->setTouchEnabled(true); //디스패처. 리스너와 오브젝트를 연결해주는 역할 EventDispatcher* dispatcher = Director::getInstance()->getEventDispatcher(); //터치 위치를 알려주는 리스너. 단일 터치. //바로 만들어쓰는 식별자는 auto를 사용한다. auto positionListener = EventListenerTouchOneByOne::create(); //zOrder에 따라 밑에 깔린애도 동작할지 아닐지를 결정한다. positionListener->setSwallowTouches(true); //콜백 함수 대입 positionListener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this); positionListener->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved, this); //디스패처를 이용해 객체와 리스너를 이어준다. 화면 전체를 터치할 수 있게 만들어야 하므로 객체는 this dispatcher->addEventListenerWithSceneGraphPriority(positionListener, this); createGameAction(); createGameScene(); return true; }
여기까지 하고 실행하면 다음과 같이 잘 실행되는 것을 확인할 수 있습니다.
꽤 많은 내용이 한꺼번에 진행되었기 때문에 오타로 인해 문제가 생길 수 있으니 잘 확인해 주시고요
무엇보다 이전버전과는 터치의 동작이 달라졌기 때문에 헷갈리실 수 있어요.
캡쳐를 보면 이미지 크기가 살짝 맞지 않는 부분이 있지만 일단은 넘어가도록 하겠습니다.