cocos2d-x 菜鸟学习笔记十六(简单的碰撞检测 实践篇 中)

原创 2013年06月09日 15:38:27

这一篇就说说导弹发射与碰撞部分的代码,总体来说就和一个微型的飞行射击游戏差不多,但是在这部分内容里,游戏运用了一些碰撞后的特效代码如:背景闪烁、地面出现弹坑……不过貌似cocos2d-x没有完全封装chipmunk,所以示例中有些功能实现起来过于麻烦,如地面的碰撞线段按边缘附加本来可用的marchAllWithBorder,这样的方法就得替换成手动设置……因此像弹坑这样的效果就不打算再做了,反正弹坑效果也就是混合效果,主要是混合图形后产生新的碰撞线段没什么办法,可以在日后学习box2d后再自己玩下……这里只演示导弹撞击墙体后的爆炸效果:

1.首先需要定义一个炮弹的物理精灵,在原例中,通过自定义导弹对象的实现代码赋予了导出isTracking追踪重力点的属性,使得导弹可以在触摸移动时跟着移动的坐标飞行。这部分代码的实现方式我改用了前面用过的代码,把刚体、形状、精灵关联等代码全部封装进了自定义的导弹精灵类里:

//自定义一个物理精灵类,用于定制导弹的自有属性
class Missile :
	public cocos2d::extension::CCPhysicsSprite
{
public:
	Missile(void);
	~Missile(void);
	//自定义创建导弹精灵,这里自己加了三个参数,分别是乌云精灵的位置,触屏坐标对应的矢量点和加入到的物理空间
	static Missile* create(const char *pszFileName,cocos2d::CCPoint pos,cpVect target,cpSpace* m_pSpace);

	//导弹加速度的回调函数
	static void
	MissileVelocityUpdate(cpBody *cBody, cpVect gravity, cpFloat damping, cpFloat dt);

	//是否追踪触屏移动点
	bool isTracking;
	//触屏点对应的重力矢量点,用于决定导弹发射运行的角度
	cpVect target;

};

导弹需要在物理世界里,所以上面继承的是物理精灵类,下面是实现文件:

#include "Missile.h"
#define MISSILE_SIZE 32.0
#define MISSILE_SPEED 200.0
#define MISSILE_TURN_SPEED 6.0

Missile::Missile(void)
{
	isTracking=false;
}


Missile::~Missile(void)
{

}
//计算带追踪属性的导弹发射角度对应的矢量点
static cpVect
Turn(cpVect v1, cpVect v2, cpFloat limit)
{
	// Angle between the two vectors
	cpFloat angle = cpfacos(cpfclamp01(cpvdot(cpvnormalize(v1), cpvnormalize(v2))));
	if(angle){
		// Performs an nlerp() between two direction vectors.
		cpVect direction = cpvnormalize(cpvlerp(v1, v2, cpfmin(limit, angle)/angle));
		return cpvmult(direction, MISSILE_SPEED);
	} else {
		return v1;
	}
}

//导弹加速度的回调函数
void Missile::MissileVelocityUpdate(cpBody *cBody, cpVect gravity, cpFloat damping, cpFloat dt)
{
	cpBody *abody = cBody;
	Missile *missile = (Missile*)abody->data;
	//根据导弹是否处于追踪状态决定是否刷新矢量点
	if(missile->isTracking){
		cpVect targetVelocity = cpvmult(cpvnormalize(cpvsub(missile->target, abody->p)), MISSILE_SPEED);
		abody->v = Turn(abody->v, targetVelocity, MISSILE_TURN_SPEED*dt);
	}
	//根据当前矢量点计算出导弹运行的角度
	abody->a = cpvtoangle(abody->v) - M_PI/2.0;
}

