cocos2dx在设计之初就集成了两套物理引擎,它们是box2d和chipmunk。我目前使用的是最新版的cocos2dx 3.2。引擎中默认使用的是chipmunk,如果想要改使用box2d的话,需要修改对应的android工程或者是ios工程的配置文件。

  在2.x版本的cocos中,使用物理引擎的步骤十分繁琐。但在3.x版本中变得非常方便了。我这次的学习目标是制作一个打砖块的小游戏。

  首先,现在的Scene类提供了一个静态工厂方法,用以创造一个集成物理引擎的场景。  

 Scene::initWithPhysics()

  这个方法能让你的场景具备创建物理世界的基本条件,接下来我们设置这个物理世界的重力条件,因为打砖块游戏中不需要重力的影响,所以我们把该场景里的重力设置为0:

PhysicsWorld* world = getPhysicsWorld();
world->setGravity(Vec2(,));

  然后我们要给这个物理世界创造一个边界,便于我们观察效果,我的做法是把物理世界的scene和游戏逻辑的实现分开,新建一个继承自layer的类来写游戏逻辑:

  这是头文件:

 #include "Util.h"

 #ifndef __FristGame__GameLayer__
#define __FristGame__GameLayer__ class GameLayer:public Layer
{
public:
Sprite* ball;
Sprite* paddle;
Sprite* edgeSp;
Sprite* prop;
PhysicsBody* ballBody;
TMXTiledMap* map; CREATE_FUNC(GameLayer);
bool init(); void loadPhysicsBody();//加载物理世界的边界
void loadTileMap();//加载使用tiledmap地图编辑器制作的地图
void loadProp();//加载碰撞特定砖块会掉落的道具
void update1(float dt);//打开一个定时器
void contact();//碰撞事件注册
};
#endif /* defined(__FristGame__GameLayer__) */

  小提示:在3.2版本的物理世界中我们不能使用scheduleupdate()函数,似乎body(刚体)的运动是在update里处理的,一旦我们重写了这个函数,物理世界中的小球就不再运动了。所以我们另外设置一个定时器update1来使用。

  这是cpp文件:

   #include "GameLayer.h"
