Cocos2d-x 3D物理引擎(Bullet/PhysX集成)
【摘要】 1. 引言随着3D游戏在移动平台和PC端的普及,Cocos2d-x作为跨平台游戏引擎,也在不断扩展其3D能力。虽然Cocos2d-x最初专注于2D游戏开发,但从v3.x版本开始引入了3D渲染和物理支持。本文将深入探讨如何在Cocos2d-x中集成主流3D物理引擎Bullet和PhysX,实现真实的3D物理模拟效果。3D物理引擎能够为游戏带来:真实的碰撞检测:支持复杂3D形状的精确碰撞逼真的动...
1. 引言
-
真实的碰撞检测:支持复杂3D形状的精确碰撞 -
逼真的动力学模拟:重力、摩擦力、弹性等物理特性 -
关节约束系统:铰链、球体、滑块等复杂连接方式 -
车辆物理:真实的车辆悬挂和轮胎模拟 -
破坏效果:刚体破碎、变形等视觉效果
2. 技术背景
2.1 Cocos2d-x 3D架构
-
Camera:3D场景相机系统 -
Light:光照系统(方向光、点光源、聚光灯) -
Model:3D模型加载与渲染 -
Sprite3D:3D精灵封装 -
Physics3D:3D物理系统抽象层
2.2 Bullet物理引擎
-
开发公司:Erwin Coumans(AMD) -
特点:开源、跨平台、轻量级、适合移动设备 -
核心算法:GJK算法(碰撞检测)、LCP求解器(约束求解) -
支持特性:刚体动力学、软体物理、车辆物理、角色控制器
2.3 PhysX物理引擎
-
开发公司:NVIDIA -
特点:商业级性能、GPU加速、高级特性丰富 -
核心优势:大规模场景支持、布料模拟、流体模拟 -
许可模式:免费用于大多数商业应用(年收入<$100K)
2.4 Cocos2d-x物理抽象层
Physics3DComponent和Physics3DServer提供统一的物理接口,支持多种底层物理引擎的切换。3. 应用使用场景
3.1 3D平台跳跃游戏
3.2 3D射击游戏
3.3 赛车游戏
3.4 建筑模拟游戏
3.5 VR/AR应用
4. 不同场景下详细代码实现
4.1 环境准备与项目配置
4.1.1 CMake配置(CMakeLists.txt)
# 启用3D模块
set(USE_PHYSICS3D ON)
set(USE_BULLET_PHYSICS ON) # 或者 USE_PHYSX_PHYSICS
# 查找Bullet库
find_package(Bullet REQUIRED)
if(BULLET_FOUND)
include_directories(${BULLET_INCLUDE_DIRS})
target_link_libraries(${APP_NAME} ${BULLET_LIBRARIES})
endif()
# 或者使用PhysX(需要NVIDIA PhysX SDK)
# find_package(PhysX REQUIRED)
# if(PHYSX_FOUND)
# include_directories(${PHYSX_INCLUDE_DIRS})
# target_link_libraries(${APP_NAME} ${PHYSX_LIBRARIES})
# endif()
4.1.2 Android.mk配置
# 启用3D物理
LOCAL_CFLAGS += -DUSE_PHYSICS3D
# 链接Bullet
LOCAL_STATIC_LIBRARIES += bullet_static
# 或者PhysX
# LOCAL_STATIC_LIBRARIES += physx_static
4.1.3 头文件引入
#include "cocos2d.h"
#include "base/CCPhysics3D.h"
#include "math/CCMath.h"
#if defined(USE_BULLET_PHYSICS)
#include "bullet/btBulletDynamicsCommon.h"
#elif defined(USE_PHYSX_PHYSICS)
#include "PxPhysicsAPI.h"
#endif
using namespace cocos2d;
using namespace cocos2d::physics3d;
4.2 Bullet集成实现
4.2.1 Bullet物理世界管理类
// BulletPhysicsManager.h
#ifndef __BULLET_PHYSICS_MANAGER_H__
#define __BULLET_PHYSICS_MANAGER_H__
#include "cocos2d.h"
#include "bullet/btBulletDynamicsCommon.h"
class BulletPhysicsManager {
private:
btBroadphaseInterface* _broadphase;
btDefaultCollisionConfiguration* _collisionConfiguration;
btCollisionDispatcher* _dispatcher;
btSequentialImpulseConstraintSolver* _solver;
btDiscreteDynamicsWorld* _dynamicsWorld;
std::vector<btRigidBody*> _rigidBodies;
std::map<int, btRigidBody*> _bodyMap; // nodeID -> rigidBody
public:
BulletPhysicsManager();
~BulletPhysicsManager();
bool init();
void update(float dt);
// 刚体管理
btRigidBody* createRigidBody(float mass, const btTransform& startTransform, btCollisionShape* shape);
void addRigidBody(btRigidBody* body);
void removeRigidBody(btRigidBody* body);
// 形状创建
btSphereShape* createSphereShape(float radius);
btBoxShape* createBoxShape(const btVector3& halfExtents);
btCapsuleShape* createCapsuleShape(float radius, float height);
// 同步Cocos2d-x节点与刚体
void syncNodeTransform(Node* node, btRigidBody* body);
btDiscreteDynamicsWorld* getWorld() { return _dynamicsWorld; }
};
#endif // __BULLET_PHYSICS_MANAGER_H__
// BulletPhysicsManager.cpp
#include "BulletPhysicsManager.h"
BulletPhysicsManager::BulletPhysicsManager()
: _broadphase(nullptr)
, _collisionConfiguration(nullptr)
, _dispatcher(nullptr)
, _solver(nullptr)
, _dynamicsWorld(nullptr) {
}
BulletPhysicsManager::~BulletPhysicsManager() {
// 清理Bullet资源
for (auto body : _rigidBodies) {
if (body && body->getMotionState()) {
delete body->getMotionState();
}
delete body->getCollisionShape();
delete body;
}
_rigidBodies.clear();
_bodyMap.clear();
if (_dynamicsWorld) delete _dynamicsWorld;
if (_solver) delete _solver;
if (_dispatcher) delete _dispatcher;
if (_collisionConfiguration) delete _collisionConfiguration;
if (_broadphase) delete _broadphase;
}
bool BulletPhysicsManager::init() {
// 碰撞配置
_collisionConfiguration = new btDefaultCollisionConfiguration();
_dispatcher = new btCollisionDispatcher(_collisionConfiguration);
// 宽相位碰撞检测
_broadphase = new btDbvtBroadphase();
// 约束求解器
_solver = new btSequentialImpulseConstraintSolver();
// 动力学世界
_dynamicsWorld = new btDiscreteDynamicsWorld(_dispatcher, _broadphase, _solver, _collisionConfiguration);
_dynamicsWorld->setGravity(btVector3(0, -9.8f, 0));
return true;
}
void BulletPhysicsManager::update(float dt) {
if (_dynamicsWorld) {
_dynamicsWorld->stepSimulation(dt, 10); // 最大10次迭代
// 同步所有刚体的变换到对应的Cocos2d-x节点
for (auto& pair : _bodyMap) {
Node* node = Director::getInstance()->getNode(pair.first);
if (node) {
syncNodeTransform(node, pair.second);
}
}
}
}
btRigidBody* BulletPhysicsManager::createRigidBody(float mass, const btTransform& startTransform, btCollisionShape* shape) {
btVector3 localInertia(0, 0, 0);
if (mass != 0.0f) {
shape->calculateLocalInertia(mass, localInertia);
}
btDefaultMotionState* motionState = new btDefaultMotionState(startTransform);
btRigidBody::btRigidBodyConstructionInfo rbInfo(mass, motionState, shape, localInertia);
btRigidBody* body = new btRigidBody(rbInfo);
_rigidBodies.push_back(body);
return body;
}
void BulletPhysicsManager::addRigidBody(btRigidBody* body) {
if (_dynamicsWorld && body) {
_dynamicsWorld->addRigidBody(body);
}
}
void BulletPhysicsManager::removeRigidBody(btRigidBody* body) {
if (_dynamicsWorld && body) {
_dynamicsWorld->removeRigidBody(body);
auto it = std::find(_rigidBodies.begin(), _rigidBodies.end(), body);
if (it != _rigidBodies.end()) {
_rigidBodies.erase(it);
}
// 从映射中移除
for (auto mapIt = _bodyMap.begin(); mapIt != _bodyMap.end(); ) {
if (mapIt->second == body) {
mapIt = _bodyMap.erase(mapIt);
} else {
++mapIt;
}
}
}
}
btSphereShape* BulletPhysicsManager::createSphereShape(float radius) {
return new btSphereShape(radius);
}
btBoxShape* BulletPhysicsManager::createBoxShape(const btVector3& halfExtents) {
return new btBoxShape(halfExtents);
}
btCapsuleShape* BulletPhysicsManager::createCapsuleShape(float radius, float height) {
return new btCapsuleShape(radius, height);
}
void BulletPhysicsManager::syncNodeTransform(Node* node, btRigidBody* body) {
if (!node || !body) return;
btTransform transform;
body->getMotionState()->getWorldTransform(transform);
btVector3& origin = transform.getOrigin();
btQuaternion rotation = transform.getRotation();
node->setPosition3D(Vec3(origin.x(), origin.y(), origin.z()));
node->setRotationQuat(Quaternion(rotation.x(), rotation.y(), rotation.z(), rotation.w()));
}
4.2.2 3D物理场景示例
// Physics3DScene.h
#ifndef __PHYSICS3D_SCENE_H__
#define __PHYSICS3D_SCENE_H__
#include "cocos2d.h"
#include "BulletPhysicsManager.h"
class Physics3DScene : public cocos2d::Scene {
private:
BulletPhysicsManager* _physicsManager;
cocos2d::Camera* _camera;
cocos2d::Sprite3D* _selectedObject;
public:
static cocos2d::Scene* createScene();
virtual bool init() override;
virtual void update(float dt) override;
void createGround();
void createDynamicObjects();
void createJoints();
void setupCamera();
void setupTouchControls();
bool onTouchBegan(cocos2d::Touch* touch, cocos2d::Event* event);
void onTouchMoved(cocos2d::Touch* touch, cocos2d::Event* event);
void onKeyPressed(cocos2d::EventKeyboard::KeyCode keyCode, cocos2d::Event* event);
CREATE_FUNC(Physics3DScene);
};
#endif // __PHYSICS3D_SCENE_H__
// Physics3DScene.cpp
#include "Physics3DScene.h"
USING_NS_CC;
Scene* Physics3DScene::createScene() {
return Physics3DScene::create();
}
bool Physics3DScene::init() {
if (!Scene::init()) {
return false;
}
// 初始化物理管理器
_physicsManager = new BulletPhysicsManager();
_physicsManager->init();
// 设置摄像机
setupCamera();
// 创建3D场景元素
createGround();
createDynamicObjects();
createJoints();
// 设置输入控制
setupTouchControls();
// 注册更新
scheduleUpdate();
return true;
}
void Physics3DScene::setupCamera() {
_camera = Camera::createPerspective(60, Director::getInstance()->getWinSize().width / Director::getInstance()->getWinSize().height, 0.1f, 1000.0f);
_camera->setPosition3D(Vec3(0, 10, 20));
_camera->lookAt(Vec3(0, 0, 0), Vec3(0, 1, 0));
addChild(_camera);
// 默认摄像机
Camera::setDefaultCamera(_camera);
}
void Physics3DScene::createGround() {
// 创建地面模型
auto ground = Sprite3D::create("ground.obj");
if (!ground) {
// 如果没有模型文件,创建一个平面
ground = Sprite3D::create();
auto mesh = Mesh::createPlane(50.0f, 50.0f);
ground->setMesh(mesh);
ground->setTexture("ground_texture.png");
}
ground->setPosition3D(Vec3(0, -5, 0));
ground->setScale(1.0f);
addChild(ground);
// 创建地面刚体
auto physicsManager = BulletPhysicsManager::getInstance();
btTransform groundTransform;
groundTransform.setIdentity();
groundTransform.setOrigin(btVector3(0, -5, 0));
btBoxShape* groundShape = physicsManager->createBoxShape(btVector3(25.0f, 0.5f, 25.0f));
btRigidBody* groundBody = physicsManager->createRigidBody(0.0f, groundTransform, groundShape); // 质量为0表示静态
physicsManager->addRigidBody(groundBody);
// 关联节点和刚体
groundBody->setUserPointer(reinterpret_cast<void*>(ground->getTag()));
physicsManager->syncNodeTransform(ground, groundBody);
}
void Physics3DScene::createDynamicObjects() {
auto physicsManager = BulletPhysicsManager::getInstance();
// 创建几个动态立方体
for (int i = 0; i < 5; ++i) {
auto cube = Sprite3D::create("cube.obj");
if (!cube) {
cube = Sprite3D::create();
auto mesh = Mesh::createBox(2.0f);
cube->setMesh(mesh);
cube->setTexture("cube_texture.png");
}
float x = (rand() % 10 - 5) * 2.0f;
float z = (rand() % 10 - 5) * 2.0f;
cube->setPosition3D(Vec3(x, 10 + i * 2.0f, z));
addChild(cube);
// 创建刚体
btTransform cubeTransform;
cubeTransform.setIdentity();
cubeTransform.setOrigin(btVector3(x, 10 + i * 2.0f, z));
btBoxShape* cubeShape = physicsManager->createBoxShape(btVector3(1.0f, 1.0f, 1.0f));
btRigidBody* cubeBody = physicsManager->createRigidBody(1.0f, cubeTransform, cubeShape);
// 设置物理属性
cubeBody->setRestitution(0.3f); // 弹性
cubeBody->setFriction(0.6f); // 摩擦力
physicsManager->addRigidBody(cubeBody);
physicsManager->syncNodeTransform(cube, cubeBody);
}
// 创建一个球体
auto sphere = Sprite3D::create("sphere.obj");
if (!sphere) {
sphere = Sprite3D::create();
auto mesh = Mesh::createSphere(1.0f);
sphere->setMesh(mesh);
sphere->setTexture("sphere_texture.png");
}
sphere->setPosition3D(Vec3(0, 15, 0));
addChild(sphere);
btTransform sphereTransform;
sphereTransform.setIdentity();
sphereTransform.setOrigin(btVector3(0, 15, 0));
btSphereShape* sphereShape = physicsManager->createSphereShape(1.0f);
btRigidBody* sphereBody = physicsManager->createRigidBody(1.0f, sphereTransform, sphereShape);
sphereBody->setRestitution(0.8f); // 高弹性
physicsManager->addRigidBody(sphereBody);
physicsManager->syncNodeTransform(sphere, sphereBody);
}
void Physics3DScene::createJoints() {
// 创建两个刚体用于演示铰链关节
auto physicsManager = BulletPhysicsManager::getInstance();
// 第一个刚体(静态锚点)
btTransform anchorTransform;
anchorTransform.setIdentity();
anchorTransform.setOrigin(btVector3(-5, 5, 0));
btBoxShape* anchorShape = physicsManager->createBoxShape(btVector3(0.5f, 0.5f, 0.5f));
btRigidBody* anchorBody = physicsManager->createRigidBody(0.0f, anchorTransform, anchorShape);
physicsManager->addRigidBody(anchorBody);
// 第二个刚体(动态摆锤)
btTransform pendulumTransform;
pendulumTransform.setIdentity();
pendulumTransform.setOrigin(btVector3(-5, 2, 0));
btBoxShape* pendulumShape = physicsManager->createBoxShape(btVector3(0.3f, 2.0f, 0.3f));
btRigidBody* pendulumBody = physicsManager->createRigidBody(1.0f, pendulumTransform, pendulumShape);
physicsManager->addRigidBody(pendulumBody);
// 创建铰链关节
btVector3 pivotInA(-5, 5, 0);
btVector3 pivotInB(-5, 2, 0);
btVector3 axisInA(0, 0, 1); // 绕Z轴旋转
btVector3 axisInB(0, 0, 1);
btHingeConstraint* hinge = new btHingeConstraint(*anchorBody, *pendulumBody, pivotInA, pivotInB, axisInA, axisInB);
hinge->setLimit(-M_PI_4, M_PI_4); // 限制摆动角度 ±45度
physicsManager->getWorld()->addConstraint(hinge);
// 创建可视化对象
auto anchorVis = Sprite3D::create();
auto anchorMesh = Mesh::createBox(1.0f);
anchorVis->setMesh(anchorMesh);
anchorVis->setPosition3D(Vec3(-5, 5, 0));
anchorVis->setColor(Color3B::RED);
addChild(anchorVis);
auto pendulumVis = Sprite3D::create();
auto pendulumMesh = Mesh::createBox(0.6f, 4.0f, 0.6f);
pendulumVis->setMesh(pendulumMesh);
pendulumVis->setPosition3D(Vec3(-5, 2, 0));
pendulumVis->setColor(Color3B::BLUE);
addChild(pendulumVis);
}
void Physics3DScene::setupTouchControls() {
auto listener = EventListenerTouchAllAtOnce::create();
listener->onTouchesBegan = [this](const std::vector<Touch*>& touches, Event* event) {
for (auto& touch : touches) {
this->onTouchBegan(touch, event);
}
};
listener->onTouchesMoved = [this](const std::vector<Touch*>& touches, Event* event) {
for (auto& touch : touches) {
this->onTouchMoved(touch, event);
}
};
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
// 键盘控制
auto keyboardListener = EventListenerKeyboard::create();
keyboardListener->onKeyPressed = CC_CALLBACK_2(Physics3DScene::onKeyPressed, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(keyboardListener, this);
}
bool Physics3DScene::onTouchBegan(Touch* touch, Event* event) {
auto location = touch->getLocation();
auto camera = Camera::getDefaultCamera();
// 射线检测选择物体
Ray ray = camera->screenPointToRay(location.x, location.y);
// 简化的选择逻辑 - 实际应用中应该使用场景相交检测
_selectedObject = nullptr;
return true;
}
void Physics3DScene::onTouchMoved(Touch* touch, Event* event) {
if (_selectedObject && _physicsManager) {
auto location = touch->getLocation();
auto prevLocation = touch->getPreviousLocation();
// 计算移动增量
Vec2 delta = location - prevLocation;
// 应用力到选中的刚体
// 这里简化处理,实际应用需要获取对应的刚体并施力
}
}
void Physics3DScene::onKeyPressed(EventKeyboard::KeyCode keyCode, Event* event) {
switch (keyCode) {
case EventKeyboard::KeyCode::KEY_SPACE:
// 施加爆炸力
if (_physicsManager) {
auto world = _physicsManager->getWorld();
btVector3 explosionPos(0, 5, 0);
float explosionRadius = 10.0f;
float explosionPower = 50.0f;
// 对所有刚体施加爆炸力
for (int i = world->getNumCollisionObjects() - 1; i >= 0; --i) {
btCollisionObject* obj = world->getCollisionObjectArray()[i];
btRigidBody* body = btRigidBody::upcast(obj);
if (body && body->getMass() > 0) { // 只影响动态刚体
btVector3 bodyPos = body->getWorldTransform().getOrigin();
btVector3 direction = bodyPos - explosionPos;
float distance = direction.length();
if (distance < explosionRadius) {
direction.normalize();
float force = explosionPower * (1.0f - distance / explosionRadius);
body->applyImpulse(direction * force, bodyPos);
}
}
}
}
break;
case EventKeyboard::KeyCode::KEY_R:
// 重置场景
Director::getInstance()->replaceScene(Physics3DScene::createScene());
break;
default:
break;
}
}
void Physics3DScene::update(float dt) {
if (_physicsManager) {
_physicsManager->update(dt);
}
// 相机控制
if (Keyboard::isPressed(EventKeyboard::KeyCode::KEY_UP)) {
_camera->setPosition3D(_camera->getPosition3D() + Vec3(0, 0, -1));
}
if (Keyboard::isPressed(EventKeyboard::KeyCode::KEY_DOWN)) {
_camera->setPosition3D(_camera->getPosition3D() + Vec3(0, 0, 1));
}
if (Keyboard::isPressed(EventKeyboard::KeyCode::KEY_LEFT)) {
_camera->setPosition3D(_camera->getPosition3D() + Vec3(-1, 0, 0));
}
if (Keyboard::isPressed(EventKeyboard::KeyCode::KEY_RIGHT)) {
_camera->setPosition3D(_camera->getPosition3D() + Vec3(1, 0, 0));
}
}
4.3 PhysX集成实现
4.3.1 PhysX物理管理器
// PhysXPhysicsManager.h
#ifndef __PHYSX_PHYSICS_MANAGER_H__
#define __PHYSX_PHYSICS_MANAGER_H__
#include "cocos2d.h"
#include "PxPhysicsAPI.h"
using namespace physx;
class PhysXPhysicsManager {
private:
PxFoundation* _foundation;
PxPhysics* _physics;
PxScene* _scene;
PxCooking* _cooking;
PxDefaultCpuDispatcher* _dispatcher;
PxMaterial* _defaultMaterial;
std::vector<PxRigidActor*> _actors;
std::map<int, PxRigidActor*> _actorMap;
public:
PhysXPhysicsManager();
~PhysXPhysicsManager();
bool init();
void update(float dt);
// 刚体管理
PxRigidDynamic* createDynamicActor(const PxTransform& transform, PxShape* shape, float density = 1.0f);
PxRigidStatic* createStaticActor(const PxTransform& transform, PxShape* shape);
void addActor(PxRigidActor* actor);
void removeActor(PxRigidActor* actor);
// 形状创建
PxSphereGeometry createSphereGeometry(float radius);
PxBoxGeometry createBoxGeometry(const PxVec3& halfExtents);
PxCapsuleGeometry createCapsuleGeometry(float radius, float halfHeight);
// 材质
PxMaterial* createMaterial(float staticFriction, float dynamicFriction, float restitution);
// 同步
void syncNodeTransform(Node* node, PxRigidActor* actor);
PxScene* getScene() { return _scene; }
};
#endif // __PHYSX_PHYSICS_MANAGER_H__
// PhysXPhysicsManager.cpp
#include "PhysXPhysicsManager.h"
PhysXPhysicsManager::PhysXPhysicsManager()
: _foundation(nullptr)
, _physics(nullptr)
, _scene(nullptr)
, _cooking(nullptr)
, _dispatcher(nullptr)
, _defaultMaterial(nullptr) {
}
PhysXPhysicsManager::~PhysXPhysicsManager() {
if (_scene) _scene->release();
if (_dispatcher) _dispatcher->release();
if (_cooking) _cooking->release();
if (_physics) _physics->release();
if (_defaultMaterial) _defaultMaterial->release();
if (_foundation) _foundation->release();
}
bool PhysXPhysicsManager::init() {
// 创建Foundation
_foundation = PxCreateFoundation(PX_PHYSICS_VERSION, memAlloc, memFree);
if (!_foundation) return false;
// 创建Physics
_physics = PxCreatePhysics(PX_PHYSICS_VERSION, *_foundation, PxTolerancesScale());
if (!_physics) return false;
// 创建Cooking
_cooking = PxCreateCooking(PX_PHYSICS_VERSION, *_foundation, PxCookingParams(PxTolerancesScale()));
if (!_cooking) return false;
// 创建默认材质
_defaultMaterial = _physics->createMaterial(0.5f, 0.5f, 0.6f);
// 创建场景描述
PxSceneDesc sceneDesc(_physics->getTolerancesScale());
sceneDesc.gravity = PxVec3(0.0f, -9.81f, 0.0f);
// 创建CPU调度器
_dispatcher = PxDefaultCpuDispatcherCreate(1);
sceneDesc.cpuDispatcher = _dispatcher;
// 碰撞检测
sceneDesc.filterShader = PxDefaultSimulationFilterShader;
// 创建场景
_scene = _physics->createScene(sceneDesc);
if (!_scene) return false;
return true;
}
void PhysXPhysicsManager::update(float dt) {
if (_scene) {
_scene->simulate(dt);
_scene->fetchResults(true);
// 同步所有演员的变换
for (auto& pair : _actorMap) {
Node* node = Director::getInstance()->getNode(pair.first);
if (node) {
syncNodeTransform(node, pair.second);
}
}
}
}
PxRigidDynamic* PhysXPhysicsManager::createDynamicActor(const PxTransform& transform, PxShape* shape, float density) {
PxRigidDynamic* actor = PxCreateDynamic(*_physics, transform, *shape, *_defaultMaterial, density);
if (actor) {
_actors.push_back(actor);
}
return actor;
}
PxRigidStatic* PhysXPhysicsManager::createStaticActor(const PxTransform& transform, PxShape* shape) {
PxRigidStatic* actor = PxCreateStatic(*_physics, transform, *shape, *_defaultMaterial);
if (actor) {
_actors.push_back(actor);
}
return actor;
}
void PhysXPhysicsManager::addActor(PxRigidActor* actor) {
if (_scene && actor) {
_scene->addActor(*actor);
}
}
void PhysXPhysicsManager::removeActor(PxRigidActor* actor) {
if (_scene && actor) {
_scene->removeActor(*actor);
auto it = std::find(_actors.begin(), _actors.end(), actor);
if (it != _actors.end()) {
_actors.erase(it);
}
for (auto mapIt = _actorMap.begin(); mapIt != _actorMap.end(); ) {
if (mapIt->second == actor) {
mapIt = _actorMap.erase(mapIt);
} else {
++mapIt;
}
}
actor->release();
}
}
PxMaterial* PhysXPhysicsManager::createMaterial(float staticFriction, float dynamicFriction, float restitution) {
return _physics->createMaterial(staticFriction, dynamicFriction, restitution);
}
5. 原理解释
5.1 物理引擎核心架构
-
Bullet: btDiscreteDynamicsWorld -
PhysX: PxScene
-
动态刚体:受力和碰撞影响 -
静态刚体:不受力影响,用于场景几何 -
运动学刚体:通过动画控制,可推开动态刚体
-
原始形状:球体、立方体、胶囊体 -
复合形状:多个原始形状的组合 -
网格形状:复杂网格的精确碰撞
-
铰链关节:绕轴旋转 -
球体关节:万向节运动 -
滑块关节:沿轴平移
5.2 碰撞检测流程
-
宽相位(Broad Phase):快速筛选可能碰撞的物体对 -
窄相位(Narrow Phase):精确检测碰撞并计算接触点 -
约束求解(Constraint Solver):计算碰撞响应力 -
积分(Integration):更新物体位置和速度
5.3 力与运动
F = ma-
力改变物体的加速度 -
积分得到速度和位置 -
阻尼模拟能量损失
6. 核心特性
6.1 跨平台支持
-
统一的C++接口 -
自动适配不同平台的底层实现 -
移动端优化(减少计算量)
6.2 高性能模拟
-
多线程并行计算 -
SIMD指令优化 -
GPU加速(PhysX)
6.3 丰富的物理特性
-
刚体动力学 -
软体物理(Bullet) -
布料模拟(PhysX) -
流体模拟(PhysX)
6.4 易用的集成接口
-
与Cocos2d-x节点系统无缝集成 -
自动坐标同步 -
简化的资源管理
7. 原理流程图
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Cocos2d-x场景 │───▶│ 创建物理世界 │───▶│ 添加刚体与形状 │
│ (Node/Sprite3D) │ │ (btWorld/PxScene)│ └─────────────────┘
└─────────────────┘ └──────────────────┘ │
▼
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ 应用外力/约束 │───▶│ 物理模拟步进 │───▶│ 更新变换矩阵 │
│ (Force/Joint) │ │ (StepSimulation) │ │ (Sync Transform)│
└─────────────────┘ └──────────────────┘ └─────────────────┘
│
▼
┌─────────────────┐
│ 渲染最终帧 │
│ (Draw with new │
│ positions) │
└─────────────────┘
8. 环境准备
8.1 开发环境要求
-
Cocos2d-x v3.17+ 或 v4.x -
C++11 或更高版本 -
CMake 3.10+ -
Bullet Physics SDK 2.87+ 或 NVIDIA PhysX SDK 4.1+
8.2 依赖库配置
# Ubuntu
sudo apt-get install libbullet-dev
# Windows - 下载Bullet源码并编译
git clone https://github.com/bulletphysics/bullet3.git
-
从NVIDIA官网下载PhysX SDK -
或使用包管理器(如vcpkg):
vcpkg install physx
8.3 项目结构
Classes/
├── Physics3D/
│ ├── BulletPhysicsManager.h/.cpp
│ ├── PhysXPhysicsManager.h/.cpp
│ └── Physics3DScene.h/.cpp
Resources/
├── Models/
│ ├── ground.obj
│ ├── cube.obj
│ └── sphere.obj
├── Textures/
│ ├── ground_texture.png
│ ├── cube_texture.png
│ └── sphere_texture.png
└── Shaders/
└── standard.vert/frag
9. 实际详细应用代码示例实现
9.1 AppDelegate集成
// AppDelegate.cpp
#include "AppDelegate.h"
#include "Physics3DScene.h"
#include "BulletPhysicsManager.h"
USING_NS_CC;
AppDelegate::AppDelegate() {
}
AppDelegate::~AppDelegate() {
}
bool AppDelegate::applicationDidFinishLaunching() {
// 初始化导演
auto director = Director::getInstance();
auto glview = director->getOpenGLView();
if (!glview) {
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)
glview = GLViewImpl::createWithRect("Cocos2d-x 3D Physics", Rect(0, 0, 960, 640));
#else
glview = GLViewImpl::create("Cocos2d-x 3D Physics");
#endif
director->setOpenGLView(glview);
}
// 设置设计分辨率
glview->setDesignResolutionSize(960, 640, ResolutionPolicy::SHOW_ALL);
// 启用3D物理
director->setDisplayStats(true);
director->setAnimationInterval(1.0 / 60);
// 创建场景
auto scene = Physics3DScene::createScene();
director->runWithScene(scene);
return true;
}
void AppDelegate::applicationDidEnterBackground() {
Director::getInstance()->stopAnimation();
}
void AppDelegate::applicationWillEnterForeground() {
Director::getInstance()->startAnimation();
}
9.2 车辆物理示例扩展
// VehiclePhysics.h
#ifndef __VEHICLE_PHYSICS_H__
#define __VEHICLE_PHYSICS_H__
#include "BulletPhysicsManager.h"
class VehiclePhysics {
private:
BulletPhysicsManager* _physicsManager;
btRigidBody* _chassisBody;
btVehicleRaycaster* _vehicleRayCaster;
btRaycastVehicle* _vehicle;
public:
VehiclePhysics(BulletPhysicsManager* physicsManager);
~VehiclePhysics();
bool createVehicle(const btVector3& chassisPosition);
void updateVehicle(float dt);
void applyEngineForce(float force);
void setSteeringValue(float steering);
btRigidBody* getChassisBody() { return _chassisBody; }
};
#endif // __VEHICLE_PHYSICS_H__
// VehiclePhysics.cpp
#include "VehiclePhysics.h"
VehiclePhysics::VehiclePhysics(BulletPhysicsManager* physicsManager)
: _physicsManager(physicsManager)
, _chassisBody(nullptr)
, _vehicleRayCaster(nullptr)
, _vehicle(nullptr) {
}
VehiclePhysics::~VehiclePhysics() {
if (_vehicle) {
delete _vehicle;
}
if (_vehicleRayCaster) {
delete _vehicleRayCaster;
}
}
bool VehiclePhysics::createVehicle(const btVector3& chassisPosition) {
if (!_physicsManager) return false;
auto world = _physicsManager->getWorld();
// 创建底盘
btTransform chassisTransform;
chassisTransform.setIdentity();
chassisTransform.setOrigin(chassisPosition);
btBoxShape* chassisShape = _physicsManager->createBoxShape(btVector3(2.0f, 0.5f, 4.0f));
_chassisBody = _physicsManager->createRigidBody(800.0f, chassisTransform, chassisShape);
_chassisBody->setActivationState(DISABLE_DEACTIVATION);
_physicsManager->addRigidBody(_chassisBody);
// 创建车辆射线检测器
_vehicleRayCaster = new btDefaultVehicleRaycaster(world);
// 创建车辆
btRaycastVehicle::btVehicleTuning tuning;
tuning.m_suspensionStiffness = 20.0f;
tuning.m_suspensionCompression = 4.4f;
tuning.m_suspensionDamping = 2.3f;
tuning.m_maxSuspensionTravelCm = 500.0f;
tuning.m_frictionSlip = 10.5f;
tuning.m_maxSuspensionForce = 6000.0f;
_vehicle = new btRaycastVehicle(tuning, _chassisBody, _vehicleRayCaster);
world->addVehicle(_vehicle);
// 设置车轮
float connectionHeight = 1.2f;
float wheelRadius = 0.5f;
float wheelWidth = 0.4f;
float suspensionRestLength = 0.6f;
// 车轮位置(相对于底盘中心)
btVector3 wheelDirectionCS0(0, -1, 0);
btVector3 wheelAxleCS(-1, 0, 0);
// 添加四个车轮
btVector3 connectionPointCS0;
// 前左
connectionPointCS0 = btVector3(2.0f, connectionHeight, 2.5f);
_vehicle->addWheel(connectionPointCS0, wheelDirectionCS0, wheelAxleCS, suspensionRestLength, wheelRadius, tuning, true);
// 前右
connectionPointCS0 = btVector3(-2.0f, connectionHeight, 2.5f);
_vehicle->addWheel(connectionPointCS0, wheelDirectionCS0, wheelAxleCS, suspensionRestLength, wheelRadius, tuning, true);
// 后左
connectionPointCS0 = btVector3(2.0f, connectionHeight, -2.5f);
_vehicle->addWheel(connectionPointCS0, wheelDirectionCS0, wheelAxleCS, suspensionRestLength, wheelRadius, tuning, false);
// 后右
connectionPointCS0 = btVector3(-2.0f, connectionHeight, -2.5f);
_vehicle->addWheel(connectionPointCS0, wheelDirectionCS0, wheelAxleCS, suspensionRestLength, wheelRadius, tuning, false);
// 配置所有车轮
for (int i = 0; i < _vehicle->getNumWheels(); i++) {
btWheelInfo& wheel = _vehicle->getWheelInfo(i);
wheel.m_suspensionStiffness = tuning.m_suspensionStiffness;
wheel.m_wheelsDampingRelaxation = tuning.m_suspensionDamping;
wheel.m_wheelsDampingCompression = tuning.m_suspensionCompression;
wheel.m_frictionSlip = tuning.m_frictionSlip;
wheel.m_rollInfluence = 0.1f;
}
return true;
}
void VehiclePhysics::updateVehicle(float dt) {
if (_vehicle) {
_vehicle->updateVehicle(dt);
}
}
void VehiclePhysics::applyEngineForce(float force) {
if (_vehicle) {
for (int i = 0; i < _vehicle->getNumWheels(); i++) {
_vehicle->applyEngineForce(force, i);
}
}
}
void VehiclePhysics::setSteeringValue(float steering) {
if (_vehicle) {
// 只影响前轮
_vehicle->setSteeringValue(steering, 0);
_vehicle->setSteeringValue(steering, 1);
}
}
10. 运行结果
10.1 预期视觉效果
-
重力模拟:立方体和球体受重力影响下落,与地面碰撞反弹 -
关节运动:蓝色摆锤绕红色锚点做受限摆动 -
爆炸效果:按空格键产生爆炸力,推开周围物体 -
车辆物理:完整的车辆悬挂和轮胎物理(如果实现车辆示例)
10.2 性能指标
-
帧率:在中等复杂度场景下维持60FPS(桌面端) -
内存占用:Bullet约10-50MB,PhysX约20-100MB(取决于场景复杂度) -
CPU使用:物理模拟占单核CPU的10-30%
11. 测试步骤及详细代码
11.1 单元测试
// PhysicsTest.cpp
#include "CppUnitTest.h"
#include "BulletPhysicsManager.h"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace Physics3DTest {
TEST_CLASS(Physics3DTest) {
public:
TEST_METHOD(TestBulletPhysicsInitialization) {
auto physicsManager = new BulletPhysicsManager();
Assert::IsTrue(physicsManager->init());
delete physicsManager;
}
TEST_METHOD(TestCreateRigidBody) {
auto physicsManager = new BulletPhysicsManager();
physicsManager->init();
btTransform transform;
transform.setIdentity();
transform.setOrigin(btVector3(0, 0, 0));
auto shape = physicsManager->createSphereShape(1.0f);
auto body = physicsManager->createRigidBody(1.0f, transform, shape);
Assert::IsNotNull(body);
Assert::AreEqual(1.0f, body->getMass());
delete physicsManager;
}
};
}
11.2 手动测试步骤
-
基础功能测试: -
启动应用,观察物体自然下落 -
验证碰撞检测和反弹效果 -
测试关节限制是否正常工作
-
-
交互测试: -
使用空格键触发爆炸效果 -
验证相机控制(方向键移动) -
测试R键重置场景
-
-
性能测试: -
添加更多物体观察帧率变化 -
在不同设备上测试性能表现 -
监控内存使用情况
-
-
边界条件测试: -
测试大量物体同时碰撞 -
验证极端角度下的关节行为 -
测试内存不足情况下的稳定性
-
12. 部署场景
12.1 移动端部署
-
在 build.gradle中确保链接Bullet/PhysX库 -
设置合适的ABI过滤(armeabi-v7a, arm64-v8a) -
优化物理计算频率以适应移动CPU
-
在Xcode项目中添加Bullet/PhysX框架 -
设置适当的编译器优化标志 -
考虑Metal后端渲染配合物理模拟
12.2 桌面端部署
-
利用多核CPU优势,提高物理计算线程数 -
支持更高精度的物理模拟 -
集成开发调试工具
12.3 嵌入式设备
-
简化物理场景复杂度 -
使用定点数学库优化计算 -
考虑功耗优化,降低CPU使用率
13. 疑难解答
13.1 常见问题及解决方案
-
原因:迭代次数不足或时间步长过大 -
解决:增加 velocityIterations和positionIterations,减小物理更新间隔
// Bullet中增加迭代次数
_dynamicsWorld->stepSimulation(dt, 10, 1.0f/120.0f); // 最大10次迭代,子步长1/120秒
-
原因:过多动态刚体或复杂碰撞检测 -
解决:使用静态刚体替代不移动的几何体,简化碰撞形状
-
原因:未正确释放Bullet/PhysX资源 -
解决:确保每个 create对应一个delete,使用RAII模式管理资源
-
原因:不同平台的浮点运算差异 -
解决:使用固定的时间点步长,避免依赖帧率
13.2 调试技巧
// 启用Bullet调试绘制
#ifdef DEBUG_DRAW
#include "bullet/BulletCollision/CollisionDispatch/btCollisionWorld.h"
#include "bullet/BulletCollision/CollisionShapes/btBoxShape.h"
class DebugDrawer : public btIDebugDraw {
public:
virtual void drawLine(const btVector3& from, const btVector3& to, const btVector3& color) override {
// 在屏幕上绘制调试线
}
// 实现其他虚函数...
};
DebugDrawer debugDrawer;
_dynamicsWorld->setDebugDrawer(&debugDrawer);
#endif
14. 未来展望
14.1 技术趋势
-
光线追踪与物理模拟结合 -
物理感知的LOD系统 -
基于物理的材质渲染
-
机器学习预测物理行为 -
智能碰撞优化 -
自适应物理精度
-
服务器端复杂物理计算 -
多客户端同步物理状态 -
分布式物理仿真
-
真实世界物理与虚拟物理结合 -
手势交互的物理反馈 -
空间锚点的物理约束
14.2 面临的挑战
-
高精度模拟的计算开销 -
移动设备的性能限制 -
网络同步的延迟问题
-
不同引擎间的状态同步 -
资源管理与内存优化 -
跨平台一致性保证
-
物理状态的完整性验证 -
防止客户端物理篡改 -
公平的多人体验保障
15. 总结
-
环境搭建:配置项目以支持3D物理引擎 -
核心实现:创建物理世界、刚体、关节和力的系统 -
场景应用:实现复杂的3D物理交互效果 -
性能优化:针对不同平台进行优化 -
问题解决:常见问题的诊断与解决方案
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)