//重写创建的过程,这里用到的是前两节使用的代码
Missile* Missile::create(const char *pszFileName,cocos2d::CCPoint pos,cpVect target,cpSpace* m_pSpace)
{
    Missile* pRet = new Missile();
    if (pRet && pRet->initWithFile(pszFileName))
    {
#if CC_ENABLE_CHIPMUNK_INTEGRATION    
    int posx, posy;
	pRet->target=target;
	//随机取重心位置坐标
    posx = CCRANDOM_0_1() * 200.0f;
    posy = CCRANDOM_0_1() * 200.0f;

    posx = (posx % 4) * 85;
    posy = (posy % 3) * 121;

	//设定刚体形状,这里定义了四个矢量点,所以形状为矩形
    int num = 4;
    cpVect verts[] = {
        cpv(-10,-30),
        cpv(-10, 20),
        cpv( 10, 20),
        cpv( 10,-30),
    };
	//创建刚体
    cpBody *body = cpBodyNew(1.0f, cpMomentForPoly(1.0f, num, verts, cpvzero));
	body->data=pRet;
	//设定刚体的重心点,然后将刚体加入到物理空间
    body->p = cpv(pos.x, pos.y);
	body->v = cpvmult(cpvnormalize(cpvsub(pRet->target, body->p)), MISSILE_SPEED);
	body->velocity_func = MissileVelocityUpdate;
    cpSpaceAddBody(m_pSpace, body);
	//为刚体定义一个物理形状,cpPolyShapeNew用于建立多边形,其它定义请看源码
    cpShape* shape = cpPolyShapeNew(body, num, verts, cpvzero);
	//定义弹性及摩擦系数,然后加入到物理空间
    shape->e = 0.5f; shape->u = 0.5f;
	//设定碰撞类型标识
	shape->collision_type=5;

    cpSpaceAddShape(m_pSpace, shape);
	//cpSpaceAddCollisionHandler(m_pSpace,2,1,beginCollision,NULL,NULL,NULL,NULL);

	shape->data = pRet;

	//将精灵关联到刚体并为其设定位置坐标
    pRet->setCPBody(body);
    pRet->setPosition(pos);
#endif

        pRet->autorelease();
    }
    else
    {
        CC_SAFE_DELETE(pRet);
    }

    return pRet;
}

一些计算、换算方法就自己琢磨下好了……下面是主层的类:

class HelloWorld : public cocos2d::CCLayer
{
public:
	HelloWorld();
    ~HelloWorld();

    // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
    virtual bool init();  

    // there's no 'id' in cpp, so we recommand to return the exactly class pointer
    static cocos2d::CCScene* scene();
	//触摸时的回调函数
	virtual void ccTouchesBegan(cocos2d::CCSet* touches, cocos2d::CCEvent* event);
	//触摸移动时的回调函数,用于引导导弹
	virtual void ccTouchesMoved(cocos2d::CCSet* touches, cocos2d::CCEvent* event);
	//触屏结束时的回调函数,用于清除导弹的跟踪属性
	virtual void ccTouchesEnded(cocos2d::CCSet* touches, cocos2d::CCEvent* event);
		//初始化物理系统
    void initPhysics();
	//更新地面的碰撞影响
	void updateTerrain();

		//物理系统运行模式的定义菜单
    void toggleDebugCallback(CCObject* pSender);

	#if CC_ENABLE_CHIPMUNK_INTEGRATION    
	//调试节点,可用来查看定义的形状
    CCPhysicsDebugNode* m_pDebugLayer; // weak ref
	#endif
    
    // a selector callback
    void menuCloseCallback(CCObject* pSender);

		//精灵与刚体的更新事件
    void update(float dt);
	//乌云精灵
	cloud* cloudSprite;

		//物理世界(空间)
    cpSpace* m_pSpace;

		//刚体形状定义
    cpShape* m_pWalls[4];

		//碰撞开始时的回调
	static int beginCollision(cpArbiter *arb, cpSpace *space, void *data);

		//碰撞结束后的对象销毁回调函数
	static void postStepRemove(cpSpace *space, cpShape *shape, void *unused);

	//加入爆炸效果
	static void addExplotion(cocos2d::CCPoint pos);

    // implement the "static node()" method manually
    CREATE_FUNC(HelloWorld);

};


然后是创建四面墙体,代码稍微改了下:

//物理系统初始化
void HelloWorld::initPhysics()
{
#if CC_ENABLE_CHIPMUNK_INTEGRATION    
    // init chipmunk
    //cpInitChipmunk();
	//创建一个物理空间
    m_pSpace = cpSpaceNew();
	//设定空间的重力点
    m_pSpace->gravity = cpv(0, -100);
	
    //
    // rogue shapes
    // We have to free them manually
    //这里创建了四个形状用于创建一个矩形的墙体,其对应的刚体是静态刚体,也就是保持静止不动的刚体
	//cpSegmentShapeNew用来建立一个段状形状,其它定义请看源码
    // bottom
    m_pWalls[0] = cpSegmentShapeNew( m_pSpace->staticBody,
        cpv(VisibleRect::leftBottom().x,VisibleRect::leftBottom().y),
        cpv(VisibleRect::rightBottom().x, VisibleRect::rightBottom().y), 9.0f);

    // top
    m_pWalls[1] = cpSegmentShapeNew( m_pSpace->staticBody, 
        cpv(VisibleRect::leftTop().x, VisibleRect::leftTop().y),
        cpv(VisibleRect::rightTop().x, VisibleRect::rightTop().y), 0.0f);

    // left
    m_pWalls[2] = cpSegmentShapeNew( m_pSpace->staticBody,
        cpv(VisibleRect::leftBottom().x,VisibleRect::leftBottom().y),
        cpv(VisibleRect::leftTop().x,VisibleRect::leftTop().y), 0.0f);

    // right
    m_pWalls[3] = cpSegmentShapeNew( m_pSpace->staticBody, 
        cpv(VisibleRect::rightBottom().x, VisibleRect::rightBottom().y),
        cpv(VisibleRect::rightTop().x, VisibleRect::rightTop().y), 0.0f);

	//为形状设定弹性及摩擦系数并加入到物理空间
    for( int i=0;i<4;i++) {
        m_pWalls[i]->e = 1.0f;
        m_pWalls[i]->u = 1.0f;
		//设定碰撞类型标识
		m_pWalls[i]->collision_type=i;
		//添加墙体到物理空间
        cpSpaceAddStaticShape(m_pSpace, m_pWalls[i] );
		//设定碰撞的回调函数,5是导弹的碰撞类型标识
		cpSpaceAddCollisionHandler(m_pSpace,5,i,beginCollision,NULL,NULL,NULL,NULL);
    }

    // Physics debug layer
    m_pDebugLayer = CCPhysicsDebugNode::create(m_pSpace);
    this->addChild(m_pDebugLayer, Z_PHYSICS_DEBUG);
#endif
}

然后是触屏部分的回调代码:

void HelloWorld::ccTouchesBegan(CCSet* touches, CCEvent* event)
{
    //Add a new body/atlas sprite at the touched location
	//根据触屏位置添加刚体精灵
    CCSetIterator it;
    CCTouch* touch;
    for( it = touches->begin(); it != touches->end(); it++) 
    {
        touch = (CCTouch*)(*it);

        if(!touch)
            break;
		
		//根据触屏点创建一个导弹
		MissileSprite=Missile::create("Missile.png",cloudSprite->getPosition(),cpv(this->getParent()->convertTouchToNodeSpace(touch).x,this->convertTouchToNodeSpace(touch).y),m_pSpace);
		MissileSprite->setScale(0.5f);
		addChild(MissileSprite);
		
		//更新乌云眼神的角度
		cloudSprite->lookAt=touch->getLocation();
	}
}

	//触摸移动时的回调函数,用于引导导弹
void HelloWorld::ccTouchesMoved(CCSet* touches, CCEvent* event){
    CCSetIterator it;
    CCTouch* touch;
	    for( it = touches->begin(); it != touches->end(); it++) 
    {

	touch = (CCTouch*)(*it);
	        if(!touch)
            break;

		}

		if(MissileSprite){
		//开启导弹追踪属性并设置重力矢量点到当前触屏移动到的位置
			MissileSprite->isTracking=true;
			MissileSprite->target=cpv(this->getParent()->convertTouchToNodeSpace(touch).x,this->convertTouchToNodeSpace(touch).y);
		}
		//更新乌云眼神的角度
		cloudSprite->lookAt=touch->getLocation();
}

//触摸结束时的回调
void HelloWorld::ccTouchesEnded(CCSet* touches, CCEvent* event){
	//去除导弹的跟踪属性
	if(MissileSprite){
	MissileSprite->isTracking=false;
	}
}

最后是碰撞回调的代码:

int HelloWorld::beginCollision(cpArbiter *arb, cpSpace *space, void *data){
	//获取碰撞的shape,a代表用cpSpaceAddCollisionHandler添加碰撞回调时的碰撞对象类型a,b亦然
	CP_ARBITER_GET_SHAPES(arb, a, b);
	CCLog("begin collision p_x:%1.1f p_y:%1.1f",a->body->p.x,a->body->p.y);
	//设置物理空间在结束当前动作时的回调函数,用于安全释放对象,此调用只会在完成当前动作后调用一次
	cpSpaceAddPostStepCallback(space,(cpPostStepFunc)postStepRemove,a,NULL);
	//addExplotion(a->body->v);
	//返回值为false时,碰撞对象会只在初次碰撞时执行回调函数,两个碰撞对象会重叠在一起
	return true;
}

//移除导弹精灵
void HelloWorld::postStepRemove(cpSpace *space, cpShape *shape, void *unused){
//CCLog("postStep p_x:%1.f p_y:%1.f",shape->body->p.x,shape->body->p.y);
	//取出data中的对应数据
	CCPhysicsSprite *sprite = (CCPhysicsSprite*)shape->data;
		//CCLog("%1d",sprite->getTag());

	//触发爆炸
	addExplotion(ccp(shape->body->p.x,shape->body->p.y));

//释放碰撞的对象,这里释放时要注意顺序
//先释放形状
	cpSpaceRemoveShape(space, shape);
	cpShapeFree(shape);
//释放刚体
	cpSpaceRemoveBody(space, sprite->getCPBody());
	cpBodyFree(sprite->getCPBody());
//移除精灵
	sprite->removeFromParentAndCleanup(true);
//释放消失的导弹指针
	MissileSprite=NULL;
	
}

	//加入爆炸效果
