1. 程式人生 > >Bullet(Cocos2dx)之封裝PhysicsWorld3D

Bullet(Cocos2dx)之封裝PhysicsWorld3D

Bullet3之封裝PhysicsWorld3D

根據bullet3 HelloWorld程式去封裝一個PhysicsWorld3D

首先應該去建立一個物理世界,而對於一個物理世界,預設都有重力,提供一個建立

世界的靜態方法(重力預設為(0, -10, 0)

static PhysicsWorld3D* create(const btVector3& gravity = btVector3(0, -10, 0));

負責建立世界,同時對世界初始化

這裡建立一個btDiscreteDynamicsWorld

直接複製bullet3 HelloWorld對世界的初始化,並修改

_collisionConfiguration,_dispatcher, _solver, _overlappingPairCache, _drawer均為成員變數,

具體使用參照Bullet的文件

PhysicsWorld3D* PhysicsWorld3D::create(const btVector3& gravity)
{
	auto world = new PhysicsWorld3D;
	if (world && world->initWorld(gravity))
	{
		return world;
	}

	delete world;
	return nullptr;
}


bool PhysicsWorld3D::initWorld(const btVector3& gravity)
{
	_collisionConfiguration = new btDefaultCollisionConfiguration();

	_dispatcher = new	btCollisionDispatcher(_collisionConfiguration);

	_overlappingPairCache = new btDbvtBroadphase();

	_solver = new btSequentialImpulseConstraintSolver;
	_world = new btDiscreteDynamicsWorld(_dispatcher, _overlappingPairCache, _solver, _collisionConfiguration);
	if (_world == nullptr)
	{
		return false;
	}
	
	_world->setGravity(gravity);

	return true;
}

銷燬一個物理世界

void PhysicsWorld3D::destroy()
{
	this->clear();
	delete _collisionConfiguration;
	delete _dispatcher;
	delete _solver;
	delete _overlappingPairCache;
	delete _world;
	delete this;
}

void PhysicsWorld3D::clear()
{
	int i;
	//remove the rigidbodies from the dynamics world and delete them
	for (i = _world->getNumCollisionObjects() - 1; i >= 0; i--)
	{
		btCollisionObject* obj = _world->getCollisionObjectArray()[i];
		btRigidBody* body = btRigidBody::upcast(obj);
	
		if (body && body->getMotionState())
		{
			delete body->getMotionState();
			delete body->getCollisionShape();
		}
		_world->removeCollisionObject(body);
		delete obj;
	}

}

建立一些簡單的body

由於每種body都有自己的材質資訊

btRigidBodyConstructionInfo是構造一個剛體資訊的結構體,

我們只需關心幾個引數,

friction; // 摩擦係數

rollingFriction; // 滾動摩擦係數

restitution; // 恢復係數(彈性係數)

mass;   // 質量

自己去實現一個簡單的材質結構體

struct PhysicsMaterial3D
{
	btScalar friction;
	btScalar rollingFriction;
	btScalar restitution;
	btScalar mass;
		
	PhysicsMaterial3D() :
		friction(0.0f),
		rollingFriction(0.f),
		restitution(0.f),
		mass(0.f)
	{}

	PhysicsMaterial3D(btScalar aMass, btScalar aFriction, btScalar aRestitution, btScalar aRollingFriction) :
		friction(aFriction),
		rollingFriction(aRollingFriction),
		restitution(aRestitution),
		mass(aMass)
	{}
};

並提供一個預設的材質資訊

const PhysicsMaterial3D PHYSICS_MATERIAL3D_DEFAULT(1.f, 0.5f, 0.5f, 0.0f); 

實現構造3個基本物體,如下宣告

btRigidBody* addPlane(const btVector3& normal, const btVector3& position, const PhysicsMaterial3D& material = PHYSICS_MATERIAL3D_DEFAULT);
btRigidBody* addSphere(btScalar radius, const btVector3& position, const PhysicsMaterial3D& material = PHYSICS_MATERIAL3D_DEFAULT);
btRigidBody* addBox(const btVector3& halfSize, const btVector3& position, const PhysicsMaterial3D& material = PHYSICS_MATERIAL3D_DEFAULT);

對於一個無限的平面,需要一個法向量決定Plane的朝向,同時position決定plane的位置,當然還有材質,但是mass必須為0

對於一個球體(Sphere)半徑,位置,材質

對於一個盒子(Box)尺寸, 位置,材質

btRigidBody* PhysicsWorld3D::addPlane(const btVector3& normal, const btVector3& position, const PhysicsMaterial3D& material)
{
CCAssert(material.mass == 0.f, "plane's mass must be 0."); // 特殊處理,保證mass為0
	btCollisionShape* groundShape = new btStaticPlaneShape(normal, 0.f);
	auto body = getBody(groundShape, position, material);

	_world->addRigidBody(body);
	return body;
}

btRigidBody* PhysicsWorld3D::addSphere(btScalar radius, const btVector3& position, const PhysicsMaterial3D& material)
{
	btCollisionShape* colShape = new btSphereShape(radius);
	auto body = getBody(colShape, position, material);

	_world->addRigidBody(body);

	return body;
}

btRigidBody* PhysicsWorld3D::addBox(const btVector3& size, const btVector3& position, const PhysicsMaterial3D& material)
{
	btCollisionShape* colShape = new btBoxShape(size * 0.5f); // halfSize
	auto body = getBody(colShape, position, material);

	_world->addRigidBody(body);

	return body;
}

構造一個剛體包含一些共同的步驟collisionShape, position, material

由於Plane,Sphere,Box collisionShape型別不同,所以單獨實現,

其他的公共步驟可以抽離出來

btRigidBody* getBody(btCollisionShape* colShape, const btVector3& position, const PhysicsMaterial3D& material);

仿照HelloWorld構造body的方法

btRigidBody* PhysicsWorld3D::getBody(btCollisionShape* colShape, const btVector3& position, const PhysicsMaterial3D& material)
{
	/// Create Dynamic Objects
	btTransform startTransform;
	startTransform.setIdentity();

	//rigidbody is dynamic if and only if mass is non zero, otherwise static
	bool isDynamic = (material.mass != 0.f);

	btVector3 localInertia(0,0,0);
	if (isDynamic)
		colShape->calculateLocalInertia(material.mass, localInertia); // 計算物體慣性

	startTransform.setOrigin(position); // 設定物體位置

	//using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects
	btDefaultMotionState* myMotionState = new btDefaultMotionState(startTransform);
	btRigidBody::btRigidBodyConstructionInfo rbInfo(material.mass, myMotionState,colShape,localInertia);
// 使用自定義的材質
	rbInfo.m_restitution = material.restitution;
	rbInfo.m_friction = material.friction;
	rbInfo.m_rollingFriction = material.rollingFriction;
// 建立body
	btRigidBody* body = new btRigidBody(rbInfo);

	return body;
}

不要忘了物理世界的更新

void PhysicsWorld3D::update(float dt)
{
	_world->stepSimulation(dt);
}

完整原始碼