cocos2d-x 게임 만들기 4편에 이어서 이번 편에서는 표창과 적간에 충돌처리를 구현해 보겠습니다. 간단한 게임이므로 복잡한 충돌처리 로직을 사용하는 것보다는 단순히 사각형 영역을 비교해서 충돌처리를 구현하려고 합니다.
영역비교를 하기 위해서는 표창과 적을 일일이 비교 해야 합니다. 그러기 위해서는 화면에 그려지고 있는 표창과 적의 인스턴스 주소를 가지고 있어야 합니다. 표창과 적의 개수는 제한 된 것이 아니므로 가변 배열 같은 것이 있으면 편리할 것 같습니다.
ObjC 언어는 가변 배열 용도로 NSMutableArrray 클래스를 제공하고 C++에서는 STL의 vector 템플릿 클래스를 제공합니다. cocos2d-x 라이브러리는 이 vector를 이용하여 NSMutableArray 클론 클래스를 제공합니다. STL에서 컬렉션의 이터레이터 개념을 알면 아주 쉽게 사용할 수 있습니다.
그럼 구현을 시작해 볼까요?
우선 GameScene.h 에 충돌처리를 위한 가변배열을 선언하겠습니다. 그리고 이 가변 배열인자들을 초기화 해줄 생성자와 배열들의 메모리를 해제할 소멸자 그리고 충돌처리를 구현할 checkCollision() 스케줄링 메서드도 선언해 주었습니다.
// GameScene.h class GameScene : public cocos2d::CCLayerColor { ... private: //: 외부에서 호출할 일이 없으므로 private으로 만든다 GameScene(); ~GameScene(); void checkCollision(cocos2d::ccTime dt); private: //: 충돌처리를 위한 가변 리스트 //: 적 스프라이트 목록을 관리합니다 cocos2d::CCMutableArray< cocos2d::CCSprite* > *_targets; //: 총알 스프라이트 목록을 관리합니다 cocos2d::CCMutableArray< cocos2d::CCSprite* > *_projectiles; };
이제 생성자와 소멸자를 구현해 충돌처리의 대상인 표창과 적을 담을 가변 배열을 초기화(메모리를 여기서 할당하지는 않습니다)하고 소멸자에서 메모리를 해제하는 코드를 작성하겠습니다.
// GameScene.cpp // 생성자 GameScene::GameScene() : _targets(NULL), _projectiles(NULL) {} // 소멸자 GameScene::~GameScene() { if(_targets) { _targets->release(); _targets = NULL; } if(_projectiles) { _projectiles->release(); _projectiles=NULL; } }
위의 코드에서 바로 delete 문을 사용하지 않고 release()를 호출한 것은 cocos2d-x 가 객체들을 레퍼런스 카운트를 사용하여 관리하기 때문입니다. 아이폰 개발자들에게 익숙할 retain, release, autorelease 등이 모두 구현 되어 있습니다. 따라서 cocos2d-x 클래스의 인스턴스들은 레퍼런스 카운트를 통해 관리되도록 직접적인 delete문은 사용하지 않고 위의 메모리 관리 메서드들을 사용하는 것이 좋습니다.
아래는 CCObject 에 구현되어 있는 메모리 관리 메서드의 구현 내용입니다. 아래의 메서드 외에도 살펴 보아야 할 메서들이 있으므로 꼭 코드를 까 보시길 바랍니다 :)
// CCObject.cpp void CCObject::release(void) { assert(m_uReference > 0); --m_uReference; if (m_uReference == 0) { delete this; } } void CCObject::retain(void) { assert(m_uReference > 0); ++m_uReference; } CCObject* CCObject::autorelease(void) { CCPoolManager::getInstance()->addObject(this); m_bManaged = true; return this; }
위의 GameScene 생성자에서는 가변 배열을 가리킬 포인터 변수들을 NULL로 초기화 했고 실제 인스턴스 할당은 하지 않았습니다. 실제 인스턴스 할당은 init 메서드에서 하겠습니다. 그리고 충돌처리를 위한 스케줄러 메서드를 스케줄링 하겠습니다.
// GameScene.cpp // 레이어를 초기화합니다. bool GameScene::init() { ... //: 가변 리스트 인스턴스를 만듭니다 _targets = new CCMutableArray< CCSprite * >(); _projectiles = new CCMutableArray< CCSprite * >(); ... //: 충돌 체크를 한다 this->schedule(schedule_selector(GameScene::checkCollision)); return true; }
이제 적이 화면에 추가될 때와 표창이 화면에 추가될 때, 가변 인자 배열에 각각의 인스턴스 포인터를 넣어 주어야 합니다. 그래야 충돌처리 검사 스케줄러에서 각 인스턴스들의 충돌 영역을 비교 할 수 있습니다.
addTargets 와 spriteMoveFinished 메서드에 적 인스턴스의 포인터를 추가하고 삭제하는 코드를 작성합니다.
// GameScene.cpp void GameScene::addTarget() { ... //: this->addChild(target); 메서드 위에 아래 코드를 작성합니다. _targets->addObject(target); this->addChild(target); ... } void GameScene::spriteMoveFinished(CCNode *node) { _targets->removeObject((CCSprite *)node); this->removeChild(node, true); }
addProjectileWithTouchLocation 과 projectileMovedFinished 에도 표창 인스턴스의 포인터를 넣고 빼는 코드를 추가합니다.
void GameScene::addProjectileWithTouchLocation(cocos2d::CCPoint location) { ... // this->addChild(projectile) 코드 위에 적어줍니다. _projectiles->addObject(projectile); ... } void GameScene::projectileMovedFinished(cocos2d::CCNode *node) { //: 화면을 벗어난 표창을 제거한다 _projectiles->removeObject((CCSprite *)node); node->removeFromParentAndCleanup(true); }
이제 충돌체크를 하는 메서드만 구현하면 됩니다. 이 메서드는 표창과 적을 일일이 사각형 영역 비교하여 서로 영역이 겹치면 충돌처리로 판단하고 해당 적과 표창을 화면에서 지웁니다. 자세한 구현 내용은 주석을 참고하세요.
// GameScene.cpp void GameScene::checkCollision(ccTime dt) { //: 지울 표창을 담을 가변 배열 CCMutableArray< CCSprite* > *projectileToDelete = new CCMutableArray< CCSprite * >(); //: 가변 배열 이터레이터 CCMutableArray< CCSprite* >::CCMutableArrayIterator it, jt; //: 표창 하나당 모든 적과의 영역 비교를 하여 충돌처리를 한다 for(it=_projectiles->begin(); it!=_projectiles->end(); it++) { CCSprite *projectile = *it; CCRect projectileRect = CCRectMake( projectile->getPosition().x - (projectile->getContentSize().width/2), projectile->getPosition().y - (projectile->getContentSize().height/2), projectile->getContentSize().width, projectile->getContentSize().height); //: 지울 적을 담을 가변 배열 CCMutableArray< CCSprite* > *targetsToDelete = new CCMutableArray< CCSprite* >(); for(jt=_targets->begin(); jt!=_targets->end(); jt++) { CCSprite *target = *jt; CCRect targetRect = CCRectMake( target->getPosition().x - (target->getContentSize().width/2), target->getPosition().y - (target->getContentSize().height/2), target->getContentSize().width, target->getContentSize().height); //: 표창과 적과의 충돌체크 if(CCRect::CCRectIntersectsRect(projectileRect, targetRect)) { targetsToDelete->addObject(target); } } //: 표창에 맞은 적을 지운다 for(jt=targetsToDelete->begin(); jt!=targetsToDelete->end(); jt++) { CCSprite *target = *jt; _targets->removeObject(target); this->removeChild(target, true); } //: 지울 적의 개수가 1 이상이면 표창과 충돌한 것이므로 //: 지울 표창 가변 배열에 현재 표창을 추가한다 if(targetsToDelete->count() > 0) { projectileToDelete->addObject(projectile); } targetsToDelete->release(); } //: 지울 표창을 지운다 for(it=projectileToDelete->begin(); it!=projectileToDelete->end(); it++) { CCSprite *projectile = *it; _projectiles->removeObject(projectile); this->removeChild(projectile, true); } projectileToDelete->release(); }
아래는 실행화면입니다. 동영상으로 만들어 보았습니다 :)
5편을 여기서 마치고 다음 6편에서는 사운드 플레이를 구현해 보겠습니다.
항상 행복하세요!
박재익님(http://blog.naver.com/
droverlord )께서 cocos2d-x 최신 버전(2.x.x)으로 작성한 예제 코드를 보내 주셨습니다.
정말 감사드립니다!
'게임개발팁' 카테고리의 다른 글
cocos2d-X 게임 만들기 6편 (3) | 2011.12.06 |
---|---|
cocos2d-x Box2D 프로젝트 만들기 (10) | 2011.11.21 |
cocos2d-X 게임 만들기 4편 (4) | 2011.11.09 |
cocos2d-X 게임 만들기 3편 (11) | 2011.10.13 |
cocos2d-X 게임 만들기 2편 (8) | 2011.10.13 |