BOX2D物理引擎

头文件

#include "Box2D/Box2D.h"//引入使用Box2D引擎需要头文件

#include "extensions/cocos-ext.h"

USING_NS_CC_EXT;




#ifndef Box2DTest_hpp

#define Box2DTest_hpp


#include <stdio.h>

#include <iostream>

using namespace std;

#include "cocos2d.h"

using namespace cocos2d;

#include "ui/CocosGUI.h"

using namespace cocos2d::ui;


#include "Box2D/Box2D.h"//引入使用Box2D引擎需要头文件

#include "extensions/cocos-ext.h"

USING_NS_CC_EXT;


#include "MyContactListener.hpp"

class Box2DTest:public Layer{

public:

    static Scene* createScene();

    bool init()override;

    CREATE_FUNC(Box2DTest);

    void onEnter()override;

    void onExit()override;

    virtual bool onTouchBegan(Touch *touch, Event *unused_event) override;

    virtual void onTouchMoved(Touch *touch, Event *unused_event) override;

    virtual void onTouchEnded(Touch *touch, Event *unused_event) override;

    virtual void onTouchCancelled(Touch *touch, Event *unused_event) override;

    void update(float dt)override;

    void initPhysics();

    void addPhysicsSprite(Vec2 p);

private:

    Size visiableSize;

    b2World* world;

    MyContactListener* listener;


};



#include "Box2DTest.hpp"

//box2d 使用MKS -千克- 单位制,因为文档称他的浮点运算在这样的单位下表现最佳

//一般情况下,我们希望将屏幕比如 480*320 映射为10米见方左右,那么习惯上设置的 像素- 比率为 32,即 #define PTM_RATIO 32

#define PTM_RATIO 32

Scene* Box2DTest::createScene()

{

    auto scene = Scene::create();

    auto layer = Box2DTest::create();

    scene->addChild(layer);

    return scene;

}

bool Box2DTest::init(){

    if (!Layer::init()) {

        return false;

    }

    visiableSize = Director::getInstance()->getVisibleSize();

    //调用initPhysics()函数初始化物理引擎

    this->initPhysics();

    return true;

}

void Box2DTest::initPhysics()

{

//1、创建世界

//定义重力矢量 声明重力变量,b2Vec2是一个二维矢量,它的两个属性为浮点数xy,表示在x轴和y轴方向的矢量。

    b2Vec2 gravity;

//竖直向下 其中(0.0f, -10.0f)表示只有重力作用物体,-10.0f表示沿着y轴向下。

    gravity.Set(0.f, -10.f);

//创建一个物理世界

    world = new b2World(gravity);

//允许睡眠 world->SetAllowSleeping(true)是允许物体睡眠与否,如果允许休眠,可以提高物理世界中物体的处理效率,只有在发生碰撞时才唤醒该对象。

    world->SetAllowSleeping(true);

//允许碰撞

   /* 是开启连续物理测试[ 开启连续物理测试,这是因为计算机只能把一段连续的时间分成许多离散的时间点,再对每个时间点之间的行为进行演算,如果时间点的分割不够细致,速度较快的两个物体碰撞时就可能会产生穿透现象,开启连续物理将启用特殊的算法来避免该现象。]*/

    world->SetContinuousPhysics(true);

    

//2、创建地面盒

    /*

     body用以下步骤来创建:

     1. 用位置(position), 阻尼(damping)等来定义body

     2. world对象来创建body

     3. 用形状(shape), 摩擦(friction), 密度(density)等来定义fixture

     4. body上来创建fixture

     */

    //第一步,创建ground body。为此我们需要一个body定义。在定义中,我们指定ground body的初始位置

    b2BodyDef groundBodyDef;//创建地面定义

    groundBodyDef.position.Set(0.0f, 0.0f);//原点 是设置形状的位置。

    

    //第二步,将body定义传给world对象,用以创建ground bodyworld对象并不保留body定义的引用。body默认是静态的。静态物体和其它静态物体之间并没有碰撞,它们是固定的。

    

    //行代码b2Body* groundBody = world->CreateBody(&groundBodyDef)是通过形状定义变量groundBodyDef创建地面物体。

    b2Body* groundBody = world->CreateBody(&groundBodyDef);//刚体

    

    //第三步,创建地面多边形。我们用简便函数SetAsBox使得地面多边形构成一个盒子形状,盒子的中心点就是父body的原点。

    //依次定义盒子的边界

    //foot  盒子的位置点

    //定义一个有边的形状

    b2EdgeShape groundBox;

    groundBox.Set(b2Vec2(0, 0), b2Vec2(visiableSize.width/PTM_RATIO, 0));//设置地平线  (左下角。右下角)

    //第四步,我们创建shape fixture,以完成ground body

    //使用夹具固定形状到物体上

    groundBody->CreateFixture(&groundBox, 0);

    

     //top

    groundBox.Set(b2Vec2(0,visiableSize.height/PTM_RATIO), b2Vec2(visiableSize.width/PTM_RATIO, visiableSize.height/PTM_RATIO));

    groundBody->CreateFixture(&groundBox, 0);

    

    //left

    groundBox.Set(b2Vec2(0,0), b2Vec2(0,visiableSize.height/PTM_RATIO));

    groundBody->CreateFixture(&groundBox,0);

    

    //right

    groundBox.Set(b2Vec2(visiableSize.width/PTM_RATIO,0), b2Vec2(visiableSize.width/PTM_RATIO,visiableSize.height/PTM_RATIO));

    groundBody->CreateFixture(&groundBox,0);

    

    //创建一个监听   碰撞检测

    listener=new MyContactListener(world,this);

    world->SetContactListener(listener);

    

    this->addPhysicsSprite(Vec2(100, 100));

    

}

