cocos2dx碰撞检测
在Cocos2d-x开发过程中,碰撞检测是一个常见的需求。那么,Cocos2d-x的碰撞检测是如何实现的呢?下面我们将详细介绍具体的实现过程。
场景假设
假设场景中有多个敌人和子弹,子弹可以击中敌人,并且在碰撞发生后,子弹和敌人都会消失。
实现步骤
1. 追踪敌人和子弹
为了区分敌人和子弹,我们可以为这两种物体添加标签(tag)。由于CCSprite继承自CCNode,已经具备setTag和getTag方法,因此我们可以通过这种方式来区分不同的精灵。给敌人添加tag = 1,给子弹添加tag = 2。
在HelloWorldScene.h中添加两个成员对象,分别用于存储敌人和子弹:
protected:
cocos2d::CCArray *_targets;
cocos2d::CCArray *_projectiles;
在Cocos2d-x中,需要在构造函数中初始化这两个变量,在init()函数中进行内存分配,在析构函数中释放内存:
// 构造函数
HelloWorld::HelloWorld()
:_targets(NULL)
,_projectiles(NULL)
{
}
// 初始化
void HelloWorld::init() {
_targets = new CCArray;
_projectiles = new CCArray;
}
// 析构函数
HelloWorld::~HelloWorld()
{
if (_targets)
{
_targets->release();
_targets = NULL;
}
if (_projectiles)
{
_projectiles->release();
_projectiles = NULL;
}
// cpp不需要调用super释放,虚析构函数将会做这些事
}
2. 添加敌人和子弹到数组
添加敌人
修改addTarget()函数,将新的敌人添加到targets数组中,并将其标签设置为1:
// 添加到数组中
target->setTag(1);
_targets->addObject(target);
添加子弹
修改ccTouchesEnded()函数,将新的子弹添加到_projectiles数组中,并将其标签设置为2:
// 添加到_projectiles数组中
projectile->setTag(2);
_projectiles->addObject(projectile);
3. 移除精灵
修改spriteMoveFinished()函数,从相应的数组中移除一些精灵:
void HelloWorld::spriteMoveFinished(CCNode* sender)
{
CCSprite *sprite = (CCSprite *)sender;
this->removeChild(sprite, true);
if (sprite->getTag() == 1) // target
{
_targets->removeObject(sprite);
}
else if (sprite->getTag() == 2) // projectile
{
_projectiles->removeObject(sprite);
}
}
4. 碰撞检测
update()函数会每帧执行一次,通常用于检测碰撞并移除发生碰撞的子弹和敌人。在HelloWorldScene.h中声明该函数,在HelloWorldScene.cpp中定义:
void HelloWorld::update(float dt)
{
CCArray *projectilesToDelete = new CCArray;
CCArray* targetsToDelete = new CCArray;
CCObject* it = NULL;
CCObject* jt = NULL;
CCARRAY_FOREACH(_projectiles, it)
{
CCSprite *projectile = dynamic_cast<CCSprite*>(it);
CCRect projectileRect = CCRectMake(
projectile->getPosition().x - (projectile->getContentSize().width/2),
projectile->getPosition().y - (projectile->getContentSize().height/2),
projectile->getContentSize().width,
projectile->getContentSize().height);
CCARRAY_FOREACH(_targets, jt)
{
CCSprite *target = dynamic_cast<CCSprite*>(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 (projectileRect.intersectsRect(targetRect))
{
targetsToDelete->addObject(target);
projectilesToDelete->addObject(projectile);
}
}
}
CCARRAY_FOREACH(targetsToDelete, jt)
{
CCSprite *target = dynamic_cast<CCSprite*>(jt);
_targets->removeObject(target);
this->removeChild(target, true);
}
CCARRAY_FOREACH(projectilesToDelete, it)
{
CCSprite* projectile = dynamic_cast<CCSprite*>(it);
_projectiles->removeObject(projectile);
this->removeChild(projectile, true);
}
projectilesToDelete->release();
targetsToDelete->release();
}
5. 调用update()函数
最后,将update()函数添加到计时器中,使其每帧被调用一次,通常在onEnter()函数中添加以下代码:
void HelloWorld::onEnter()
{
CCLayer::onEnter();
this->schedule( schedule_selector(HelloWorld::update) );
}
通过以上步骤,一个简单的碰撞检测模型就建立好了。