Cocos2dx-3.10学习之PhysicsWorld解析

简介-Cocos2dx中的物理属性:

1.在createScene()的时候,我们可以创建基于物理世界的场景或者不使用物理世界的场景(即普通的场景)。
2.Node和Sprite拥有自己的身体(body)属性。
3.而Cocos2dx3.x已经封装了物理属性,Body(PhysicsBody), Shape(PhysicsShape), Contact碰撞(PhysicsContact), Joint关节(PhysicsJoint), World(PhysicsWorld),更加方便使用。
4.除使用Cocos2d-x物理API外, 你还可以直接使用Chipmunk或Box2D这两个引擎中的其中一个。

也就是说我们可以使用物理引擎(Box2D或Chipmunk)
创建一个游戏工程或者使用Cocos2dx集成的物理世界PhysicsWorld来创建。
在此主要解析后者:

创建一个带物理世界的场景:
1.下面的代码创建了一个带物理世界的创建方法,并传递到子层上。

class HelloWorld : public cocos2d::Layer
{
public:
    static cocos2d::Scene* createScene();
    virtual bool init();
    CREATE_FUNC(HelloWorld);
    //添加物理世界创建方法
    void setPhyWorld(PhysicsWorld* world){
        m_world = world;
    }
private:
    //添加物理世界成员指针变量
    PhysicsWorld* m_world;
};

2.然后在HelloWorld.cpp中createScene方法添加如下代码:

Scene* HelloWorld::createScene()
{
    //创建物理世界场景
    auto scene = Scene::createWithPhysics();
    //对物理世界场景进行需要的设置(略,根据需要添加)
    scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);
    //创建层
    auto layer = HelloWorld::create();
    //设置层的物理世界
    layer->setPhyWorld(scene->getPhysicsWorld);
    scene->addChild(layer);
    //返回场景
    return scene;
}

Scene类有一个新的static工厂方法createWithPhysics()创建一个带物理世界的场景。你可以通过Scene的getPhysicsWorld()方法获取PhysicsWorld实例。

在调试物理引擎时PhysicsWorld的setDebugDrawMask()非常有用。它可以使物理世界中的形状(shape)、关节(joint)、接触(contact)可见。记得在发布你的游戏时关闭该调试功能。

你可以使用setPhyWorld()方法传递PhysicsWorld到ChildLayer,在一个场景中只有一个PhysicsWorld实例。各个层之间共享。

PhysicsWorld 拥有默认的重力设置,Vect(0.0f, -98.0f), 你可以使用 setGravity() 设置你自己的。你可以通setSpeed() 改变物速。

创建受重力影响的精灵:

//创建受重力影响的精灵
auto sprite = Sprite::create("bird1.png");
sprite->setTag(1);
//创建刚体
auto body = PhysicsBody::createCircle(sprite->getContentSize().width / 2);
//刚体相关设置
body->setContactTestBitmask(1);//设置为1,开启其他刚体与此物理刚体产生碰撞,设置它才能触发碰撞检测回调函数
//setCategoryBitmask和setCollisionBitmask默认值都是1

//绑定刚体
sprite->setPhysicsBody(body);
//设置位置
sprite->setPosition(Vec2(visibleSize.width / 2, visibleSize.height / 2));
this->addChild(sprite);

首先创建一个sprite,然后用PhysicsBody::createCircle创建一个圆形的body附加在sprite上。整个过程和之前创建边界的过程是一致的。

您可以创建PhysicsShape并将它们通过addShape()添加到刚体,但要注意,质量mess(由密度和面积计算)和瞬间形状会自动添加到刚体,而在添加到刚体body后你无法改变形状的相对位置和旋转。如果你不需要它了,您可以使用removeShape()删除它。

创建物理边界:
下面的代码将告诉你如何创建在屏幕上一个物理边界:

//创建物理边界
//边界刚体
auto edgeBody = PhysicsBody::createEdgeBox(visibleSize, PHYSICSBODY_MATERIAL_DEFAULT, 3);
//刚体相关设置
edgeBody->setContactTestBitmask(1); //设置为1,开启其他刚体与此物理刚体产生碰撞
//边界节点
auto edgeNode = Node::create();
edgeNode->setPosition(Vec2(visibleSize.width / 2, visibleSize.height / 2));
//边界节点绑定刚体
edgeNode->setPhysicsBody(edgeBody);
//加入到场景
addChild(edgeNode);

PhysicsWorld有很多工厂方法,如createEdgeBox创建一个矩形的边框,所有的参数是:
a.矩形区域,设置作为visibleSize。
b.可选参数,纹理,默认为PHYSICSBODY_MATERIAL_DEFAULT。
c.可选参数,边框大小,默认为1。

然后,我们创建节点,并连接刚刚创建的节点的身体。屏幕为节点的位置设置好的中心,最后加入节点到场景。

在Cocos2d-x3.0节点的addChild方法可以处理物理刚体。它会自动添加节点的身体到场景的PhysicsWorld。

PhysicsBody的工程方法可以根据参数设置刚体的大小,创建相应的PhysicsBody和PhysicsShape。这是一个常见的做法由物理引擎直接创建一个刚体。然而在Cocos2d-x 3.0物理集成简化了这个过程,所以我们并不需要编写大量的代码。

碰撞检测:

//添加碰撞检测
auto contactListener = EventListenerPhysicsContact::create();//创建碰撞监听
contactListener->onContactBegin = CC_CALLBACK_1(HelloWorld::onContactBegin, this);//回调函数
_eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this); //加入事件监听

回调函数:

bool HelloWorld::onContactBegin(const PhysicsContact &contact) {//传入物理碰撞对象
    log("contact");
    return true;
}

每一次碰撞检测事件是由EventListenerPhysicsContact监听。创建一个实例,然后设置其回调函数conContactBegin。 CC_CALLBACK_1是回调指针转换使用C++11的功能。因为onContactBegin回调函数有两个参数,所以我们使用CC_CALLBACK_1转换他们。

_eventDispatcher是基类节点Node的成员,它可以被用来通过一个初始化层。
你还可以使用EventListenerPhysicsContactWithBodies,EventListenerPhysicsContactWithShapes,EventListenerPhysicsContactWithGroup来监听你感兴趣的bodys,shape或group事件。但是,你还需要设置物理接触相关的位掩码值,因为接触事件默认不会被接受,即使你创建相对的EventListener。

接触相对位掩码设置和组设置 和Box2D的一样。
这里有三个值:CategoryBitmask,ContactTestBitmask和CollisionBitmask。您可以使用相应的get/set方法来获取/设置它们。它们是由逻辑和操作测试的。1.当一个身体的CategoryBitmask并与另一主体的ContactTestBitmask其结果不等于零时,接触事件将被发出,相反接触的事件将不被发送。2.当一个的身体的CategoryBitmask并与另一主体的CollisionBitmask其结果不等于零,则它们将碰撞,相反不会
是独立的,在默认情况下,CategoryBitmask值为0xFFFFFFFF,ContactTestBitmask值是00000000,而CollisionBitmask值为0xFFFFFFFF,表示所有的身体会相互碰撞,但默认不发送接触事件。
另一个设置物理接触是组(group),当它的值大于零,在同一组中的对象会相互碰撞时;当它的值小于零则不会相互碰撞时。注意,当一群不等于零,它会忽略碰撞位掩码设置(接触测试设置仍然有效)。

EventListenerPhysicsContact有四个接触回调函数:onContactBegin,onContactPreSolve,onContactPostSolveonContactSeperate

onContactBegin会在接触被调用开始,仅在这个接触调用一次。您可以决定两个形状有无碰撞通过返回true或false。您可以使用PhysicsContact::setData()来设置接触操作的用户数据。注意,当时onContactBegin返回false时,onContactPreSolveonContactPostSolve不会被调用,但是onContactSeperate将被调用一次。

onContactPreSolve会在每一步被调用,你可以使用PhysicsContactPreSolve设置方法来设置接触参数,如恢复原状,摩擦等。你还可以通过返回true或false决定两个形状是否有碰撞,你可以调用PhysicsContactPreSolve::ignore()跳过后续的onContactPreSolve和`onContactPostSolve回调(返回默认为true)。

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

onContactSeperate将在两个形状分开时被调用。它也是在这个接触仅调用一次。它必须和onContactBegin配对使用,你可以在这里销毁你使用PhysicsContact::setData()设置的自己的数据。

触摸监听:

//添加触摸监听
auto listener = EventListenerTouchOneByOne::create();
//是否吞噬触摸事件,设置为是
listener->setSwallowTouches(true); //用来处理触摸事件是否依据显示的顺序关系向后传递。
listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
listener->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);

回调函数实现:

//两个触摸回调函数,代表触摸被添加到现在这个场景(HelloWorld)中来了
bool HelloWorld::onTouchBegan(Touch *t, Event *e) {
    //当开始点击时,给精灵一个向上的速度,开始点击后,精灵就会有一个向上的速度
    sprite->getPhysicsBody()->setVelocity(Vec2(0, 100));
    return true;
}
void HelloWorld::onTouchEnded(Touch *t, Event *e) {
    log("onTouchEnded\n");
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值