cocos2d-xで四角形と円の衝突判定

Pocket

game_scsho

作り途中のアプリのスクショです。まだ全然ちゃっちいです(ちゃっちさがちっとも緩和されない…)。

今デバッグ用に円を表示させてるんですが、要はこの円と中にスプライトが入ってるかを判定したくなって、色々調べました。

参考:XNA メモ – 円と長方形の当たり判定

何か他のもっと簡単なやり方はありそうな気がするし、というより実装されてそうな気もするんだけど(見つけられなかった)、ただ結構簡単に自作できたんで、忘れないうちにまとめておきます(需要はないような気もするけど…)。

やり方

cocos2d-xにはポイント同士の距離を求めるccpDistance()と2つの点から一番近い位置を求めるclampfとかccpClampといった便利な関数があるんで、それを使います。
参考にしたページのとやり方はまんま同じです。

円のクラスを作成する(CCCircle)

cocos2d-xには円を表示するようなのがなかったから、それだけ自作します。
どうせならaddChildできた方がなにかと便利だから、CCNodeを実装させます。

//CCCircle.h
class CCCircle : public CCNode{
	CC_SYNTHESIZE(float , radius , Radius);
public:
	CCCircle(const CCPoint $point , float $radius):radius($radius){
		this->setPosition($point);
	};
	static CCCircle* create(CCPoint $point , float $radius);
	virtual bool init();
	//円と四角形の衝突判定
	bool intersectRect(const CCRect &$rect);
	//円同士の衝突判定
	bool intersectCircle(CCCircle* $circle);
	//ついで
	CCDrawNode* getDrawNode(ccColor4F $f);
};

//CCCircle.cpp
#include "CCCircle.h"
using namespace cocos2d;

CCCircle* CCCircle::create(CCPoint $point , float $radius){
	CCCircle* pret = new CCCircle($point , $radius);
	if(pret && pret->init()){
		pret->autorelease();
		return pret;
	}
	CC_SAFE_DELETE(pret);
	pret = NULL;
	return NULL;
}

bool CCCircle::init(){
	if(!CCNode::init()) return false;
	return true;
}

bool CCCircle::intersectRect(const CCRect &$rect){
	CCPoint pos = ccp(clampf(m_obPosition.x , $rect.origin.x , $rect.origin.x + $rect.size.width),
			  clampf(m_obPosition.y , $rect.origin.y , $rect.origin.y + $rect.size.height));
	return ccpDistance(m_obPosition , pos) < radius;
}

bool CCCircle::intersectCircle(CCCircle* $circle){
	return ccpDistance(m_obPosition , $circle->getPosition()) < (radius + $circle->getRadius());
}

CCDrawNode* CCCircle::getDrawNode(ccColor4F $f){
	CCDrawNode* draw = CCDrawNode::create();
	draw->drawDot(ccp(0,0) , radius , $f);
	return draw;
}

m_obPositionはCCNodeのメンバ変数です。setPositionのですね。

intersectRectで当たり判定をしてます。clampfで最短距離の位置を求め、円の中心からの距離を求めて、それが円の半径より短ければ衝突してるという感じです。

使ってみる

実際に使ってみます。

CCSize winSize = CCDirector::sharedDirector()->getVisibleSize();
CCPoint winPoint = CCDirector::sharedDirector()->getVisibleOrigin();
CCPoint pos = ccp(winPoint.x + winSize.width / 2 , winPoint.y + winSize.height / 2);
//画面の中心に作成する
CCCircle* circle = CCCircle::create(pos , 300.0f);
circle->setTag(1);
this->addChild(circle);

//別のターゲット
CCSprite* pSprite = CCSprite::create("coin.png");
pSprite->setPosition(ccp(winPoint.x + winSize.width / 2 + 100 , winPoint.y + winSize.height / 2 + 100));
pSprite->setTag(2);
this->addChild(pSprite);

//デバッグ用で
CCDrawNode* nodes = circle->getDrawNode(ccc4f(1.0f,1.0f,1.0f,0.8f));
nodes->setPosition(pos);
this->addChild(nodes , 100);

...

//別の場面で取り出して当たり判定してみる
CCCircle* circle = (CCCircle*)this->getChildByTag(1);
CCSprite* sprite = (CCSprite*)this->getChildByTag(2);
if(circle->intersectRect(sprite->boundingBox())){
   CCLOG("あたったよ。よかったね。");
}

実行すると、無事「あたったよ。よかったね。」と言ってもらえた\(^o^)/

まとめ

なんというか、ブログを書くたびに色々な意味で恥ずかしくなっちゃうんだけど(自分の初心者っぷりとか…)、とりあえずわざわざクラスなんて作らなくても

CCPoint circlePos = ccp(0,0);
CCPoint pos = ccp(clampf(circlePos.x , $rect.origin.x , $rect.origin.x + $rect.size.width),
		  clampf(circlePos.y , $rect.origin.y , $rect.origin.y + $rect.size.height));
float radius = 100.0f;
bool hantei = ccpDistance(circlePos , pos) < radius;

だけで判定できますね(´・ω・`)

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です