cocos2d中使用chipmunk做碰撞检测
发布时间:2020-12-14 20:26:51 所属栏目:百科 来源:网络整理
导读:我们知道cocos2d本身不包含物理引擎,但是它集成了2个开源物理引擎可供我们选择:box2d和chipmunk。对于一般的正规矩形精灵的碰撞检测,可以简单的使用Core Graphics提供的CGRectIntersectsRect接口实现。其他情况要想减少工作量,最好的方法就是借助物理引
我们知道cocos2d本身不包含物理引擎,但是它集成了2个开源物理引擎可供我们选择:box2d和chipmunk。对于一般的正规矩形精灵的碰撞检测,可以简单的使用Core Graphics提供的CGRectIntersectsRect接口实现。其他情况要想减少工作量,最好的方法就是借助物理引擎。
本篇教程实现一个菱形和三角形的碰撞检测,足以演示基本的chipmunk用法。 Demo基于cocos2d 2.0 beta,chipmunk6.0.2. 教程截图:
Demo下载地址: http://ityran.com/thread-904-1-1.html 1 Demo流程 首先创建一个三角形的精灵,我把它命名为emery,它静止在屏幕上。每次点击屏幕,从左边固定位置会发射出一个菱形的bullet,bullet匀速直线运动碰到emery后消失。Reset重新开新游戏。 2 创建一个chipmunk的工程 首先需要安装cocos2d 2.0 beta,之所以选择2.0,有以下2个原因:
创建工程的时候选择cocos2d_chipmunk模板, 这样我们就得到一个包含了chipmunk的工程,并自带了测试代码。 而HelloWorldLayer将是我们的主战场,修改它的实现达到前述Demo效果。 3 chipmunk基本概念 space:物理空间,可容纳body,shape,joint。 body:刚体,可被赋予shape。刚体具有质量,转动惯量,位置,线性速度,加速度,角度,角速度,角加速度等属性。刚体之间可通过joint连接。 shape:决定刚体的碰撞外形。一个刚体上可覆盖上多个shape,同属于一个刚体的shape不会互相发生碰撞。 joint: 用于连接刚体。 本篇教程不涉及joint,也不涉及一个body多个shape。 也就是一个精灵对于一个刚体有一个外形。有点绕口~。~ 关于space,默认有一个staticBody属性,staticBody是在整个物理检测中永远保持禁止不动的刚体。这通常用来把屏幕四周设置为墙体,以避免精灵飞出到屏幕外面。 接下来我们看代码实现。 4 chipmunk系统初始化 在AppDelegate.m的applicationDidFinishLaunching函数前面加入 初始化整个chipmunk系统,只需做一次。// init chipmunk cpInitChipmunk(); 模板生成的代码是在layer初始化中完成chipmunk的初始化,这样处理并不是很合适,我把它移到这个位置来。 5 HelloWorldLayer.h解析 @interface HelloWorldLayer : CCLayerColor { cpSpace *space_; // strong ref } @end @interface PhysicsSprite : CCSprite { cpShape *shape_; // strong ref cpSpace *space_; // weak ref } -(void) setPhysicsShape:(cpShape *)shape space:(cpSpace *)space;HelloWorldLayer包含了一个cpSpace,每个layer对应一个space,这很好理解,layer自己管理自己的space,不同的layer的space会不一样。space的生命周期由layer管理。 定义一个PhysicsSprite,他有两个成员变量shape_和space_,space_只是引用,不做生命周期管理。 setPhysicsShape方法把精灵和刚体关联起来。 6 HelloWorldLayer.m解析 6.1 PhysicsSprite的实现 由于cocos2d 2.0 使用OpenGL ES 2.0,这里有了新的改变精灵位置和方向的方法:矩阵变换。 看过泰然OpenGL ES系列教程( 从零开始学习OpenGL ES之七 – 变换和矩阵 )的对这个应该有印象。 -(BOOL) dirty { return YES; }重写dirty方法,返回YES,目的是让每次layer的update调用后重新绘制PhysicsSprite精灵。 -(CGAffineTransform) nodeToParentTransform { CGFloat x = shape_->body->p.x; CGFloat y = shape_->body->p.y; if ( !isRelativeAnchorPoint_ ) { x += anchorPointInPoints_.x; y += anchorPointInPoints_.y; } // Make matrix CGFloat c = shape_->body->rot.x; CGFloat s = shape_->body->rot.y; if( ! CGPointEqualToPoint(anchorPointInPoints_,CGPointZero) ){ x += c*-anchorPointInPoints_.x + -s*-anchorPointInPoints_.y; y += s*-anchorPointInPoints_.x + c*-anchorPointInPoints_.y; } // Translate,Rot,anchor Matrix transform_ = CGAffineTransformMake( c,s,-s,c,x,y ); return transform_; }重写精灵的矩阵变换方法nodeToParentTransform,模板提供的这个实现,能改变精灵的位置和角度。shape_->body->p和shape_->body->rot分别是刚体的位置坐标和角度。我们会看到,在update函数中,调用了chipmunk的cpSpaceStep方法,这个方法根据时间流逝计算出每个刚体的新位置和角度,然后在这里被使用最终达到精灵移动旋转的目的。 (void) removeFromParentAndCleanup:(BOOL)cleanup { cpSpaceRemoveBody(space_,shape_->body); cpBodyFree(shape_->body); cpSpaceRemoveShape(space_,shape_); cpShapeFree(shape_); [super removeFromParentAndCleanup:cleanup]; }重写removeFromParentAndCleanup方法。 在碰撞发生后,bullet精灵消失,与之对应的刚体也需要释放。 在body被free之前,它必须先从space中移出,否则cpSpaceStep会继续计算这个刚体,接下来就是crash~。 shape也是同样的处理。 6.2 HelloWorldLayer初始化 -(id) init { if( (self=[super initWithColor:ccc4(166,166,255)])) { // enable events self.isTouchEnabled = YES; self.isAccelerometerEnabled = YES; CGSize s = [[CCDirector sharedDirector] winSize]; // title CCLabelTTF *label = [CCLabelTTF labelWithString:@"Touch the screen"
6.3 初始化物理引擎 -(void) initPhysics { //CGSize s = [[CCDirector sharedDirector] winSize]; space_ = cpSpaceNew(); // set to zero space_->gravity = ccp(0,0); cpSpaceAddCollisionHandler(space_,kTagBulletNode,kTagEmeryNode,begin,NULL,NULL); }
6.4 Emery精灵的创建 -(void) addNewEmeryAtPosition:(CGPoint)pos { PhysicsSprite *sprite = [PhysicsSprite spriteWithFile:@"triangle.png" rect:CGRectMake(0,50,50)]; sprite.position = pos; sprite.tag = kTagEmeryNode; [self addChild:sprite]; int num = 3; CGPoint verts[] = { ccp(-25,-25),ccp(0,25),ccp(25,}; cpBody *body = cpBodyNew(1.0f,cpMomentForPoly(1.0f,num,verts,CGPointZero)); body->p = pos; cpSpaceAddBody(space_,body); cpShape* shape = cpPolyShapeNew(body,CGPointZero); shape->collision_type = kTagEmeryNode; shape->data = sprite; //shape->e = 0.5f; shape->u = 0.5f; cpSpaceAddShape(space_,shape); [sprite setPhysicsShape:shape space:space_]; }
addNewBulletAtPosition的实现流程大致相同,参数不同而已。 最大的不同点: cpBodySetVel(body,cpv(160,0)); 我们给bullet刚体设置了一个初速度,cpv构成一个速度矢量,表明了方向和速度大小。 6.5 碰撞检测 整个chipmunk工作流程是这样子: 在space中我们摆放好了各个刚体,刚体有shape,速度等属性。 每个update周期,cpSpaceStep依据时间的推移,计算出新的刚体位置坐标,方向等。 cocos2d依据这些数据来重新绘制精灵。 我们设置的碰撞回调函数会在cpSpaceStep中某个阶段被调用。 static int begin(cpArbiter *arb,cpSpace *space,void *unused) { // Get pointers to the two bodies in the collision pair and define local variables for them. // Their order matches the order of the collision types passed // to the collision handler this function was defined for CP_ARBITER_GET_SHAPES(arb,a,b); // additions and removals can't be done in a normal callback. // Schedule a post step callback to do it. // Use the hook as the key and pass along the arbiter. cpSpaceAddPostStepCallback(space,(cpPostStepFunc)postStepRemove,NULL); // The object is dead,don't process the collision further return 0; }
static void postStepRemove(cpSpace *space,cpShape *shape,void *unused) { PhysicsSprite *sprite = shape->data; assert(sprite.tag == kTagBulletNode); if( sprite ) { [sprite removeFromParentAndCleanup:YES]; } }shape->data保存了精灵实例。 assert帮助我们调试,确认下将被销毁的是不是bullet精灵。 removeFromParentAndCleanup把精灵从屏幕上抹去。 6.6 HelloWorldLayer销毁时的内存清理 reset按钮的处理流程,涉及整个space清理,过程如下: CCScene *s = [CCScene node]; id child = [HelloWorldLayer node]; [s addChild:child]; [[CCDirector sharedDirector] replaceScene: s];按钮事件触发以后,创建了一个新的包含HelloWorldLayer 的Scene,来取代当前Scene。 cocos2d会自动去销毁之前的scene,dealloc将被调用。 - (void)dealloc { cpSpaceEachBody(space_,spaceBodyCallback,NULL); cpSpaceEachShape(space_,spaceShapeCallback,NULL); cpSpaceFree( space_ ); [super dealloc]; }cpSpaceEachBody和cpSpaceEachShape是两个遍历方法,遍历space中的所有body和shape,并为每个找到的body或shape调用处理回调函数。 在处理回调函数spaceBodyCallback和spaceShapeCallback里面我们进行释放操作,在这里不cpSpaceRemoveBody和cpSpaceRemoveShape是安全的,space马上就会被释放,update也不会被调用。 7 最后 运行Demo看下效果吧,点击屏幕任意位置会触发一次子弹。 Demo可能比较简陋,如果能达到 Keep It Simple,Stupid 的效果,那就简陋点吧。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |