牛骨文教育服务平台(让学习变的简单)

 李华明Himi 原创,转载务必在明显处注明:
转载自【黑米GameDev街区】 原文链接: http://www.himigame.com/iphone-cocos2d/474.html>

上一节讲述了粒子的相关问题,当然啦,不示弱,今天继续将物理系统给大家进行简单的介绍和讲述;

首先先介绍,如何在cocos2d中加入box2d开发lib包,因为一般使用cocos2d引擎进行开发游戏时,大家创建项目都会选用cocos2d框架,而不是直接采用物理系统的cocos2d框架,但是后期忽然需要在项目中使用物理系统(这种情况很经常发生,至于为什么,童鞋们都懂得~),OK,首先创建一个普通的cocos2d项目;

OK,加入box2d->lib步骤如下:

1.首先将box2d的lib包拷贝到刚创建的项目下,然后右键项目的libs文件夹进行导入项目中;(如果你没有box2d的lib包,那就新建一个cocos2d-box2d的项目就有了)

2.双击你的项目名默认打开配置信息窗口,点击Build Settings标签,然后在页面中找到”Search Paths“一栏,然后在“User Header Search Paths”中添加“xx/libs”;这里的XX是你的项目名称;紧接着在“User Header Search Paths”一项的上面设置“Always Serch Paths”的选项 为YES,默认为NO;这里务必要设置;

3.最后commadn+B (我用的xcode For lion)编译项目代码,如果提示编译成功,OK,可以使用啦;

下面我来给大家简单的介绍以下如何在cocos2d中使用Box2d物理系统,虽然关于Box2d的相关资料和教程很少,但是这里我也不会很详细的介绍和解释,因为我即将上市的Android游戏开发书籍中已经对Box2d进行了很详细的讲解和两个物理小游戏实战,所以这里就大概的介绍下一些重要的方法;

便于讲解,这里我直接使用Xcode直接创建一个cocos2d-Box2d的项目,然后简单的修改:

//
//  HelloWorldLayer.mm
//  Box2dProject
//
//  Created by 华明 李 on 11-9-14.
//  Himi
//
// Import the interfaces
#import "HelloWorldLayer.h"
 
#define PTM_RATIO 32

// enums that will be used as tags
enum {
	kTagTileMap = 1, 
	kTagAnimation1 = 1,
};

// HelloWorldLayer implementation
@implementation HelloWorldLayer

+(CCScene *) scene
{ 
	CCScene *scene = [CCScene node];  
	HelloWorldLayer *layer = [HelloWorldLayer node]; 
	[scene addChild: layer]; 
	return scene;
}

// on "init" you need to initialize your instance
-(id) init
{
	//初始化中,在屏幕上创建了物理世界,并且创建了在屏幕四周创建了刚体防止物理世界中的刚体超屏
    //最后并且调用一个tick方法用于让物理世界不断的去模拟
	if( (self=[super init])) { 
		self.isTouchEnabled = YES; 
		self.isAccelerometerEnabled = YES; 
		CGSize screenSize = [CCDirector sharedDirector].winSize;
		CCLOG(@"Screen width %0.2f screen height %0.2f",screenSize.width,screenSize.height); 
		// Define the gravity vector.
		b2Vec2 gravity;
		gravity.Set(0.0f, -10.0f);
		bool doSleep = true;  
		world = new b2World(gravity, doSleep); 
		world->SetContinuousPhysics(true); 
		// Debug Draw functions
		m_debugDraw = new GLESDebugDraw( PTM_RATIO );
		world->SetDebugDraw(m_debugDraw); 
		uint32 flags = 0;
		flags += b2DebugDraw::e_shapeBit; 
		m_debugDraw->SetFlags(flags);	 
		b2BodyDef groundBodyDef;
		groundBodyDef.position.Set(0, 0); // bottom-left corner 
		b2Body* groundBody = world->CreateBody(&groundBodyDef); 
		b2PolygonShape groundBox;		
		 		// bottom
		groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(screenSize.width/PTM_RATIO,0));
		groundBody->CreateFixture(&groundBox,0);
		
		// top
		groundBox.SetAsEdge(b2Vec2(0,screenSize.height/PTM_RATIO), b2Vec2(screenSize.width/PTM_RATIO,screenSize.height/PTM_RATIO));
		groundBody->CreateFixture(&groundBox,0);
		
		// left
		groundBox.SetAsEdge(b2Vec2(0,screenSize.height/PTM_RATIO), b2Vec2(0,0));
		groundBody->CreateFixture(&groundBox,0);
		
		// right
		groundBox.SetAsEdge(b2Vec2(screenSize.width/PTM_RATIO,screenSize.height/PTM_RATIO), b2Vec2(screenSize.width/PTM_RATIO,0));
		groundBody->CreateFixture(&groundBox,0);
		
		  
		CCLabelTTF *label = [CCLabelTTF labelWithString:@"Himi" fontName:@"Marker Felt" fontSize:32];
		[self addChild:label z:0];
		label.position = ccp( screenSize.width/2, screenSize.height-50); 
		[self schedule: @selector(tick:)];
	}
	return self;
}

