1.cocos2d 自带了两套物理引擎:Box2D 和Chipmunk。
两套引擎都是为2D游戏设计的,可以和cocos2d 完美整合。
Box2D 是用 C++写的,而 Chipmunk 用的是 C。
Box2D中的变量和方法名都是用全称命名的,Chipmunk中很多地方用的是只有一个字母的简写。有一些功能只有Box2D提供,Chipmunk是没有的。比如,Box2D有针对快速移动
物体(例如子弹)直接穿透物体而不进行碰撞测试的解决方法。 ...
2.刚体:
物理物体叫做“刚体”(Rigid Bodies),这是因为物理引擎在驱动这些物体生成动画时,把它们当作硬的,不会变形的物体。将物体这样简化以后,物理引擎就可以同时计算大量的刚体了。
物理引擎中存在两种刚体:动态移动的(dynamic )和静态的(static)刚体。
静态刚体不会移动,也不应该移动- 因为物理引擎可以依赖静态刚体不会互相碰撞这个特性来做出一些优化。静态刚体的密度可以被设为0.
动态刚体,它们会相互碰撞,而且也会和静态刚体碰撞。动态刚体除了拥有位置
(position)和旋转(rotation )参数,还有至少3个用于定义动态刚体的参数。
它们分别是:密度或者质量(density or mass)- 用于衡量刚体有多重。另一
个是摩擦力(friction )- 用于表示刚体在平面上移动时遇到阻力的大小或者有多
滑。最后是复原(restitution )- 用于定义刚体的弹性。
3.碰撞
现实世界里的物体在运动时都会丢失能量,但是你可以在物理引擎中生成移动碰撞后不会丢失任何能量的动态刚体,甚至让刚体在与别的刚体发生碰撞弹回来以后,以更快的速度进行移动。
动态和静态刚体外面都有一个或者多个形体。这个形体用于判断刚体之间的碰撞的。每一次碰撞会生成一些碰撞点(contact points ) - 两个相碰撞刚体之间的交叉点。这些碰撞点可被用于播放粒子效果或者在刚体的碰撞处动态地添加刮痕。
物理引擎通过使用力量,脉冲和扭矩生成动态刚体的动画。不能直接设置刚体的位置和旋转。如果手动修改物体位置信息,物理引擎先前所作的某些假设就会失效。
4.关节
可以使用关节(joints )把多个刚体连接起来,这样可以用不同的方式限制相互连接着的刚体的活动。某些关节可能配备有马达,比如它们可被用于驱动汽车的轮子或者给关节产生摩擦力,这样关节在向某个方向移动以后,会试着回到原来的位置。
关节通过使用b2World类的CreateJoint方法来生成。使用刚体的 GetWorld 方法就可以得到b2World。
// 生成旋转关节
b2RevoluteJointDef jointDef;
jointDef.Initialize(bodyA, bodyB, bodyB->GetWorldCenter());
bodyA->GetWorld()->CreateJoint(&jointDef);
柱状关节:只允许朝一个方向移动,单筒望远镜是一个应用柱状关节的很好的例子。
b2PrismaticJointDef jointDef;
b2Vec2 worldAxis(0.0f, 1.0f);
jointDef.Initialize(bodyA, bodyB, body->GetWorldCenter(), w orldAxis);
jointDef.lowerTranslation = 0.0f;
jointDef.upperTranslation = 0.75f;
jointDef.enableLimit = true;
jointDef.maxMoto rForce = 60.0f;
jointDef.motorSpeed = 20.0f;
jointDef.enableMotor = false;
joint = (b2PrismaticJoint*)body- >GetWorld()->CreateJoint(&jointDef);
把活塞的摩擦力和密度设置为极限值,这样弹球就不会在碰到活塞的时候弹跳出去了,这可以保证平滑的发射动作。
fixtureDef.friction = 0.99f;
fixtureDef.restitution = 0.01f;
5.物理引擎的局限性
真实世界过于复杂,完全放到物理引擎中进行模拟是不可能的。这就是为什么要使用刚体的原因。
在某些极端情况下,物理引擎有可能会捕捉不到某些已经发生的碰撞 – 例如,当刚体以很快的速度移动时,一个刚体可能直接穿透另一个刚体。虽然在量子物理学中这样的穿透情况会发生。
刚体有时候会相互穿透卡在一起,特别是在使用了关节将它们连接在一起以后。卡在一起的刚体会努力要分开,但是为了满足关节的连接要求,它们又不得不卡在一起,结果是卡在一起的刚体会产生颤动。
我们也可能碰到游戏运行的问题。如果我们在游戏里使用了很多刚体,你永远不会知道这些刚体相互作用后的最终结果。最终,有些玩家会把自己卡死在刚体中,或者他们也可能会发现如何利用物理模拟的漏洞,跑到游戏中他们本来不应该去的区域。
6.Box2D
因为Box2D使用C++写的,所以必须使用.mm作为所有项目的实现文件的后缀名,而不是通常的.m后缀名。.mm后缀用于告知Xcode把有此后缀名的文件作为Objective-C++或者C++代码来处理。如果你使用了.m后缀,Xcode就会把代码以Objective-C和C来处理,Xcode就不能正确处理Box2D的C++代码了。因此,如果碰到很多编译错误,首先检查一下是不是所有的实现文件都是以.mm作为后缀的。
Box2D手册:http://www.box2d.org/manual.html
Box2D的API参考下载:http://code.google.com/p/box2d
7.b2World
b2World初始化时使用了一个初始的重力矢量值和用于决定是否允许动态刚体“睡眠”的标记变量。
例:对Box2D世界进行初始化
b2Vec2 gravity = b2Vec2(0.0f, -10.0f);
bool allowBodiesToSleep = true;
world = new b2World(gravity, allowBodiesToSleep);
会“睡眠”的动态刚体:当施加到某个刚体上的力量小于临界值一段时间以后,这个刚体将会进入“睡眠”状态。换句话说,如果某个刚体移动或者旋转的很慢或者根本不在动的话,物理引擎将会把它标记为“睡眠”状态,不再对其施加力量,直到新的力量施加到刚体
上让其再次移动或者旋转。通过把一些刚体标记为“睡眠”状态,物理引擎可
以省下很多时间。除非你游戏中的所有动态刚体处于持续的运动中,否则应该把
allowBodiesToSleep变量设置为true。
传入Box2D世界的重力是一个b2Vec2的struct类型。它和CGPoint在本质上是
一样的,都储存着x 轴和y 轴的浮点值。和真实世界一样重力都是一个常量(0,-10)。
8.刚体b2Body
把活动范围限制在屏幕之内:
创建一个静态刚体:
b2BodyDef containerBodyDef;
b2Body* containerBody = world->CreateBody(&containerBodyDef);
刚体通常都是使用world的CreateBody方法来生成的,可以确保正确地分配和释放刚体所占用的内存。默认情况下,一个空的刚体定义会生成一个位于(0,0)位置的静态刚体。
填充4条边,以构成空心刚体,屏幕的四个边需要单独创建,以便只有四个边是实心的,中间则是空心的,用于放入其它刚体:
b2PolygonShape screenBoxShape;
int density = 0;
// 底部
screenBoxShape.SetAsEdge(lowerLeftCorner, lowerRightCorner);
containerBody->CreateFixture(&screenBoxShape, density);
// 顶部
screenBoxShape.SetAsEdge(upperLe ftCorner, upperRightCorner);
containerBody->CreateFixture(&screenBoxShape, density);
// 左边
screenBoxShape.SetAsEdge(upperLeftCorner, lowerLeftCorner);
containerBody->CreateFixture(&screenBoxShape, density);
// 右边
screenBoxShape.SetAsEdge(upperRi ghtCorner, lowerRightCorner);
containerBody->CreateFixture(&screenBoxShape, density);
b2PolygonShape类还有SetAsBox方法,调用它会生成的是一个实心的刚体。
使用SetAsBox方法生成的盒子是其接受的参数大小的两倍,所以提供给这个方法的
参数坐标值要除以2,或者乘以0.5f。
9.单位:Box2D中距离以米为单位,质量以公斤为单位,时间以秒为单位。
Box2D在0.1米到10米的范围内工作是最优化的,因为它针对这个范围做过专门优化,把创建的Box2D世界中的刚体的大小限定在越接近1米越好,太小或者太大的刚体很可能会在游戏运行过程中产生错误和奇怪的行为。
PTM_RATIO的定义如下: #define PTM_RATIO 32
PTM_RATIO用于定义32个像素在Box2D世界中等同于1米。一个有32像素宽和高的
盒子形状的刚体等同于1米宽和高的物体。2x32像素大小的瓷砖的尺寸刚好是1x1米,大小4x4像素的大小是0.125米x0.125米。
Box2d在处理大小在0.1到10个单元的对象的时候做了一些优化。这里的0.1米大概就是一个杯子那么大,10的话,大概就是一个箱子的大小。
屏幕的宽度和高度值除以一个名为 PTM_RATIO的常量,把像素值转换成了以米为单位来
计算长度:
CGSize screenSize = [CCDirector sharedDirector].winSize;
float widthInMeters = screenSize.width / PTM_RATIO;
float heightInMeters = screenSize.height / PTM_RATIO;
//四个角的坐标, 以米为单位:
b2Vec2 lowerLeftCorner = b2Vec2(0, 0);
b2Vec2 lowerRightCorner = b2Vec2(widthInMeters, 0);
b2Vec2 upperLeftCorner = b2Vec2(0, heightInMeters);
b2Vec2 upperRightCorner = b2Vec2(widthInMeters, heightInMeters) ;
在b2Vec2和CGPoint之间转换:
-(b2Vec2) toMeters:(CGPoint)point
{
return b2Vec2(point.x / PTM_R ATIO, point.y / PTM_RATIO);
}
-(CGPoint) toPixels:(b2Vec2)vec
{
return ccpMult(CGPointMake (vec.x, vec.y), PTM_RATIO);
}
10.生成一个由精灵作为表现的动态刚体示例:
-(void) addNewSpriteAt:(CGPoint)pos
{
CCSpriteBatchNode* batch = (CCSpriteBatchNode*)[self getChildByTag:kTagBatchNode];
int idx = CCRANDOM_0_1() * TILESET_COLUMNS;
int idy = CCRANDOM_0_1 () * TILESET_ROWS;
CGRect tileRect = CGRectMake(TILESIZE * idx, TILESIZE * idy, TILESIZE, TILESIZE);
CCSprite* sprite = [CCSprite sp riteWithBatchNod e:batch rect:tileRect];
sprite.position = pos;
[batch addChild:sprite];
//创建一个刚体的定义,并将其设置为动态刚体
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position = [self toMeters:pos];
bodyDef.userData = sprite;
b2Body* body = world->CreateBody(&bodyDef);
// 定义一个盒子形状,并将其复制给body fixture
b2PolygonShape dynamicBox;
float tileInMeters = TILESIZE / PTM_RATIO;
dynamicBox.SetAsBox(tileInMeters * 0.5f, tileInMe ters * 0.5f);
b2FixtureDef fixtureDef;
fixtureDef.shape = &dynamicBox;
fixtureDef.density = 0.3f;
fixtureDef.friction = 0.5f;
fixtureDef.restitution = 0.6f;
body->CreateFixture(&fixtureDef);
}
世界创建刚体,刚体再创建它的附着物。刚体和精灵没有直接关系,它们只是位置被维持相同罢了。
可以把b2FixtureDef 理解成包含着刚体需要用到的所有数据的容器。这些数据包括:刚体的形状(最重要的一项),密度,摩擦力和复原(这项会影响刚体在world中移动和弹跳的方式)。
11.box2d的动画
刚体看不见但摸的着,精灵看的见但摸不着。在update方法中遍历每个刚体,把刚体的userData返回,并且转换成CCSprite指针。把刚体的位置信息转换成像素值,赋值给精灵的位置属性,让精灵可以随着刚体一起移动。同样地,设置精灵的角度值。
Box2D的world是通过定期地调用Step方法来实现动画的。
Step方法需要三个参数。
第一个是timeStep,它会告诉Box2D自从上次更新以后已经过去多长时间了,直接影响着刚体会在这一步移动多长距离。不建议使用delta time来作为timeStep的值,因为delta time会上下浮动,刚体就不能以相同的速度移动了。
第二和第三个参数是迭代次数。它们被用于决定物理模拟的精确程度,也决定着计算刚体移动所需要的时间。
示例:更新每个刚体相关联的精灵的位置和旋转信息
-(void) update:(ccTime)delta
{
//使用固定的时间间隔将物理模拟向前推进一步
float timeStep = 0.03f;
int32 velocityIterations = 8;
int32 positionIterations = 1;
world->Step(timeStep, velocityIterati ons, positionIterations);
for (b2Body* body = world->GetBodyList(); body != nil; body = body->GetNext())
{
CCSprite* sprite = (CCSprite*)body->GetUserData();
if (sprite!= NULL)
{
sprite.position = [self toPixels:body->GetPosition()];
float angle = body ->GetAngle();
sprite.rotation = CC_RADIAN S_TO_DEGREES(angle) * -1;
}
}
}
12.碰撞测试
创建一个继承自b2ContactListener的新类,重写BeginContact和EndContact这两个方法,任何时候两个刚体发生碰撞时都会调用这两个方法。
将它的实例设为world的contact listener:
contactListener = new ContactListener();
world->SetContactListener(contactListener);
在b2ContactListener实例的回调中不能进行任何更改游戏物理世界的操作,所以我们可以保存两个碰撞物体的引用,然后待会在update中做其它处理。
13.
凸面体的特点是:在凸面体里面找任意两个点,这两个点连成的线的任何部分都不会落在凸面体外面。
凹面体(Concave)里任意两个点连成的线可能有一部分会落在外面。
组成多边形的顶点在定义是要以反时针方向来进行。
使用VertexHelper工具,通过把顶点一个接一个画出来的方式来 生成碰撞测试用的多边形。
VertexHelper的源代码是通过GitHub来共享的: http://github.com/jfahrenkrug/VertexHelper.,下载下来是一个xcode mac工程,
VertexHelper只是一个帮助寻找凸面体顶点数组的工具,我们需要的是它生成的多边形顶点数组,对于一个物体,可以把它当作一个凸面体,而对于一个地图,要在其中找到多个凸面体,在VertexHelper中生成的顶点数组代码,可以一段一段的拷贝,当作多个多边形,只要认为某一段的顶点若连起来后是凸面体就好了。
顶点的位置是相对于body中心的,最大顶点数量不能超过8,数量越大,越费内存,性能也越差。
14.步骤
加载场景,场景中有一个CCLayer
在CCLayer中初始化世界:设置边界,设置监听
启用渲染调试
加载纹理贴图,*.plist
在CCLayer中加载背景颜色层 CCLayerColor
在CCLayer中添加精灵批处理,CCSpriteBatchNode,(可以在每个CCNode中添加同一个纹理贴图生成的精灵批处理,以方便此node中快速生成sprite)
添加静态元素层CCNode,此node中可以添加一些CCSprite。
使用world创建刚体,然后创建和刚体对应的sprite添加到精灵批处理,并关联给刚体的userdata,此处代码也可以封装一下。
在预约的update回调方法中,遍历世界中的所有刚体,把精灵的位置和刚体的位置及角度同步。
示例中的BodyNode被添加到了TableSetup中,只是起到了保存实例引用的作用。
15.形状
b2CircleShape shape;
float radiusInMeters = (tempSprite.contentS ize.width / PTM_RATI O) * 0.5f;
shape.m_radius = radiusInMeters;
将b2BodyDef的angularDamping的域设为0.9f,它会让球对转弯的动作产生更大的阻力。这样弹球在滑过弹球桌面的时候不会产生很多的自身旋转,这对于用金属制作的有一定重量的弹球来说是标准的移动方式。
16.variable length array of non-POD element type 'b2Vec2'
问题分析:错误指向的是cocos2d里一个结构体数组定义,如下:
b2Vec2 vertices[vertexCount];
问题解决:把其改成指针形式,如下:
b2Vec2 *vertices = new b2Vec2[vertexCount];
搞定。
17.添加box2d
将libs/box2d目录拷贝到工程中;
User Header Search Paths "KnowledgeGame/Classes/libs" non
Always search user paths YES
所有与box2d扯上关系的源文件修改为.mm
18.Box2D具体是如何运作的。
创建了world对象,接下来需要往里面加入一些body对象。body对象可以随意移动,可以是怪物或者飞镖什么的,只要是参与碰撞的游戏对象都要为之创建一个相应的body对象。当然,也可以创建一些静态的body对象,用来表示游戏中的台阶或者墙壁等不可以移动的物体。
为了创建一个body对象,首先,创建一个body定义结构,然后是body对象,再指定一个shap,再是fixture定义,然后再创建一个fixture对象。下面详细介绍这个过程:
首先创建一个body定义结构体,用以指定body的初始属性,比如位置或者速度。
然后调用world对象来创建一个body对象。
然后为body对象定义一个shape,用以指定想要仿真的物体的几何形状。
接着创建一个fixture定义,同时设置之前创建好的shape为fixture的一个属性,并且设置其它的属性,比如质量或者摩擦力。
最后,可以使用body对象来创建fixture对象,通过传入一个fixture的定义结构就可以了。
请注意,可以往单个body对象里面添加很多个fixture对象。这个功能在创建特别复杂的对象的时候非常有用。比如自行车,可能要创建2个轮子,车身等等,这些fixture可以用关节连接起来。
只要把所有需要创建的body对象都创建好之后,box2d接下来就会接管工作,并且高效地进行物理仿真—只要你周期性地调用world对象的step函数就可以了。
但是,请注意,box2d仅仅是更新它内部模型对象的位置–如果想让cocos2d里面的sprite的位置也更新,并且和物理仿真中的位置相同的话,那么你也需要周期性地更新精灵的位置。
19.density,friction和restitution参数的意义。
Density 就是单位体积的质量(密度)。因此,一个对象的密度越大,那么它就有更多的质量,当然就会越难以移动.
Friction 就是摩擦力。它的范围是0-1.0, 0意味着没有摩擦,1代表最大摩擦,几乎移不动的摩擦。
Restitution 回复力。它的范围也是0到1.0. 0意味着对象碰撞之后不会反弹,1意味着是完全弹性碰撞,会以同样的速度反弹。
20.Box2d用户手册
http://www.box2d.org/manual.html
21.
两套引擎都是为2D游戏设计的,可以和cocos2d 完美整合。
Box2D 是用 C++写的,而 Chipmunk 用的是 C。
Box2D中的变量和方法名都是用全称命名的,Chipmunk中很多地方用的是只有一个字母的简写。有一些功能只有Box2D提供,Chipmunk是没有的。比如,Box2D有针对快速移动
物体(例如子弹)直接穿透物体而不进行碰撞测试的解决方法。 ...
2.刚体:
物理物体叫做“刚体”(Rigid Bodies),这是因为物理引擎在驱动这些物体生成动画时,把它们当作硬的,不会变形的物体。将物体这样简化以后,物理引擎就可以同时计算大量的刚体了。
物理引擎中存在两种刚体:动态移动的(dynamic )和静态的(static)刚体。
静态刚体不会移动,也不应该移动- 因为物理引擎可以依赖静态刚体不会互相碰撞这个特性来做出一些优化。静态刚体的密度可以被设为0.
动态刚体,它们会相互碰撞,而且也会和静态刚体碰撞。动态刚体除了拥有位置
(position)和旋转(rotation )参数,还有至少3个用于定义动态刚体的参数。
它们分别是:密度或者质量(density or mass)- 用于衡量刚体有多重。另一
个是摩擦力(friction )- 用于表示刚体在平面上移动时遇到阻力的大小或者有多
滑。最后是复原(restitution )- 用于定义刚体的弹性。
3.碰撞
现实世界里的物体在运动时都会丢失能量,但是你可以在物理引擎中生成移动碰撞后不会丢失任何能量的动态刚体,甚至让刚体在与别的刚体发生碰撞弹回来以后,以更快的速度进行移动。
动态和静态刚体外面都有一个或者多个形体。这个形体用于判断刚体之间的碰撞的。每一次碰撞会生成一些碰撞点(contact points ) - 两个相碰撞刚体之间的交叉点。这些碰撞点可被用于播放粒子效果或者在刚体的碰撞处动态地添加刮痕。
物理引擎通过使用力量,脉冲和扭矩生成动态刚体的动画。不能直接设置刚体的位置和旋转。如果手动修改物体位置信息,物理引擎先前所作的某些假设就会失效。
4.关节
可以使用关节(joints )把多个刚体连接起来,这样可以用不同的方式限制相互连接着的刚体的活动。某些关节可能配备有马达,比如它们可被用于驱动汽车的轮子或者给关节产生摩擦力,这样关节在向某个方向移动以后,会试着回到原来的位置。
关节通过使用b2World类的CreateJoint方法来生成。使用刚体的 GetWorld 方法就可以得到b2World。
// 生成旋转关节
b2RevoluteJointDef jointDef;
jointDef.Initialize(bodyA, bodyB, bodyB->GetWorldCenter());
bodyA->GetWorld()->CreateJoint(&jointDef);
柱状关节:只允许朝一个方向移动,单筒望远镜是一个应用柱状关节的很好的例子。
b2PrismaticJointDef jointDef;
b2Vec2 worldAxis(0.0f, 1.0f);
jointDef.Initialize(bodyA, bodyB, body->GetWorldCenter(), w orldAxis);
jointDef.lowerTranslation = 0.0f;
jointDef.upperTranslation = 0.75f;
jointDef.enableLimit = true;
jointDef.maxMoto rForce = 60.0f;
jointDef.motorSpeed = 20.0f;
jointDef.enableMotor = false;
joint = (b2PrismaticJoint*)body- >GetWorld()->CreateJoint(&jointDef);
把活塞的摩擦力和密度设置为极限值,这样弹球就不会在碰到活塞的时候弹跳出去了,这可以保证平滑的发射动作。
fixtureDef.friction = 0.99f;
fixtureDef.restitution = 0.01f;
5.物理引擎的局限性
真实世界过于复杂,完全放到物理引擎中进行模拟是不可能的。这就是为什么要使用刚体的原因。
在某些极端情况下,物理引擎有可能会捕捉不到某些已经发生的碰撞 – 例如,当刚体以很快的速度移动时,一个刚体可能直接穿透另一个刚体。虽然在量子物理学中这样的穿透情况会发生。
刚体有时候会相互穿透卡在一起,特别是在使用了关节将它们连接在一起以后。卡在一起的刚体会努力要分开,但是为了满足关节的连接要求,它们又不得不卡在一起,结果是卡在一起的刚体会产生颤动。
我们也可能碰到游戏运行的问题。如果我们在游戏里使用了很多刚体,你永远不会知道这些刚体相互作用后的最终结果。最终,有些玩家会把自己卡死在刚体中,或者他们也可能会发现如何利用物理模拟的漏洞,跑到游戏中他们本来不应该去的区域。
6.Box2D
因为Box2D使用C++写的,所以必须使用.mm作为所有项目的实现文件的后缀名,而不是通常的.m后缀名。.mm后缀用于告知Xcode把有此后缀名的文件作为Objective-C++或者C++代码来处理。如果你使用了.m后缀,Xcode就会把代码以Objective-C和C来处理,Xcode就不能正确处理Box2D的C++代码了。因此,如果碰到很多编译错误,首先检查一下是不是所有的实现文件都是以.mm作为后缀的。
Box2D手册:http://www.box2d.org/manual.html
Box2D的API参考下载:http://code.google.com/p/box2d
7.b2World
b2World初始化时使用了一个初始的重力矢量值和用于决定是否允许动态刚体“睡眠”的标记变量。
例:对Box2D世界进行初始化
b2Vec2 gravity = b2Vec2(0.0f, -10.0f);
bool allowBodiesToSleep = true;
world = new b2World(gravity, allowBodiesToSleep);
会“睡眠”的动态刚体:当施加到某个刚体上的力量小于临界值一段时间以后,这个刚体将会进入“睡眠”状态。换句话说,如果某个刚体移动或者旋转的很慢或者根本不在动的话,物理引擎将会把它标记为“睡眠”状态,不再对其施加力量,直到新的力量施加到刚体
上让其再次移动或者旋转。通过把一些刚体标记为“睡眠”状态,物理引擎可
以省下很多时间。除非你游戏中的所有动态刚体处于持续的运动中,否则应该把
allowBodiesToSleep变量设置为true。
传入Box2D世界的重力是一个b2Vec2的struct类型。它和CGPoint在本质上是
一样的,都储存着x 轴和y 轴的浮点值。和真实世界一样重力都是一个常量(0,-10)。
8.刚体b2Body
把活动范围限制在屏幕之内:
创建一个静态刚体:
b2BodyDef containerBodyDef;
b2Body* containerBody = world->CreateBody(&containerBodyDef);
刚体通常都是使用world的CreateBody方法来生成的,可以确保正确地分配和释放刚体所占用的内存。默认情况下,一个空的刚体定义会生成一个位于(0,0)位置的静态刚体。
填充4条边,以构成空心刚体,屏幕的四个边需要单独创建,以便只有四个边是实心的,中间则是空心的,用于放入其它刚体:
b2PolygonShape screenBoxShape;
int density = 0;
// 底部
screenBoxShape.SetAsEdge(lowerLeftCorner, lowerRightCorner);
containerBody->CreateFixture(&screenBoxShape, density);
// 顶部
screenBoxShape.SetAsEdge(upperLe ftCorner, upperRightCorner);
containerBody->CreateFixture(&screenBoxShape, density);
// 左边
screenBoxShape.SetAsEdge(upperLeftCorner, lowerLeftCorner);
containerBody->CreateFixture(&screenBoxShape, density);
// 右边
screenBoxShape.SetAsEdge(upperRi ghtCorner, lowerRightCorner);
containerBody->CreateFixture(&screenBoxShape, density);
b2PolygonShape类还有SetAsBox方法,调用它会生成的是一个实心的刚体。
使用SetAsBox方法生成的盒子是其接受的参数大小的两倍,所以提供给这个方法的
参数坐标值要除以2,或者乘以0.5f。
9.单位:Box2D中距离以米为单位,质量以公斤为单位,时间以秒为单位。
Box2D在0.1米到10米的范围内工作是最优化的,因为它针对这个范围做过专门优化,把创建的Box2D世界中的刚体的大小限定在越接近1米越好,太小或者太大的刚体很可能会在游戏运行过程中产生错误和奇怪的行为。
PTM_RATIO的定义如下: #define PTM_RATIO 32
PTM_RATIO用于定义32个像素在Box2D世界中等同于1米。一个有32像素宽和高的
盒子形状的刚体等同于1米宽和高的物体。2x32像素大小的瓷砖的尺寸刚好是1x1米,大小4x4像素的大小是0.125米x0.125米。
Box2d在处理大小在0.1到10个单元的对象的时候做了一些优化。这里的0.1米大概就是一个杯子那么大,10的话,大概就是一个箱子的大小。
屏幕的宽度和高度值除以一个名为 PTM_RATIO的常量,把像素值转换成了以米为单位来
计算长度:
CGSize screenSize = [CCDirector sharedDirector].winSize;
float widthInMeters = screenSize.width / PTM_RATIO;
float heightInMeters = screenSize.height / PTM_RATIO;
//四个角的坐标, 以米为单位:
b2Vec2 lowerLeftCorner = b2Vec2(0, 0);
b2Vec2 lowerRightCorner = b2Vec2(widthInMeters, 0);
b2Vec2 upperLeftCorner = b2Vec2(0, heightInMeters);
b2Vec2 upperRightCorner = b2Vec2(widthInMeters, heightInMeters) ;
在b2Vec2和CGPoint之间转换:
-(b2Vec2) toMeters:(CGPoint)point
{
return b2Vec2(point.x / PTM_R ATIO, point.y / PTM_RATIO);
}
-(CGPoint) toPixels:(b2Vec2)vec
{
return ccpMult(CGPointMake (vec.x, vec.y), PTM_RATIO);
}
10.生成一个由精灵作为表现的动态刚体示例:
-(void) addNewSpriteAt:(CGPoint)pos
{
CCSpriteBatchNode* batch = (CCSpriteBatchNode*)[self getChildByTag:kTagBatchNode];
int idx = CCRANDOM_0_1() * TILESET_COLUMNS;
int idy = CCRANDOM_0_1 () * TILESET_ROWS;
CGRect tileRect = CGRectMake(TILESIZE * idx, TILESIZE * idy, TILESIZE, TILESIZE);
CCSprite* sprite = [CCSprite sp riteWithBatchNod e:batch rect:tileRect];
sprite.position = pos;
[batch addChild:sprite];
//创建一个刚体的定义,并将其设置为动态刚体
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position = [self toMeters:pos];
bodyDef.userData = sprite;
b2Body* body = world->CreateBody(&bodyDef);
// 定义一个盒子形状,并将其复制给body fixture
b2PolygonShape dynamicBox;
float tileInMeters = TILESIZE / PTM_RATIO;
dynamicBox.SetAsBox(tileInMeters * 0.5f, tileInMe ters * 0.5f);
b2FixtureDef fixtureDef;
fixtureDef.shape = &dynamicBox;
fixtureDef.density = 0.3f;
fixtureDef.friction = 0.5f;
fixtureDef.restitution = 0.6f;
body->CreateFixture(&fixtureDef);
}
世界创建刚体,刚体再创建它的附着物。刚体和精灵没有直接关系,它们只是位置被维持相同罢了。
可以把b2FixtureDef 理解成包含着刚体需要用到的所有数据的容器。这些数据包括:刚体的形状(最重要的一项),密度,摩擦力和复原(这项会影响刚体在world中移动和弹跳的方式)。
11.box2d的动画
刚体看不见但摸的着,精灵看的见但摸不着。在update方法中遍历每个刚体,把刚体的userData返回,并且转换成CCSprite指针。把刚体的位置信息转换成像素值,赋值给精灵的位置属性,让精灵可以随着刚体一起移动。同样地,设置精灵的角度值。
Box2D的world是通过定期地调用Step方法来实现动画的。
Step方法需要三个参数。
第一个是timeStep,它会告诉Box2D自从上次更新以后已经过去多长时间了,直接影响着刚体会在这一步移动多长距离。不建议使用delta time来作为timeStep的值,因为delta time会上下浮动,刚体就不能以相同的速度移动了。
第二和第三个参数是迭代次数。它们被用于决定物理模拟的精确程度,也决定着计算刚体移动所需要的时间。
示例:更新每个刚体相关联的精灵的位置和旋转信息
-(void) update:(ccTime)delta
{
//使用固定的时间间隔将物理模拟向前推进一步
float timeStep = 0.03f;
int32 velocityIterations = 8;
int32 positionIterations = 1;
world->Step(timeStep, velocityIterati ons, positionIterations);
for (b2Body* body = world->GetBodyList(); body != nil; body = body->GetNext())
{
CCSprite* sprite = (CCSprite*)body->GetUserData();
if (sprite!= NULL)
{
sprite.position = [self toPixels:body->GetPosition()];
float angle = body ->GetAngle();
sprite.rotation = CC_RADIAN S_TO_DEGREES(angle) * -1;
}
}
}
12.碰撞测试
创建一个继承自b2ContactListener的新类,重写BeginContact和EndContact这两个方法,任何时候两个刚体发生碰撞时都会调用这两个方法。
将它的实例设为world的contact listener:
contactListener = new ContactListener();
world->SetContactListener(contactListener);
在b2ContactListener实例的回调中不能进行任何更改游戏物理世界的操作,所以我们可以保存两个碰撞物体的引用,然后待会在update中做其它处理。
13.
凸面体的特点是:在凸面体里面找任意两个点,这两个点连成的线的任何部分都不会落在凸面体外面。
凹面体(Concave)里任意两个点连成的线可能有一部分会落在外面。
组成多边形的顶点在定义是要以反时针方向来进行。
使用VertexHelper工具,通过把顶点一个接一个画出来的方式来 生成碰撞测试用的多边形。
VertexHelper的源代码是通过GitHub来共享的: http://github.com/jfahrenkrug/VertexHelper.,下载下来是一个xcode mac工程,
VertexHelper只是一个帮助寻找凸面体顶点数组的工具,我们需要的是它生成的多边形顶点数组,对于一个物体,可以把它当作一个凸面体,而对于一个地图,要在其中找到多个凸面体,在VertexHelper中生成的顶点数组代码,可以一段一段的拷贝,当作多个多边形,只要认为某一段的顶点若连起来后是凸面体就好了。
顶点的位置是相对于body中心的,最大顶点数量不能超过8,数量越大,越费内存,性能也越差。
14.步骤
加载场景,场景中有一个CCLayer
在CCLayer中初始化世界:设置边界,设置监听
启用渲染调试
加载纹理贴图,*.plist
在CCLayer中加载背景颜色层 CCLayerColor
在CCLayer中添加精灵批处理,CCSpriteBatchNode,(可以在每个CCNode中添加同一个纹理贴图生成的精灵批处理,以方便此node中快速生成sprite)
添加静态元素层CCNode,此node中可以添加一些CCSprite。
使用world创建刚体,然后创建和刚体对应的sprite添加到精灵批处理,并关联给刚体的userdata,此处代码也可以封装一下。
在预约的update回调方法中,遍历世界中的所有刚体,把精灵的位置和刚体的位置及角度同步。
示例中的BodyNode被添加到了TableSetup中,只是起到了保存实例引用的作用。
15.形状
b2CircleShape shape;
float radiusInMeters = (tempSprite.contentS ize.width / PTM_RATI O) * 0.5f;
shape.m_radius = radiusInMeters;
将b2BodyDef的angularDamping的域设为0.9f,它会让球对转弯的动作产生更大的阻力。这样弹球在滑过弹球桌面的时候不会产生很多的自身旋转,这对于用金属制作的有一定重量的弹球来说是标准的移动方式。
16.variable length array of non-POD element type 'b2Vec2'
问题分析:错误指向的是cocos2d里一个结构体数组定义,如下:
b2Vec2 vertices[vertexCount];
问题解决:把其改成指针形式,如下:
b2Vec2 *vertices = new b2Vec2[vertexCount];
搞定。
17.添加box2d
将libs/box2d目录拷贝到工程中;
User Header Search Paths "KnowledgeGame/Classes/libs" non
Always search user paths YES
所有与box2d扯上关系的源文件修改为.mm
18.Box2D具体是如何运作的。
创建了world对象,接下来需要往里面加入一些body对象。body对象可以随意移动,可以是怪物或者飞镖什么的,只要是参与碰撞的游戏对象都要为之创建一个相应的body对象。当然,也可以创建一些静态的body对象,用来表示游戏中的台阶或者墙壁等不可以移动的物体。
为了创建一个body对象,首先,创建一个body定义结构,然后是body对象,再指定一个shap,再是fixture定义,然后再创建一个fixture对象。下面详细介绍这个过程:
首先创建一个body定义结构体,用以指定body的初始属性,比如位置或者速度。
然后调用world对象来创建一个body对象。
然后为body对象定义一个shape,用以指定想要仿真的物体的几何形状。
接着创建一个fixture定义,同时设置之前创建好的shape为fixture的一个属性,并且设置其它的属性,比如质量或者摩擦力。
最后,可以使用body对象来创建fixture对象,通过传入一个fixture的定义结构就可以了。
请注意,可以往单个body对象里面添加很多个fixture对象。这个功能在创建特别复杂的对象的时候非常有用。比如自行车,可能要创建2个轮子,车身等等,这些fixture可以用关节连接起来。
只要把所有需要创建的body对象都创建好之后,box2d接下来就会接管工作,并且高效地进行物理仿真—只要你周期性地调用world对象的step函数就可以了。
但是,请注意,box2d仅仅是更新它内部模型对象的位置–如果想让cocos2d里面的sprite的位置也更新,并且和物理仿真中的位置相同的话,那么你也需要周期性地更新精灵的位置。
19.density,friction和restitution参数的意义。
Density 就是单位体积的质量(密度)。因此,一个对象的密度越大,那么它就有更多的质量,当然就会越难以移动.
Friction 就是摩擦力。它的范围是0-1.0, 0意味着没有摩擦,1代表最大摩擦,几乎移不动的摩擦。
Restitution 回复力。它的范围也是0到1.0. 0意味着对象碰撞之后不会反弹,1意味着是完全弹性碰撞,会以同样的速度反弹。
20.Box2d用户手册
http://www.box2d.org/manual.html
21.
发表评论
-
cocos2d-x 开发环境搭建
2013-02-07 16:46 108151.在mac上配置cocos2d-x开发环境,xcode &a ... -
游戏开发
2012-11-29 10:17 22201.概念 hp:hit point 生命值,打击值 ,血条 w ... -
SneakyInput
2012-11-21 09:34 1647虚拟控制手柄 因为iOS设备使用触摸屏来输入,没有传统移动游 ... -
无限滚屏效果,高级视差滚屏
2012-10-31 21:52 1859CCParallaxNode不适用于生 ... -
45度角磁砖地图
2012-10-13 22:42 3116设计45度角地图最重要 ... -
瓷砖地图
2012-10-09 22:44 297690度角或45度角瓷砖地 ... -
粒子效果,调试渲染
2012-09-27 15:19 21451.要更加真实的效果模 ...
相关推荐
在本案例中,我们看到的"box2d物理引擎cocos2d"是Cocos2d集成了Box2D物理引擎的一个实例,它涵盖了Cocos2d的2.x和3.x两个版本。 Box2D引擎的核心概念包括刚体(Bodies)、形状(Shapes)、关节(Joints)和力...
Box2D物理引擎源码及中文文档;Box2d有C++,flash和Java等版本。android可以直接使用java版本的Jbox2d,但因为Jbox2d的图形渲染是使用processing库来实现的,所以,在android中使用Jbox2d的时候,图形渲染的工作就...
这个压缩包包含的资源是关于AS3.0版本的Box2D物理引擎,其中包括了引擎的源代码、教程以及一个示例项目,可以帮助开发者更好地理解和应用Box2D。 首先,Box2D物理引擎是一个强大的工具,它允许开发者创建具有真实...
本文将深入探讨如何在cocos2d-x项目中集成并使用Box2D物理引擎。 1. **cocos2d-x简介** cocos2d-x是用C++编写的,基于cocos2d-x Objective-C版本的跨平台游戏开发框架。它支持iOS、Android、Windows、Mac等多个...
以下是关于Box2D物理引擎的一些关键知识点: 1. **基本概念**: - **刚体(Rigid Bodies)**:Box2D中的基本实体,可以是静态或动态。静态刚体不会受到力的影响,而动态刚体则会受力影响并改变运动状态。 - **...
Box2D是一款强大的开源2D物理引擎,广泛应用于游戏开发,为游戏中的物体运动提供真实感的物理模拟。本章将深入探讨Box2D在游戏开发中的应用和核心概念。 首先,Box2D的设计目标是简化2D物理模拟,使开发者能够轻松...
本项目提供的“拆方块游戏”源代码是基于Box2D物理引擎实现的一个实例,旨在帮助开发者理解如何运用Box2D来创建具有真实感的物理效果的游戏。 Box2D引擎的核心在于模拟现实世界的物理现象,如重力、碰撞检测、摩擦...
总的来说,Box2D物理引擎提供了强大的2D物理模拟功能,通过深入理解和熟练运用上述知识点,开发者能够创造出更加逼真、动态的游戏体验。在实际项目中,不断实践和学习,掌握Box2D的高级特性和技巧,将有助于提升游戏...
【瘸腿蛤蟆笔记42-cocos2d-x-3.2 Box2d物理引擎Motor Joint代码】 在游戏开发中,物理引擎是一个重要的组成部分,它能够帮助开发者模拟真实世界中的物理现象,使得游戏中的物体运动更加逼真。Cocos2d-x是一个广泛...
基于ros2/rviz的box2d物理引擎机器人仿真程序,可以rviz中查看仿真机器人的运动
在本文中,我们将深入探讨Cocos2d-x游戏开发框架中的Box2D物理引擎,以及如何在其中实现作用力。Box2D是一个流行的2D物理模拟库,它为游戏开发者提供了构建真实感物理世界的能力。Cocos2d-x 3.2版本集成了Box2D,...
在本篇中,我们将深入探讨使用Cocos2d-x 3.2框架集成Box2D物理引擎实现自由落体效果的编程技术。Cocos2d-x是一个广泛使用的开源游戏开发框架,它支持多种平台,包括iOS、Android以及桌面系统。Box2D是一个强大的2D...
《瘸腿蛤蟆笔记38-cocos2d-x-3.2 Box2d物理引擎Ray-cast代码》 本文将深入探讨Cocos2d-x 3.2版本中Box2D物理引擎的Ray-cast(射线检测)功能。Box2D是一款广泛应用于2D游戏开发的开源物理引擎,它提供了强大的物理...
Box2D是一个流行的开源2D物理引擎,常用于游戏开发和其他需要实时物理模拟的应用中。在本主题中,我们将探讨如何将Box2D集成到Qt应用程序中,为你的项目添加真实的物理效果。Qt是一个功能强大的C++框架,用于创建跨...
Box2D是一个强大的开源2D物理引擎,专为游戏开发者设计,使得在游戏场景中创建真实的物理效果变得简单易行。这个中文手册是为那些希望深入理解和应用Box2D的程序员准备的,提供了详尽的指导和实例,帮助开发者更好地...
Cocos2d-x教程视频Cocos2d-x功能扩展-Cocos2d-x Box2d物理引擎实战开发提取方式是百度网盘分享地址
Box2D是一款强大的开源2D物理引擎,由Ernesto Posse和Eric Haines共同创建,最初是为了他们的项目“Physics for Game Developers”而设计。它使用C++编写,旨在为游戏开发者提供简单、高效的2D物理模拟解决方案。Box...
Box2D是一个开源的2D物理引擎,广泛用于游戏开发、模拟和教育领域。它提供了精确的物理模拟,包括刚体动力学、碰撞检测、关节连接等,使得开发者能够创建出具有真实感的动态场景。Box2DFlashAS3+2.1a是Box2D的一个...
一个应用BOX 2D 物理引擎和EGE 图形库做的刚体运动动画程序。提供完整的源程序以及中文注释,供BOX 2D 物理引擎初学者学习。 利用免费、简单的EGE 图形库和免费的 微软 VC 编译器,可以生成Windows 下的 模拟刚体...
最近将box2D(www.box2D.org)物理引擎的全部代码翻译成了Delphi版本,工作量很大,由于Delphi 2005以前不支持运算符重载,所以所有计算部分都写了两个版本。理论上支持Delphi所有版本,兼容性仅在Delphi7上测试。另外...