void Box2DTest::addPhysicsSprite(Vec2 p)

{

    //创建物理引擎精灵对象

    //CCRANDOM_0_1() 生成随机数的范围是 0 <= (float)x < 1


    int idx = (CCRANDOM_0_1()>0.5?1:0);

    int idy = (CCRANDOM_0_1()>0.5?1:0); //1 0

    auto sprite = Sprite::create("blocks.png",Rect(32*idx, 32*idy, 32,32));

    sprite->setPosition(Vec2(p.x, p.y));

    this->addChild(sprite);

    

    //物体定义

    b2BodyDef bodyDef;

    bodyDef.type = b2_dynamicBody;//设置物体类型为动态物体,物体分为静态和动态物体

    bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);//设置物体的位置,它的单位是米。

    

    //创建物体

    b2Body * body = world->CreateBody(&bodyDef);

    body->SetUserData(sprite);//设置刚体的纹理

    

    

    // 定义ÇÇ的盒子形状

    //b2PolygonShape dynamicBox是声明多边形形状定义变量。

    b2PolygonShape dynamicBox;

    //设置多边形为矩形盒子形状,由于坐标原点在盒子的左下角,SetAsBox是设置盒子的中心为(0.5, 0.5),那么这个盒子的就是1米见方的大小。

    dynamicBox.SetAsBox(0.5, 0.5);

    

    // 夹具定义 (摩擦系数、质量、等)

    b2FixtureDef fixtureDef;

    

    //设置夹具的形状

    fixtureDef.shape = &dynamicBox;

    fixtureDef.density = 1.f;//设置密度

    fixtureDef.friction = 1.0f;//设置摩擦

    fixtureDef.restitution = 1.0f;//设置弹性


    

    //碰撞过滤

//    fixtureDef.filter.categoryBits = 1;

//    fixtureDef.filter.maskBits = 1;

//    fixtureDef.filter.groupIndex = 1;

    /*

     是否碰撞:

      iscollod = A.maskBits & B.categoryBits != 0  &&  A.categoryBits & B.maskBits != 0

     分组索引

     AB的分组索引相同且为正数,则产生碰撞。

     AB的分组索引相同且为负数,则不产生碰撞。

     在其他情况下都适用正常的类别/遮罩过滤原则。

     简单概括就是,如果是大雨0的相同组,则一定碰撞,如果是小于0的相同组则一定不碰撞。

     */

    

//    b2Filter

    

    

    //使用夹具固定形状到物体上

    body->CreateFixture(&fixtureDef);

    

    

}

void Box2DTest::onEnter()

{

    Layer::onEnter();

    auto  touchListener = EventListenerTouchOneByOne::create();

    touchListener->onTouchBegan = CC_CALLBACK_2(Box2DTest::onTouchBegan,this);

    touchListener->onTouchMoved = CC_CALLBACK_2(Box2DTest::onTouchMoved,this);

    touchListener->onTouchEnded = CC_CALLBACK_2(Box2DTest::onTouchEnded, this);

    touchListener->onTouchCancelled = CC_CALLBACK_2(Box2DTest::onTouchCancelled,this);

    touchListener->setSwallowTouches(true);

    Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(touchListener, this);

    

    this->scheduleUpdate();

    //schedule(CC_CALLBACK_1(Box2DTest::update,this), 0.01,"update");

}