//Box2d调试模式,因为物理世界是看不到摸不到的,那么物理世界中的刚体其实也一样无法看到,
//但是为了便于开发调试,Box2d提供了调试类,主要作用是能将物理世界的所有刚体、关节等都利用线条框出来,
//这样便于设置你的Body与Sprite之间的位置关系 ----
-(void) draw
{
	glDisable(GL_TEXTURE_2D);
	glDisableClientState(GL_COLOR_ARRAY);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY); 
	world->DrawDebugData(); 
	// restore default GL states
	glEnable(GL_TEXTURE_2D);
	glEnableClientState(GL_COLOR_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);

}
//---添加一个刚体,首先需要创建刚体的皮肤,可以理解这个皮肤是刚体的属性,然后利用皮肤包装出一个刚体
-(void) addNewSpriteWithCoords:(CGPoint)p sp:(CCSprite*)sprite
{
	CCLOG(@"Add sprite %0.2f x %02.f",p.x,p.y); 
	sprite.position = ccp( p.x, p.y);
	b2BodyDef bodyDef;
	bodyDef.type = b2_dynamicBody;
	bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);
    //将精灵信息赋值给刚体皮肤,这样就能让精灵的运动轨迹与这个即将创建出的刚体在物理世界中的运动轨迹同步
	bodyDef.userData = sprite;
	b2Body *body = world->CreateBody(&bodyDef);
	b2PolygonShape dynamicBox;
	dynamicBox.SetAsBox(.9f, .9f); 
	b2FixtureDef fixtureDef;
	fixtureDef.shape = &dynamicBox;	
	fixtureDef.density = 1.0f;
	fixtureDef.friction = 0.3f;
	body->CreateFixture(&fixtureDef);
    //给body施加一个力
    b2Vec2 force = body->GetWorldVector(b2Vec2(1000.0f, 600.0f));
    b2Vec2 point = body->GetWorldPoint(b2Vec2(0.4f, 0.4f));
    body->ApplyForce(force, point);//----------备注1  Himi
}

//此方法中,首先是让物理世界进行物理模拟,然后不断的遍历物理世界中的刚体运动轨迹复制给对应的精灵
-(void) tick: (ccTime) dt
{
	
	int32 velocityIterations = 8;
	int32 positionIterations = 1; 
	world->Step(dt, velocityIterations, positionIterations);
	for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
	{
		if (b->GetUserData() != NULL) {
			//Synchronize the AtlasSprites position and rotation with the corresponding body
			CCSprite *myActor = (CCSprite*)b->GetUserData();
			myActor.position = CGPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO);
			myActor.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
		}	
	}
}
//---触屏将添加一个body和精灵,位置为玩家触屏的坐标
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
	for( UITouch *touch in touches ) {
		CGPoint location = [touch locationInView: [touch view]]; 
		location = [[CCDirector sharedDirector] convertToGL: location];
        CCSprite *sprite =[CCSprite spriteWithFile:@"icon.png"];
        [self addChild:sprite];
		[self addNewSpriteWithCoords: location sp:sprite];
	}
}

- (void)accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration
{	
	static float prevX=0, prevY=0;
	
	//#define kFilterFactor 0.05f
    #define kFilterFactor 1.0f	// don"t use filter. the code is here just as an example
	
	float accelX = (float) acceleration.x * kFilterFactor + (1- kFilterFactor)*prevX;
	float accelY = (float) acceleration.y * kFilterFactor + (1- kFilterFactor)*prevY;
	prevX = accelX;
	prevY = accelY;
	b2Vec2 gravity( -accelY * 10, accelX * 10);
	world->SetGravity( gravity );
}
 
- (void) dealloc
{ 
	delete world;
	world = NULL; 
	delete m_debugDraw; 
	[super dealloc];
}
@end

这里我只是对重要的方法进行的说明,主要修改的一点地方在备注1这里,我这里对每次玩家触摸屏幕的地方创建的刚体都进行施加了一个力,让刚体进行运动,那么这个运动的轨迹也会根据你设置的物理世界的重力方向发生改变,当前项目中,重力方向垂直下落,没有X轴的变化;

还要注意一点,由于box2d是c++代码,那么如果你使用box2d的话,首先把你的Delegate.m的类改成Delegate.mm,还有你使用box2d相关代码的实现类(.m)格式的类要改成(.mm)格式才可,这样编译器就会知道是混合代码,否则都当成object-c进行编译就会报错;

运行截图如下:

从图中可以看出,在icon图的周围包围着线段,这个就是Box2d提供的调试绘制,将本无形的刚体绘制出来了;

这里我不得不说一些童鞋,例如之前我写过Android上的一个自己随手的物理系统小球的例子,我在博文中写了要触屏才创建小球,但是很多童鞋问我项目运行起来没效果有问题,我就崩溃了。。。你们让我

源码本想上传,但是发现一点击上传资源就悲剧打不开网页,大家可以直接创建一个cocos2d-box2d的项目,然后将HelloWorldLayer.mm中代码换成我上面的代码即可~