Cocos2D 开发之 学习Box2d物理引擎(1)

刚刚接触Box2d,下面通过解读一下默认项目代码了解一下神奇的Box2d物理引擎。

新建一个Box2d的项目。运行这个项目我们单击屏幕,就会出现很多带有字母的小盒子,每个小盒子受重力影响,而且盒子之间会发生碰撞。

下面我注意解说一下其中最主要的HelloWorldLayer类。

下面引用http://www.raywenderlich.com 中的一段解释(这是一个非常不错的博客)


Box2D世界相关理论

在我们开始之前,让我们先交待一下Box2D具体是如何运作的。
  你需要做的第一件事情就是,当使用cocos2d来为box2d创建一个world对象的时候。这个world对象管理物理仿真中的所有对象。
  一旦我们已经创建了这个world对象,接下来需要往里面加入一些body对象。body对象可以随意移动,可以是怪物或者飞镖什么的,只要是参与碰撞的游戏对象都要为之创建一个相应的body对象。当然,也可以创建一些静态的body对象,用来表示游戏中的台阶或者墙壁等不可以移动的物体。
  为了创建一个body对象,你需要做很多事情–首先,创建一个body定义结构,然后是body对象,再指定一个shap,再是fixture定义,然后再创建一个fixture对象。下面会一个一个解释刚刚这些东西。

  • 你首先创建一个body定义结构体,用以指定body的初始属性,比如位置或者速度。
  • 一旦创建好body结构体后,你就可以调用world对象来创建一个body对象了。
  • 然后,你为body对象定义一个shape,用以指定你想要仿真的物体的几何形状。
  • 接着创建一个fixture定义,同时设置之前创建好的shape为fixture的一个属性,并且设置其它的属性,比如质量或者摩擦力。
  • 最后,你可以使用body对象来创建fixture对象,通过传入一个fixture的定义结构就可以了。
  • 请注意,你可以往单个body对象里面添加很多个fixture对象。这个功能在你创建特别复杂的对象的时候非常有用。比如自行车,你可能要创建2个轮子,车身等等,这些fixture可以用关节连接起来。

只要你把所有需要创建的body对象都创建好之后,box2d接下来就会接管工作,并且高效地进行物理仿真—只要你周期性地调用world对象的step函数就可以了。
  但是,请注意,box2d仅仅是更新它内部模型对象的位置–如果你想让cocos2d里面的sprite的位置也更新,并且和物理仿真中的位置相同的话,那么你也需要周期性地更新精灵的位置。


首先看一下文件目录吧!

Cocos2D 开发之 学习Box2d物理引擎(1)_第1张图片

其中最后的两个文件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];
	}
}

将触摸点位置转换成Cocos2d中的位置,然后调用 addNewSpriteAtPosition: 方法,添加小盒子。


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)];

}

  • Density 就是单位体积的质量(密度)。因此,一个对象的密度越大,那么它就有更多的质量,当然就会越难以移动.
  • Friction 就是摩擦力。它的范围是0-1.0, 0意味着没有摩擦,1代表最大摩擦,几乎移不动的摩擦。
  • Restitution 回复力。它的范围也是0到1.0. 0意味着对象碰撞之后不会反弹,1意味着是完全弹性碰撞,会以同样的速度反弹。
4、
//初始化物理世界
-(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);
}

这个默认的项目大致就是这样子了,开始接触Box2d......

你可能感兴趣的:(cocos2d-x,cocos2d-iphone,box2D,物理引擎)