您的位置:首页 > 移动开发 > Cocos引擎

cocos2dx-3.0 中的物理引擎Box2D使用(一)

2014-08-27 00:04 579 查看
~~~~我的生活,我的点点滴滴!!


这只是一个学习笔记,所以看到雷同莫惊讶!

在cocos2dx-3.0中使用Box2D,我们建完工程需要把libBox2D工程引用进来,然后头文件只需要带上#include "Box2D/Box2D.h",但是

为了看Debug效果,我们需要把cocos2dx-3.0样例cpp-tests下的GLES-Render.h与GLES-Render.cpp文件拷贝到我们工程中添加进来,

准备工作做完了,我们先一起来看代码:

#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__

#include "cocos2d.h"
#include "Box2D/Box2D.h"
#include "GLES-Render.h"

#define  PTM_RATIO 32

USING_NS_CC;

class HelloWorld : public cocos2d::Layer
{
public:
    // there's no 'id' in cpp, so we recommend returning the class instance pointer
    static cocos2d::Scene* createScene();

    // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
    virtual bool init();  
    
    // a selector callback
    void menuCloseCallback(cocos2d::Ref* pSender);
    
    // implement the "static create()" method manually
    CREATE_FUNC(HelloWorld);

	void initPhysics();

	b2World *m_world;

	Size screenSize;

	Size visualSize; 
	
	Sprite* role;  

	GLESDebugDraw* mDebugDraw; 

	void setDebug(bool isDebug);  

	virtual void draw(Renderer *renderer, const kmMat4 &transform, bool transformUpdated) override;

	void update(float dt) override;

protected:
	kmMat4 _modelViewMV;
	void onDraw();
	CustomCommand _customCommand;

private:  
	void drawBox(); 
};

#endif // __HELLOWORLD_SCENE_H__


3.x版本后,渲染机制变了,所以必须像上面这样写。

1、创建世界

//定义重力
	b2Vec2 gravity(0,-9.8f);
	//产生世界
	m_world = new b2World(gravity);
	//允许物体休眠
	m_world->SetAllowSleeping(true);
	m_world->SetContinuousPhysics(true);
	//开启Debug Draw模式
	setDebug(true);


一个正常的物理世界就此产生了。

2、生成地面及墙壁

-Box2D生成的物体全是实心的,这里我们想生成一个四方形空心体,代表了墙和地面。

-Box2D采取现实世界的米-千克-秒(MKS)的单位制,能良好的处理0.1米-10米范围物体的模拟,我们定义32像素为1米作为转换,

所以的都要除以32(PTM_RATIO)。

-我们以左下角为锚点。

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

	b2BodyDef groundDef;
	b2Body *groundBody = m_world->CreateBody(&groundDef);

	b2PolygonShape groundBox;
	//开始设置四边了
	//地面
	groundBox.SetAsBox(screenSize.width / PTM_RATIO, 0.5, b2Vec2(1,1), 0);
	groundBody->CreateFixture(&groundBox, 0);
	//天空
	groundBox.SetAsBox(screenSize.width / PTM_RATIO, 0.5, b2Vec2(1, screenSize.height / PTM_RATIO - 1), 0);
	groundBody->CreateFixture(&groundBox, 0);
	//左墙
	groundBox.SetAsBox(0.5, screenSize.height / PTM_RATIO, b2Vec2(1,1), 0);
	groundBody->CreateFixture(&groundBox, 0);
	//右墙
	groundBox.SetAsBox(0.5, screenSize.height / PTM_RATIO, b2Vec2(screenSize.width / PTM_RATIO - 1, 1), 0);
	groundBody->CreateFixture(&groundBox, 0);


生成天空、墙、地面完成。

3、产生精灵

b2BodyDef groundDef;

b2PolygonShape groundBox;