void HelloWorld::addExplotion(CCPoint pos){
	
	//在撞击点加入爆炸效果
    CCParticleSystemQuad *sparkFlare = CCParticleSystemQuad::create("kaboom.plist");
    sparkFlare->retain();
	sparkFlare->setAutoRemoveOnFinish(true);
	//将爆炸位置设置到导弹所在的坐标
	sparkFlare->setPosition(pos);
	layer->addChild(sparkFlare,100);

}

通过上面的代码就可以实现乌云按照触屏点发射导弹,并在与墙体碰撞时产生爆炸……因为有点懒,所以物理空间和形状的一些属性还是按照以前的代码设定,天空的闪烁动画也没做……这个自己有兴趣的话自己修改着玩就行了。有空再看看最后一节看能不能实现碰撞的撞击力,即可以在爆炸后把周围的箱子炸飞,目测是有cpBodyApplyImpulse这个函数……

源代码:http://download.csdn.net/detail/cyistudio/5553235

cocos2d-x 菜鸟学习笔记十七(简单的碰撞检测 实践篇 下)

最后这一节记录一下cloudBomb中冲击力影响的实现和碰撞过滤。利用chipmunk中的冲击力函数可以在导弹爆炸时弹飞附近的刚体,达到最真实的爆炸所产生的物理影响,其实在游戏中也可以用来做释放技能之...
  • cyistudio
  • cyistudio
  • 2013年06月10日 14:25
  • 1880

cocos2dx 精灵的碰撞检测和消灭(3)

在上一篇的基础上增加了一点内容,必要的注释都写在代码里了,,就直接贴代码吧,我也懒得写详细的过程。 HelloWorldSence.h #ifndef __HELLOWORLD_SCENE_H__ #...
  • chaoyuan899
  • chaoyuan899
  • 2013年12月02日 15:45
  • 18358

实例介绍Cocos2d-x物理引擎:碰撞检测

碰撞检测是使用物理引擎的一个重要目的,使用物理引擎可以进行精确的碰撞检测,而且执行的效率也很高。在Cocos2d-x 3.x中使用事件派发机制管理碰撞事件,EventListenerPhysicsCo...
  • tonny_guan
  • tonny_guan
  • 2014年09月26日 18:32
  • 17564

Cocos2d-x3.2总结(四)使用物理引擎进行碰撞检测

如果你在游戏中
  • cbbbc
  • cbbbc
  • 2014年08月13日 19:10
  • 5373

Cocos2d-x教程(33)-三维物体AABB碰撞检测算法

Cocos2d-x 3.x版本 3D物体的碰撞检测,AABB包围盒。
  • u012945598
  • u012945598
  • 2014年09月25日 18:44
  • 5338

CocosCreator学习6:碰撞系统

今天来通过一个简单的例子学习碰撞。碰撞组件首先来看碰撞组件。CocosCreator提供了三种碰撞组件:Box Collider,Circle Collider,Polygon Collider。增添...
  • lin453701006
  • lin453701006
  • 2017年05月03日 22:22
  • 2524

cocos2d-x 3.2 物理碰撞机制

cocos2d-x 3.0+ 中全新的封装的物理引擎给了开发者最大的便捷,你不用再繁琐与各种物理引擎的细节,完全的封装让开发者可以更快更好的将物理引擎的机制添加到自己的游戏中,简化的设计是从2.0到3...
  • u013599970
  • u013599970
  • 2014年08月03日 11:59
  • 4711

【cocos2d-x游戏开发】物体的碰撞检测

碰撞检测是物理世界中很重要的一部分,
  • u013517637
  • u013517637
  • 2014年11月01日 09:55
  • 750

cocos2d-x中的碰撞检测

今天在csdn看到一篇文章,写矩形与圆形的碰撞检测,借鉴了作者的检测思路,我把它用到了cocos2d-x中。 原文章的地址:http://blog.csdn.net/yorhomwang/articl...
  • guochuanqi
  • guochuanqi
  • 2014年01月16日 11:46
  • 2573

Cocos2d-x碰撞检测原理与英雄要打死怪物--之游戏开发《赵云要格斗》(7)

本文将详细讲述cocos2dx中英雄与怪物的碰撞检测原理,其实就是精灵和精灵碰撞检测哈。本文主要从矩形碰撞入手,自己编写了一个矩形碰撞检测的函数,并且在游戏中来进行应用。另一方面,当英雄出动攻击后,如...
  • Evankaka
  • Evankaka
  • 2015年01月14日 11:17
  • 11696
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:cocos2d-x 菜鸟学习笔记十六(简单的碰撞检测 实践篇 中)
举报原因:
原因补充:

(最多只允许输入30个字)