cocos2d-x 공부용2014. 9. 12. 14:55

원글 주소 : 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;
}

여기까지 하고 실행하면 다음과 같이 잘 실행되는 것을 확인할 수 있습니다.



꽤 많은 내용이 한꺼번에 진행되었기 때문에 오타로 인해 문제가 생길 수 있으니 잘 확인해 주시고요

무엇보다 이전버전과는 터치의 동작이 달라졌기 때문에 헷갈리실 수 있어요.

캡쳐를 보면 이미지 크기가 살짝 맞지 않는 부분이 있지만 일단은 넘어가도록 하겠습니다.


Posted by 아이시네프