for(int i = 1 ;i <= 2 ; ++ i)
{
	auto sprite = Sprite::create("ball.png");
	sprite->setPosition(screenSize.width * 0.5, screenSize.height * 0.5);

	//动态物体
	groundDef.type = b2_dynamicBody;
	//用来存放精灵对象,就是这个完成精灵与刚体的绑定。
	groundDef.userData = sprite;
	groundDef.position.Set(screenSize.width * 0.5 / PTM_RATIO * (i), screenSize.height * 0.5 / PTM_RATIO);
	b2Body *testBody = m_world->CreateBody(&groundDef);
	groundBox.SetAsBox(1,1);

	b2FixtureDef fixtureDef;
	fixtureDef.shape = &groundBox;
	//物体的密度
	fixtureDef.density = 1.0f;
	//物体的摩擦
	fixtureDef.friction = 0.3f;

	testBody->CreateFixture(&fixtureDef);

	this->addChild(sprite);
}


生成墙与生成精灵盒子展现了两种方式流程来创建物体,正常的流程:

-b2BodyDef 物体属性定义

-b2PolygonShape 定制器的形状定义

-b2FixtureDef 定制器的定义

-最后全绑定到物体body上

4、更新位置

Box2D是在每一个时间步长Step()函数中来刷新的,这和cocos2dx的update()类似,看下Step()定义:

/// Take a time step. This performs collision detection, integration,and constraint solution.
/// @param timeStep the amount of time to simulate, this should not vary. ---步长调用时间
/// @param velocityIterations for the velocity constraint solver.         ---速度迭代的次数
/// @param positionIterations for the position constraint solver.         ---位置的迭代次数
void Step(	float32 timeStep,int32 velocityIterations,int32 positionIterations );


-速度迭代建议8-10次,超过10次影响效率并且看不出来效果。

-位置迭代建议1-8次。

看下update()函数里面的实现:

void HelloWorld::update(float dt)
{
	int velocityIterations = 8;
	int positionIterations = 1;

	m_world->Step(dt, velocityIterations, positionIterations);

	//更新精灵与刚体的位置同步
	for(b2Body *b = m_world->GetBodyList(); b; b=b->GetNext())
	{
		if( b && b->GetUserData() )
		{
			auto sprite = (Sprite*)b->GetUserData();
			sprite->setPosition(Point(b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO));
		}
	}

}


基本上完成上面操作后,精灵与刚体就绑定在一起了,但是!!运行后你完全看不出他们是否绑定成功,因为我们看不到效果图,仔细观察你会发现,物体下落到一定位置后停下来,

那个地方是我们设置的地面的高度,所以我们需要把他的轮廓显示出来,还需要添加下面代码:

void HelloWorld::draw(Renderer *renderer, const kmMat4 &transform, bool transformUpdated)
{
	//
	// IMPORTANT:
	// This is only for debug purposes
	// It is recommend to disable it
	//
	Layer::draw(renderer, transform, transformUpdated);

	kmGLPushMatrix();
	kmGLGetMatrix(KM_GL_MODELVIEW, &_modelViewMV);

	_customCommand.init(_globalZOrder);
	_customCommand.func = CC_CALLBACK_0(HelloWorld::onDraw, this);
	renderer->addCommand(&_customCommand);

	kmGLPopMatrix();
}

void HelloWorld::onDraw()
{
	kmMat4 oldMV;
	kmGLGetMatrix(KM_GL_MODELVIEW, &oldMV);
	kmGLLoadMatrix(&_modelViewMV);
	m_world->DrawDebugData();
	kmGLLoadMatrix(&oldMV);
}


这是3.x以后改变的渲染机制,draw为父类的虚函数,需要重载,CustomCommand为渲染命令,新的渲染机制是把所有的渲染命令全放一块,最后一块渲染。

这个问题纠结了好半天,由于我想直接在cocos2dx使用Box2D而不是使用cocos2dx为我们封装好的接口,但是写完Box2D的代码后,始终看不出到底有没有

写成功,前面几篇文章都是依赖于Box2D中例子testbed提供好的模块,我只需要加Box2D部分的代码,现在突然在cocos2dx中使用,完全不知道怎么融入

其中,看了cocos2dx新的demo后,发现必须使用draw来调用opengl绘制,简单看下效果图:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: