这一篇就说说导弹发射与碰撞部分的代码,总体来说就和一个微型的飞行射击游戏差不多,但是在这部分内容里,游戏运用了一些碰撞后的特效代码如:背景闪烁、地面出现弹坑……不过貌似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); }
源代码:http://download.csdn.net/detail/cyistudio/5553235