头文件
#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是一个二维矢量,它的两个属性为浮点数x和y,表示在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 body。world对象并不保留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
分组索引
当A和B的分组索引相同且为正数,则产生碰撞。
当A和B的分组索引相同且为负数,则不产生碰撞。
在其他情况下都适用正常的类别/遮罩过滤原则。
简单概括就是,如果是大雨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);
}
}
}