bool GameLayer::init()
{
loadTileMap();
loadPhysicsBody();
return true;
}
void GameLayer::loadPhysicsBody()
{
auto visibleSize = Director::getInstance()->getVisibleSize();//取得当前屏幕的尺寸size
auto origin = Director::getInstance()->getVisibleOrigin(); edgeSp = Sprite::create();//创建一个精灵
auto boundBody = PhysicsBody::createEdgeBox(visibleSize,PhysicsMaterial(0.0f,1.0f,0.0f),);//edgebox是不受刚体碰撞影响的一种刚体,我们用它来设置物理世界的边界
edgeSp->setPosition(visibleSize.width/, visibleSize.height/);//位置设置在屏幕中央
edgeSp->setPhysicsBody(boundBody);//将精灵容纳的刚体设置为boundbody。注意这里不能确定刚体和精灵是不是父子节点的关系。有兴趣的朋友请自行研究。
addChild(edgeSp);//加入渲染树 ball = Sprite::create("game_ball_a.png");//创建小球的精灵
ball->setPosition(,);//设定位置在屏幕中下部
     //PhysicsMaterial是设置刚体属性的类,三个参数分别对应三个属性:1、density(密度)2、restiution(弹性)3、friction(摩擦力),在这个游戏中我们需要小球无限碰撞,因此摩擦力和密度都设为1,弹力设为1。
ballBody = PhysicsBody::createCircle(ball->getContentSize().width/,PhysicsMaterial(0.0f,1.0f,0.0f));
ballBody->setContactTestBitmask(0xFFFFFFFF);//接触掩码值---------标注1------------(见代码后)
Vect force = Vect(1000.0f,1000.0f);
ballBody->applyImpulse(force);//这个方法不会产生力,但是会让一个速度与body的速度叠加 产生新的速度(通过这个方法我们让小球匀速运动)
ballBody->setVelocity(Vec2(,));//设置小球速度
ball->setPhysicsBody(ballBody);
addChild(ball); Sprite* batSprite = Sprite::create("game_av_d.png");//创建打砖块游戏中的砖块
PhysicsBody* batBody = PhysicsBody::createEdgeBox(batSprite->getContentSize(),PhysicsMaterial(0.0f,1.0f,0.0f));//创建相应的刚体并设置材质 32 batSprite->setPhysicsBody(batBody);
batSprite->setPosition(winSize.width/,);
addChild(batSprite);
batBody->setContactTestBitmask(0xFFFFFFFF);//设置接触掩码值
EventListenerTouchOneByOne* ev1 = EventListenerTouchOneByOne::create();//3.x版本之后对触摸事件做了全盘的修改,这里不作详细描述。这是创建一个单点触摸事件。
ev1->onTouchBegan = [](Touch* touch,Event* ev){return true;};//touchbegin不作任何处理,跳过
ev1->onTouchMoved = [=](Touch* touch,Event* ev){
float x = touch->getDelta().x;
batSprite->setPositionX(batSprite->getPositionX()+x);
};//在touchmove中移动挡板,按照触摸滑动的距离来移动挡板。
_eventDispatcher->addEventListenerWithSceneGraphPriority(ev1, this);//将触摸事件加入监听器
contact();//调用注册碰撞事件的函数
schedule(schedule_selector(GameLayer::update1));//打开定时器 }
void GameLayer::contact()
{
EventListenerPhysicsContact* evContact = EventListenerPhysicsContact::create();//创建一个物理世界的碰撞事件
evContact->onContactBegin = [](PhysicsContact& contact){return true;};
evContact->onContactSeperate = [=](PhysicsContact& contact)//该函数在两个碰撞的刚体分离后调用
{
auto bodyA = (Sprite*)(contact.getShapeA()->getBody()->getNode());//两个碰撞刚体相对应的节点之A
auto bodyB = (Sprite*)(contact.getShapeB()->getBody()->getNode());//两个相碰撞刚体对应节点之B
if(!bodyA||!bodyB)//按理说碰撞发生之后不会发生有一个刚体的节点不存在的情况,但是实际测试时发现bodyA或bodyB有为NULL的情况,因此我们在这里做一个判断排除节点为空的情况
return;
int tagA = bodyA->getTag();
int tagB = bodyB->getTag();
if(tagA == )//如果碰撞双方刚体有一个是砖块,则把这个砖块连同节点一同删掉
{
bodyA->removeFromParentAndCleanup(true);
}
if(tagB == )
{
bodyB->removeFromParentAndCleanup(true);
}
prop->setVisible(true);
};
_eventDispatcher->addEventListenerWithSceneGraphPriority(evContact,this);//注册碰撞事件
}
void GameLayer::loadTileMap()
{
map = TMXTiledMap::create("textmap.tmx");//从tmx创建一个TMXTileMap类
map->setPositionX(getPositionX() + );//设置位置 76 addChild(map);
TMXLayer* layer = map->getLayer("bricks");//从map中取出“bricks”图层
     //这个循环嵌套是为了给每个砖块精灵设置一个刚体
for(int x=;x<;x++)
{
for(int y=;y<;y++)
{
int gid = layer->getTileGIDAt(Vec2(x,y));//为了提高点效率,我们没必要给每个tile加上刚体,在创建地图时我们设定了空白处的gid值为12,因此我们只对非12的tile加上刚体
if(gid != )
{
Sprite* sprite = layer->getTileAt(Vec2(x,y));//从tile的坐标取出对应的精灵
if(!sprite)//防止sprite为NULL
continue;
PhysicsBody* body = PhysicsBody::createEdgeBox(sprite->getContentSize(),PhysicsMaterial(1.0f,1.0f,0.0f));//给精灵设置一个刚体
sprite->setTag();//加入tag,方便碰撞时的判断
body->setContactTestBitmask(0xFFFFFFFF);//设置接触掩码值
sprite->setPhysicsBody(body);
}
}
}
loadProp();
}
void GameLayer::loadProp()
{
TMXObjectGroup* objects = map->getObjectGroup("prop");//从地图中取出对象层prop
CCASSERT(NULL != objects, "'Objects' object group not found");//防止为空
auto spawnPoint = objects->getObject("pop");//从对象层中取出对象pop,在创建地图时设置好的
CCASSERT(!spawnPoint.empty(), "spawnPoint object not found");
int x = spawnPoint["x"].asInt();//spwanPoint应该是一个map型容器,这方面我理解不深,不多描述了。
int y = spawnPoint["y"].asInt();
prop = Sprite::create("game_energy_b.png");//按照从地图中取出的坐标创建一个精灵
prop->setPosition(x,y);
prop->setVisible(false);//一开始设置为不可见,当碰撞发生时设置为可见,并开始向下运动
prop->setZOrder();//防止被地图掩盖
map->addChild(prop);
}
void GameLayer::update1(float dt)
{
     //-----------标注2----------------
float x = ballBody->getVelocity().x;
float y = ballBody->getVelocity().y;
if(x!=&&x!=-)
{
if(x<)
x = -;
else
x = ;
}
if(y!=&&y!=-)
{
if(y<)
y = -;
else
y = ;
}
ballBody->setVelocity(Vec2(x,y)); }

  这里对代码中的标注进行一些解释:

  标注1:关于接触掩码值。在3.0中的事件分发机制都由事件派发器管理,所以物理引擎的碰撞事件也不例外。 下面代码注册碰撞响应事件和回调函数

 auto contactListener = EventListenerPhysicsContact::create();
contactListener->onContactBegin = CC_CALLBACK_1(HelloWorld::onContactBegin, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);

  每一次碰撞检测事件是有EventListenerPhysicsContact来进行监听的。监听到碰撞事件时,会回调响应事件onContactBegin()来进行碰撞事件的处理。_eventDispatcher是事件派发器,由它管理所有的注册事件。 EventListenerPhysicsContact是碰撞检测中的一种,也可以运用EventListenerPhysicsContactWithBodies,EventListenerPhysicsContactWithShapes,EventListenerPhysicsContactWithGroup 来进行碰撞事件的注册,对你你感兴趣的bodys,shape和group事件进行监听。   

  在上面说了这么多的东西,最重要的东西就是下面的,没有下面的东西,碰撞事件根本不起作用,这就是我第一次运用碰撞时遇到的问题。也就是设置物理接触相关的位掩码值,默认的接触事件不会被接受,需要设置一定的掩码值来使接触事件响应。 接触掩码值有三个值,分别是:

  1、CategoryBitmask,默认值为0xFFFFFFFF

  2、ContactTestBitmask,默认值为 0x00000000

  3、CollisionBitmask,默认值为0xFFFFFFFF 这三个掩码值都有对应的set/get方法来设置和获取。

  这三个掩码值由逻辑与来进行操作测试。
  一个body的CategoryBitmask和另一个body的ContactTestBitmask的逻辑与的结果不等于0时,接触事件将被发出,否则不发送。
  一个body的CategoryBitmask和另一个body的CollisionBitmask的逻辑与结果不等于0时,他们将碰撞,否则不碰撞
  默认情况下的body属性会进行物理碰撞,但不会发送碰撞检测的信号,也就不会响应碰撞回调函数,这个可以看下默认情况下的掩码值的逻辑与

  CategoryBitmask = 0xFFFFFFFF;
  ContactTestBitmask = 0x00000000;
  CategoryBitmask & ContactTestBitmask = 0,所以不会发送碰撞信号
 
  CollisionBitmask = 0xFFFFFFFF;
  CategoryBitmask & CollisionBitmask = 0xFFFFFFFF,所以物体会碰撞,但是不会响应碰撞回调函数。

  上面介绍的掩码值是碰撞检测回调中最重要的,没有上面的掩码值,所有的碰撞回调函数都不会发生。 EventListenerPhysicsContact有四个接触回调函数:

  1、onContactBegin,在接触开始时被调用,仅调用一次,通过放回true或者false来决定两个物体是否有碰撞。同时可以使用PhysicsContact::setData()来设置接触操作的用户数据。当返回false时,onContactPreSolve和onContactPostSolve将不会被调用,但是onContactSeperate将被调用一次。

  2、onContactPreSlove ,会在每一次被调用,通过放回true或者false来决定两个物体是否有碰撞,同样可以用ignore()来跳过后续的onContactPreSolve和onContactPostSolve回调函数。(默认返回true)

  3、onContactPostSolve,在两个物体碰撞反应中的每个步骤中被处理调用。可以在里面做一些后续的接触操作。如销毁body

  4、onContactSeperate,在两个物体分开时被调用,在每次接触时只调用一次,和onContactBegin配对使用。 上述中最重要的就是碰撞检测事件的讲解,这是游戏中用到碰撞经常要用到的。

  这里附上一篇博文,详细的讲解了3.x的碰撞机制:http://www.tuicool.com/articles/2eI7Nv

  标注2:据我这两天的实验来看,在mac下和windows下使用物理引擎产生的效果有巨大的差别。很多博客上的代码都是在windows上能够流畅运行,但是在mac上跑就会有很多问题。

  比如,25行的ballBody->applyImpulse(force)。在windows下只要使用applyForce就可以了,但是在mac下使用applyForce函数会让球的运动越来越快,没多久便会因为速度超过帧数飞出我们设置的边界。

  再比如,update1中的代码,这是为了保证能一直保持匀速运动而写的,在windows下完全不需要这些代码,但是在mac下如果没有,小球会在某次碰撞时候损失速度(随机的,有时不会损失),直至停下来。

  这篇博文就到这里,下面附上截图:

  

cocos2dx 3.2中的物理引擎初探(一)的更多相关文章

  1. 实例介绍Cocos2d-x中Box2D物理引擎:HelloBox2D

    我们通过一个实例介绍一下,在Cocos2d-x 3.x中使用Box2D物理引擎的开发过程,熟悉这些API的使用.这个实例运行后的场景如图所示,当场景启动后,玩家可以触摸点击屏幕,每次触摸时候,就会在触 ...

  2. cocos2d-x 3.0 使用最新物理引擎的一个源代码实例

    1.碰撞函数參数由两个变成一个了 2.检測不到碰撞.须要设置那三个參数.同一时候还要设置成动态的. body进行设置. 3.初始入口文件也发生了改变. 附录上我近期调试好的cocos2d-x 3.1 ...

  3. 实例介绍Cocos2d-x中Box2D物理引擎:使用关节

    下面我们将使用Box2D物理引擎技术进行重构.使得关节能够掌握如何在Box2D使用关节约束.HelloWorldScene.cpp中与使用关节的相关代码如下: void HelloWorld::add ...

  4. 实例介绍Cocos2d-x中Box2D物理引擎:碰撞检测

    在Box2D中碰撞事件通过实现b2ContactListener类函数实现,b2ContactListener是Box2D提供的抽象类,它的抽象函数:virtual void BeginContact ...

  5. 实例介绍Cocos2d-x中Box2D物理引擎:碰撞检測

    在Box2D中碰撞事件通过实现b2ContactListener类函数实现,b2ContactListener是Box2D提供的抽象类,它的抽象函数:virtual void BeginContact ...

  6. iOS中的物理引擎

    目前知名的2D物理引擎有 Box2d,和Chipmunk,这些是跨平台的.但苹果本身也封装了一个物理引擎, UIDynamic是从iOS 7开始引入的一种新技术,隶属于UIKit框架.这可以让开发人员 ...

  7. cocos2d-js中Chipmunk物理引擎相关(1)

    近期看些cocos2d-js的东西.用到当中的Chipmunk的一些东西.由于相关的资料也不是非常具体,所以看到一些东西实用就记录下来. 1. chipmunk是cocos2d的一个一个物理引擎.用来 ...

  8. [原][osg][osgEarth]关于在OE中使用物理引擎的调研

    关于物理引擎旋转的一些整理 参考文档 http://blog.wolfire.com/2010/03/Comparing-ODE-and-Bullet 介绍ODE和bullet的利弊 http://s ...

  9. Cocos2d-x 3.0 简捷的物理引擎

    Cocos2d-x 3.0 开发(九)使用Physicals取代Box2D和chipmunk http://www.cocos2d-x.org/docs/manual/framework/native ...

随机推荐

  1. Fishnet(计算几何)

    Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 1642   Accepted: 1051 Description A fis ...

  2. 往github上传demo

    一直在github上寻找demo,但怎么传demo上githun呢? http://www.2cto.com/kf/201504/390397.html 首先在github上 new一个reposit ...

  3. 线性代数(高斯消元):JSOI2008 球形空间产生器sphere

    JSOI2008 球形空间产生器sphere [题目描述] 有一个球形空间产生器能够在n维空间中产生一个坚硬的球体.现在,你被困在了这个n维球体中,你只知道球面上n+1个点的坐标,你需要以最快的速度确 ...

  4. [Audio processing] 常见语音特征 —— LPC

    共振峰产生的原理及其在音质上的体现,共振峰的分布位置是建立在声音产生媒介的共鸣物理结构基础上的(Resonant Physical Structure).   无论是人声还是乐器,它们的声音特性都源自 ...

  5. POJ 3208 Apocalypse Someday

    题意: 将含有连续的三个6的数称为不吉利数,比如666,1666,6662,但是6266吉利.则666为第一个不吉利数,输入整数n,求第n个不吉利数.(n <= 5*10^7) 解法: 如果是给 ...

  6. maven clean 报错

    eclipse在使用maven的tomcat控件编译java程序时,报错 Failed to execute goal org.apache.maven.plugins:maven-clean-plu ...

  7. Java中sleep,wait,yield,join的区别

    sleep() wait() yield() join()用法与区别   1.sleep()方法 在指定时间内让当前正在执行的线程暂停执行,但不会释放“锁标志”.不推荐使用. sleep()使当前线程 ...

  8. 卸载Symantec Endpoint Protection, 无需password的卸载方法

    近期一次偶然的机会, 被人装了个Symantec在电脑上, 搞得各种报警, 验证, 烦死. 然后就自然而然的想卸载掉这个该死的杀毒软件, 没想到这个杀毒软件竟然还是个流氓杀毒软件, 卸载还须要pass ...

  9. UVALIVE 4819 最大流

    题意:有N场比赛,每场比赛需要一定数量的题目数,现在有M个题目,每个题目只能提供给特定的几场比赛,并且一次只能在一场比赛中出现. 问最多可以举办多少场比赛. 思路:因为N = 15 , 所以直接二进制 ...

  10. 在Quick-cocos2dx中使用云风pbc解析Protocol Buffers,支持win、mac、ios、android

    本例主要介绍 如何将 pbc 集成到quick-cocos2dx框架中,让我们的cocos2dx客户端Lua拥有编解码Protocol Buffers能力. 参考: 云风pbc的用法: http:// ...