刚刚接触Box2d,下面通过解读一下默认项目代码了解一下神奇的Box2d物理引擎。
新建一个Box2d的项目。运行这个项目我们单击屏幕,就会出现很多带有字母的小盒子,每个小盒子受重力影响,而且盒子之间会发生碰撞。
下面我注意解说一下其中最主要的HelloWorldLayer类。
下面引用http://www.raywenderlich.com 中的一段解释(这是一个非常不错的博客)
在我们开始之前,让我们先交待一下Box2D具体是如何运作的。
你需要做的第一件事情就是,当使用cocos2d来为box2d创建一个world对象的时候。这个world对象管理物理仿真中的所有对象。
一旦我们已经创建了这个world对象,接下来需要往里面加入一些body对象。body对象可以随意移动,可以是怪物或者飞镖什么的,只要是参与碰撞的游戏对象都要为之创建一个相应的body对象。当然,也可以创建一些静态的body对象,用来表示游戏中的台阶或者墙壁等不可以移动的物体。
为了创建一个body对象,你需要做很多事情–首先,创建一个body定义结构,然后是body对象,再指定一个shap,再是fixture定义,然后再创建一个fixture对象。下面会一个一个解释刚刚这些东西。
只要你把所有需要创建的body对象都创建好之后,box2d接下来就会接管工作,并且高效地进行物理仿真—只要你周期性地调用world对象的step函数就可以了。
但是,请注意,box2d仅仅是更新它内部模型对象的位置–如果你想让cocos2d里面的sprite的位置也更新,并且和物理仿真中的位置相同的话,那么你也需要周期性地更新精灵的位置。
首先看一下文件目录吧!
其中最后的两个文件GLES-Render用于实现OpenGL ES命令的调用,以绘制各种图形,通常在开发中不需要修改。主要实现的内容在HelloWorldLayer文件中。
1、先看一下init(注:这里把有关Game Center的label的有关代码删掉了,这部分大家都懂的)
-(id) init { if( (self=[super init])) { // enable events self.touchEnabled = YES; self.accelerometerEnabled = YES; CGSize s = [CCDirector sharedDirector].winSize; // init physics [self initPhysics]; // create reset button [self createMenu]; //Set up sprite #if 1 // Use batch node. Faster CCSpriteBatchNode *parent = [CCSpriteBatchNode batchNodeWithFile:@"blocks.png" capacity:100]; spriteTexture_ = [parent texture]; #else // doesn't use batch node. Slower spriteTexture_ = [[CCTextureCache sharedTextureCache] addImage:@"blocks.png"]; CCNode *parent = [CCNode node]; #endif [self addChild:parent z:0 tag:kTagParentNode]; [self addNewSpriteAtPosition:ccp(s.width/2, s.height/2)]; CCLabelTTF *label = [CCLabelTTF labelWithString:@"Tap screen" fontName:@"Marker Felt" fontSize:32]; [self addChild:label z:0]; [label setColor:ccc3(0,0,255)]; label.position = ccp( s.width/2, s.height-50); [self scheduleUpdate];//调用该方法,可以让Box2D接管世界 } return self; }
这里的处理提供了两种创建小盒子精灵的方法:第一种是使用 batch node,速度快;第二种不使用batch node ,而是使用纹理缓存 texture cache,速度慢。
注意到其中的 [selfscheduleUpdate]; 调用的是
-(void) update: (ccTime) dt { //It is recommended that a fixed time step is used with Box2D for stability //of the simulation, however, we are using a variable time step here. //You need to make an informed choice, the following URL is useful //http://gafferongames.com/game-physics/fix-your-timestep/ int32 velocityIterations = 8; //速度迭代次数 int32 positionIterations = 1; //位置迭代次数 //迭代次数越多,box2d物理仿真的效果越好,但是耗费的资源也就越多,机器也会卡 // Instruct the world to perform a single step of simulation. It is // generally best to keep the time step and iterations fixed. world->Step(dt, velocityIterations, positionIterations); //周期性的调用step方法实现高效的物理仿真 }
这里的两个参数分别是“速度迭代次数”和“位置迭代次数”--你应该设置他们的范围在8-10之间。(译者:这里的数字越小,精度越小,但是效率更高。数字越大,仿真越精确,但同时耗时更多。8一般是个折中,如果学过数值分析,应该知道迭代步数的具体作用)
在这个方法中,世界对象调用step方法,开始执行物理世界的模拟。
2、添加触摸事件处理
//处理触摸事件 - (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { //Add a new body/atlas sprite at the touched location for( UITouch *touch in touches ) { CGPoint location = [touch locationInView: [touch view]]; location = [[CCDirector sharedDirector] convertToGL: location]; [self addNewSpriteAtPosition: location]; } }
3、
//在特定的位置添加新的精灵对象 -(void) addNewSpriteAtPosition:(CGPoint)p { CCLOG(@"Add sprite %0.2f x %02.f",p.x,p.y); // Define the dynamic body. //Set up a 1m squared box in the physics world b2BodyDef bodyDef; bodyDef.type = b2_dynamicBody; //创建一个动态物体 bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO); b2Body *body = world->CreateBody(&bodyDef); // Define another box shape for our dynamic body. b2PolygonShape dynamicBox; dynamicBox.SetAsBox(.5f, .5f);//These are mid points for our 1m box // Define the dynamic body fixture. 创建一个夹具定义 b2FixtureDef fixtureDef; //将夹具定义的形状设置为刚刚所定义的形状 fixtureDef.shape = &dynamicBox; //设置夹具定义的 密度 摩擦力 弹力 fixtureDef.density = 1.0f; fixtureDef.friction = 0.3f; fixtureDef.restitution = 0.8f; //使用物体的夹具工厂方法来创建夹具 body->CreateFixture(&fixtureDef); CCNode *parent = [self getChildByTag:kTagParentNode]; //We have a 64x64 sprite sheet(精灵表单) with 4 different 32x32 images. The following code is //just randomly picking one of the images 随机获取方块 int idx = (CCRANDOM_0_1() > .5 ? 0:1); int idy = (CCRANDOM_0_1() > .5 ? 0:1); CCPhysicsSprite *sprite = [CCPhysicsSprite spriteWithTexture:spriteTexture_ rect:CGRectMake(32 * idx,32 * idy,32,32)]; //添加到精灵表单中 [parent addChild:sprite]; [sprite setPTMRatio:PTM_RATIO]; [sprite setB2Body:body];//设置精灵对象对应的物体 [sprite setPosition: ccp( p.x, p.y)]; }
//初始化物理世界 -(void) initPhysics { CGSize s = [[CCDirector sharedDirector] winSize]; b2Vec2 gravity; gravity.Set(0.0f, -10.0f); //设置重力加速度 world = new b2World(gravity); // Do we want to let bodies sleep? //SetAllowSleeping()设置该属性以说明物体处于静止状态时是否会进入休眠状态。 //处于休眠状态的物体不会耗费系统处理时间,直到其他物体撞上它,该物体才会苏醒过来。 world->SetAllowSleeping(true); //设置世界是否支持连续碰撞机制 world->SetContinuousPhysics(true); //DebugDraw通常是在测试阶段使用的,注释掉相关代码语句(连同下面的draw方法)也可以正常运行。 m_debugDraw = new GLESDebugDraw( PTM_RATIO ); world->SetDebugDraw(m_debugDraw); //绘制模式 /* enum { e_shapeBit = 0x0001, ///< draw shapes e_jointBit = 0x0002, ///< draw joint connections e_aabbBit = 0x0004, ///< draw axis aligned bounding boxes e_pairBit = 0x0008, ///< draw broad-phase pairs e_centerOfMassBit = 0x0010 ///< draw center of mass frame }; */ uint32 flags = 0; flags += b2Draw::e_shapeBit; // flags += b2Draw::e_jointBit; // flags += b2Draw::e_aabbBit; // flags += b2Draw::e_pairBit; // flags += b2Draw::e_centerOfMassBit; m_debugDraw->SetFlags(flags); // Define the ground body. 创建一个物体定义,并初始化相关的属性 b2BodyDef groundBodyDef; groundBodyDef.position.Set(0, 0); // bottom-left corner 左下角 // Call the body factory which allocates memory for the ground body // from a pool and creates the ground box shape (also from a pool). // The body is also added to the world. //根据物体定义使用世界的工厂方法创建物体 b2Body* groundBody = world->CreateBody(&groundBodyDef); // Define the ground box shape. 为物体定义一个形状 b2EdgeShape groundBox; //定义了一个简单的四边形 即程序运行中的边框 //void Set(const b2Vec2& v1, const b2Vec2& v2); /************************************************************************** * 功能描述: 设置独立的边缘 * 参数说明: v1 : 第一个顶点 v2 : 第二个顶点 * 返 回 值: (void) ***************************************************************************/ // bottom groundBox.Set(b2Vec2(0,0), b2Vec2(s.width/PTM_RATIO,0)); //使用物体的工程方法创建夹具 groundBody->CreateFixture(&groundBox,0); // top groundBox.Set(b2Vec2(0,s.height/PTM_RATIO), b2Vec2(s.width/PTM_RATIO,s.height/PTM_RATIO)); groundBody->CreateFixture(&groundBox,0); // left groundBox.Set(b2Vec2(0,s.height/PTM_RATIO), b2Vec2(0,0)); groundBody->CreateFixture(&groundBox,0); // right groundBox.Set(b2Vec2(s.width/PTM_RATIO,s.height/PTM_RATIO), b2Vec2(s.width/PTM_RATIO,0)); groundBody->CreateFixture(&groundBox,0); }