void Box2DTest::onExit()

{

    Director::getInstance()->getEventDispatcher()->removeEventListenersForTarget(this);

}

bool Box2DTest::onTouchBegan(Touch *touch, Event *unused_event)

{

    return true;

}

void Box2DTest::onTouchMoved(Touch *touch, Event *unused_event)

{

    

}

void Box2DTest::onTouchEnded(Touch *touch, Event *unused_event)

{

    auto point = touch->getLocation();

    this->addPhysicsSprite(point);

}

void Box2DTest::onTouchCancelled(Touch *touch, Event *unused_event)

{

    

}

//更新的作用

void Box2DTest::update(float dt)

{

    float timeStep = 1.0/60.0f;//更新时间(物理世界刷新次数)

    int32 velocityIterations = 8;//速度迭代次数

    int32 positionIterations = 3;//位置迭代次数

    //刷新

    world->Step(timeStep, velocityIterations, positionIterations);

    for (b2Body *b = world->GetBodyList(); b; b = b->GetNext())

    {

        if (b->GetUserData()!=nullptr)

        {

            auto sprite = (Sprite*)b->GetUserData();

            {

            if (sprite->getTag()==4)

            {

                sprite->removeFromParent();

                world->DestroyBody(b);

                continue;

            }

            }

            sprite->setPosition( Vec2( b->GetPosition().x *PTM_RATIO, b->GetPosition().y * PTM_RATIO) );

            sprite->setRotation( -1 * CC_RADIANS_TO_DEGREES(b->GetAngle()) );

        }

    }

}




//碰撞检测

#include <iostream>

#include "Box2D/Box2D.h"

#include "cocos-ext.h"

#include "cocos2d.h"

using namespace std;

USING_NS_CC_EXT;

USING_NS_CC;


class MyContactListener:public b2ContactListener

{

public:

    b2World* _world;

    MyContactListener();

    MyContactListener(b2World* w,Layer* c);

    ~MyContactListener();

    

    virtual void BeginContact(b2Contact* contact);//碰撞开始

    virtual void EndContact(b2Contact* contact);//碰撞结束

    virtual void PreSolve(b2Contact* contact, const b2Manifold* oldManifold);//持续接触时响应

    //b2Manifold结构含有一个法向量和最多两个的接触点。向量和接触点都是相对于局部坐标系。为方便接触求解器处理,每个接触点都存储了法向冲量和切向(摩擦)冲量。

    virtual void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse);//持续接触时响应,调用完preSolve后调用

};


#include "MyContactListener.hpp"


MyContactListener::MyContactListener()

{

    

}


MyContactListener::MyContactListener(b2World* w,Layer* c)

{

    _world = w;

}


MyContactListener::~MyContactListener()

{

    

}


void MyContactListener::BeginContact(b2Contact *contact)

{

    log("BegainContact");

}


void MyContactListener::EndContact(b2Contact* contact)

{

    log("EndContact");

}

void MyContactListener::PreSolve(b2Contact* contact, const b2Manifold* oldManifold)

{

    log("PreSolve");

    

    

    /*

         B2_NOT_USED(contact); //避免此次碰撞处理

         B2_NOT_USED(oldManifold);

         contact->SetEnabled(false); //避免此次碰撞处理

     

      //超级玛丽游戏中的单面墙,可以通过判断碰撞的方向来决定是否规避此次碰撞。

     

         b2WorldManifold worldManifold;

         contact->GetWorldManifold(&worldManifold);

         if (worldManifold.normal.y > 0.5f) {

         contact->SetEnabled(false);

         }

     */

    

    

    

}

void MyContactListener::PostSolve(b2Contact* contact, const b2ContactImpulse* impulse)

{

    log("PostSolve");

    //Solve计算完成后调用的函数

    float force = impulse->normalImpulses[0];//受到的力力

    

    if (force>10) {

        

        b2Body* bodyA = contact->GetFixtureA()->GetBody();

        b2Body* bodyB = contact->GetFixtureB()->GetBody();

        auto spriteA = (Sprite*)bodyA->GetUserData();

        auto spriteB = (Sprite*)bodyB->GetUserData();

        

        if (spriteA != nullptr && spriteB != nullptr)

        {

            spriteA->setTag(4);

            spriteB->setTag(4);

            

            

        }


    }

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值