Cocos2dx 刚体类型(静态/动态/运动学)应用场景
【摘要】 引言刚体类型是物理引擎中的核心概念,它决定了物体在物理世界中的行为和响应方式。在Cocos2dx中,通过Box2D或Chipmunk物理引擎的支持,开发者可以创建静态(Static)、动态(Dynamic)和运动学(Kinematic)三种基本刚体类型。每种类型都有其独特的应用场景和行为特性,正确理解和使用这些刚体类型是构建高质量物理游戏的基础。静态刚体适用于地面、墙壁等不动的场景元素;动态...
引言
技术背景
刚体类型基本概念
-
特性:质量无限大,不受力的影响,位置固定不变 -
用途:地面、墙壁、平台等静止不动的场景元素 -
性能:计算成本最低,适合大量使用 -
Box2D类型: b2_staticBody
-
特性:具有有限质量,完全受物理力影响,可移动、旋转 -
用途:角色、道具、可交互的游戏对象 -
性能:计算成本最高,需要精细管理数量 -
Box2D类型: b2_dynamicBody
-
特性:质量无限大但不受力影响,可通过程序控制运动 -
用途:移动平台、传送带、动画机关等程序控制的运动物体 -
性能:计算成本中等,需要正确处理碰撞响应 -
Box2D类型: b2_kinematicBody
Cocos2dx物理系统集成
-
Box2D集成:完整的2D物理引擎,提供精确的刚体模拟 -
Chipmunk集成:轻量级物理引擎,易于使用和扩展 -
内置物理节点: PhysicsBody、PhysicsWorld等封装类 -
碰撞过滤:基于类别和掩码的碰撞控制 -
关节系统:连接多个刚体的约束机制
核心特性
刚体类型特性对比
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
高级特性
-
碰撞过滤:精确控制哪些刚体之间可以发生碰撞 -
连续碰撞检测:防止高速物体穿透薄壁 -
睡眠机制:静止的动态刚体进入睡眠状态节省性能 -
关节约束:连接多个刚体创建复杂机械结构 -
质心控制:自定义刚体的质心位置 -
阻尼系统:线性和角度阻尼控制运动衰减
原理流程图
刚体类型决策流程
物体分类决策树:
1. 物体是否需要程序控制运动?
├─ 否 → 2. 物体是否会因外力而移动?
│ ├─ 否 → 静态刚体(地面、墙壁)
│ └─ 是 → 动态刚体(角色、道具)
└─ 是 → 运动学刚体(移动平台、动画机关)
物理模拟流程
物理世界更新循环:
1. 接收输入/程序控制 → 2. 更新运动学刚体位置
→ 3. 应用力和冲量 → 4. 检测碰撞
→ 5. 解决碰撞约束 → 6. 积分运动方程
→ 7. 更新精灵位置 → 8. 渲染输出
碰撞响应流程
碰撞处理详细流程:
检测碰撞 → 识别刚体类型组合 →
├─ 静态-动态:静态不动,动态反弹
├─ 动态-动态:两者都受影响
├─ 运动学-动态:运动学按程序运动,动态被推开
└─ 静态-静态:忽略(无碰撞响应)
计算碰撞冲量 → 应用反弹效果 → 更新运动状态
环境准备
开发环境配置
# Cocos2dx v4.0+ 环境搭建
git clone https://github.com/cocos2d/cocos2d-x.git
cd cocos2d-x
python download-deps.py
# 创建新项目
cocos new RigidBodyDemo -p com.example.rigidbody -l cpp -d ~/projects
cd ~/projects/RigidBodyDemo
# 启用Box2D物理引擎
# 编辑 CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(RigidBodyDemo)
# 启用物理引擎
set(USE_PHYSICS ON)
set(USE_BOX2D ON)
# 添加源文件
file(GLOB_RECURSE SOURCES
Classes/*.cpp
Classes/*.h
)
# 创建可执行文件
add_executable(${APP_NAME} ${SOURCES})
# 链接库
target_link_libraries(${APP_NAME} cocos2d Box2D)
# iOS特定配置
if(IOS)
set_target_properties(${APP_NAME} PROPERTIES
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_GUI_IDENTIFIER "com.example.rigidbody"
)
endif()
项目结构
RigidBodyDemo/
├── Classes/
│ ├── AppDelegate.h/.cpp
│ ├── HelloWorldScene.h/.cpp
│ ├── StaticBodyScene.h/.cpp # 静态刚体演示
│ ├── DynamicBodyScene.h/.cpp # 动态刚体演示
│ ├── KinematicBodyScene.h/.cpp # 运动学刚体演示
│ ├── MixedBodiesScene.h/.cpp # 混合刚体演示
│ └── RigidBodyManager.h/.cpp # 刚体管理器
├── Resources/
│ ├── textures/
│ │ ├── player.png
│ │ ├── platform.png
│ │ ├── ball.png
│ │ └── button_normal.png
│ └── fonts/
│ └── arial.ttf
└── CMakeLists.txt
不同场景下详细代码实现
1. 静态刚体场景实现
头文件定义 (StaticBodyScene.h)
#ifndef __STATIC_BODY_SCENE_H__
#define __STATIC_BODY_SCENE_H__
#include "cocos2d.h"
#include <Box2D/Box2D.h>
#include <vector>
USING_NS_CC;
class StaticBodyScene : public Scene {
public:
static Scene* createScene();
virtual bool init() override;
CREATE_FUNC(StaticBodyScene);
private:
// 初始化物理系统
void initPhysics();
// 创建静态几何
void createStaticGeometry();
// 创建演示物体
void createDemoObjects();
// 创建UI控制
void createUI();
// 更新方法
void update(float delta) override;
// 场景切换回调
void onNextSceneClicked(Ref* sender, Control::EventType controlEvent);
// 碰撞回调
void BeginContact(b2Contact* contact);
void EndContact(b2Contact* contact);
// 成员变量
b2World* _world;
DrawNode* _debugDraw;
Size _visibleSize;
// 演示物体
std::vector<b2Body*> _dynamicBodies;
std::vector<Sprite*> _dynamicSprites;
// UI元素
Label* _infoLabel;
int _objectCount;
};
#endif // __STATIC_BODY_SCENE_H__
实现文件 (StaticBodyScene.cpp)
#include "StaticBodyScene.h"
#include "ui/CocosGUI.h"
using namespace ui;
Scene* StaticBodyScene::createScene() {
return StaticBodyScene::create();
}
bool StaticBodyScene::init() {
if (!Scene::init()) {
return false;
}
_visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
// 创建背景
auto background = LayerColor::create(Color4B(25, 30, 40, 255));
this->addChild(background);
// 初始化物理系统
initPhysics();
// 创建静态几何(地面、墙壁、平台等)
createStaticGeometry();
// 创建演示物体(动态刚体)
createDemoObjects();
// 创建UI控制
createUI();
// 启用更新
this->scheduleUpdate();
// 设置碰撞监听
_world->SetContactListener(this);
return true;
}
void StaticBodyScene::initPhysics() {
// 创建Box2D世界,重力向下
b2Vec2 gravity(0.0f, -9.8f);
_world = new b2World(gravity);
// 创建调试绘制
_debugDraw = DrawNode::create();
this->addChild(_debugDraw);
// 设置调试绘制标志
_world->SetDebugDraw(true);
// 创建边界墙
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0.0f, 0.0f);
b2Body* groundBody = _world->CreateBody(&groundBodyDef);
// 边界尺寸(像素到米转换,1像素 = 1/32米)
float32 ratio = 32.0f;
float32 width = _visibleSize.width / ratio;
float32 height = _visibleSize.height / ratio;
// 创建边界形状
b2EdgeShape edgeShape;
// 底部边界(地面)- 静态刚体
edgeShape.Set(b2Vec2(0.0f, 0.0f), b2Vec2(width, 0.0f));
b2FixtureDef groundFixture;
groundFixture.shape = &edgeShape;
groundFixture.density = 0.0f; // 静态刚体密度为0
groundFixture.friction = 0.3f;
groundFixture.restitution = 0.1f;
groundBody->CreateFixture(&groundFixture);
// 左侧边界
edgeShape.Set(b2Vec2(0.0f, 0.0f), b2Vec2(0.0f, height));
groundBody->CreateFixture(&edgeShape, 0.0f);
// 右侧边界
edgeShape.Set(b2Vec2(width, 0.0f), b2Vec2(width, height));
groundBody->CreateFixture(&edgeShape, 0.0f);
// 顶部边界(可选)
edgeShape.Set(b2Vec2(0.0f, height), b2Vec2(width, height));
groundBody->CreateFixture(&edgeShape, 0.0f);
}
void StaticBodyScene::createStaticGeometry() {
float32 ratio = 32.0f;
// 创建平台结构 - 使用静态刚体
std::vector<std::tuple<Vec2, Vec2, std::string>> platforms = {
{Vec2(100, 150), Vec2(300, 150), "platform1"}, // 低平台
{Vec2(400, 250), Vec2(600, 250), "platform2"}, // 中平台
{Vec2(700, 350), Vec2(900, 350), "platform3"}, // 高平台
{Vec2(200, 400), Vec2(400, 400), "platform4"}, // 右平台
{Vec2(500, 100), Vec2(700, 100), "platform5"} // 浮动平台
};
for (const auto& platform : platforms) {
Vec2 start = std::get<0>(platform);
Vec2 end = std::get<1>(platform);
std::string name = std::get<2>(platform);
// 创建静态刚体作为平台
b2BodyDef platformBodyDef;
platformBodyDef.type = b2_staticBody; // 明确指定为静态刚体
platformBodyDef.position.Set(0.0f, 0.0f);
b2Body* platformBody = _world->CreateBody(&platformBodyDef);
// 创建平台形状(线段)
b2EdgeShape platformShape;
b2Vec2 b2Start(start.x / ratio, start.y / ratio);
b2Vec2 b2End(end.x / ratio, end.y / ratio);
platformShape.Set(b2Start, b2End);
// 平台夹具
b2FixtureDef platformFixture;
platformFixture.shape = &platformShape;
platformFixture.density = 0.0f; // 静态刚体必须密度为0
platformFixture.friction = 0.4f;
platformFixture.restitution = 0.2f;
platformBody->CreateFixture(&platformFixture);
// 可视化平台
auto drawNode = DrawNode::create();
Color4F platformColor(0.5f, 0.3f, 0.1f, 1.0f); // 棕色木质平台
drawNode->drawSegment(
start,
end,
8.0f, // 线宽
platformColor
);
this->addChild(drawNode);
// 添加平台标签
auto label = Label::createWithTTF(name, "fonts/arial.ttf", 14);
label->setPosition(Vec2((start.x + end.x) / 2, start.y - 15));
label->setColor(Color3B::WHITE);
this->addChild(label);
}
// 创建静态墙壁和障碍物
std::vector<Rect> walls = {
Rect(50, 200, 20, 200), // 左墙
Rect(_visibleSize.width - 70, 200, 20, 200), // 右墙
Rect(350, 300, 20, 150), // 中间分隔墙
Rect(150, 500, 100, 20), // 底部阻挡
Rect(650, 500, 100, 20) // 底部阻挡
};
for (size_t i = 0; i < walls.size(); ++i) {
Rect wall = walls[i];
// 创建静态刚体墙壁
b2BodyDef wallBodyDef;
wallBodyDef.type = b2_staticBody;
wallBodyDef.position.Set(0.0f, 0.0f);
b2Body* wallBody = _world->CreateBody(&wallBodyDef);
// 创建矩形形状
b2PolygonShape wallBox;
wallBox.SetAsBox(
(wall.size.width / 2.0f) / ratio,
(wall.size.height / 2.0f) / ratio,
b2Vec2((wall.origin.x + wall.size.width / 2.0f) / ratio,
(wall.origin.y + wall.size.height / 2.0f) / ratio),
0.0f
);
// 墙壁夹具
b2FixtureDef wallFixture;
wallFixture.shape = &wallBox;
wallFixture.density = 0.0f;
wallFixture.friction = 0.5f;
wallFixture.restitution = 0.3f;
wallBody->CreateFixture(&wallFixture);
// 可视化墙壁
auto wallDraw = DrawNode::create();
Color4F wallColor(0.3f, 0.3f, 0.3f, 1.0f); // 灰色墙壁
wallDraw->drawSolidRect(
Vec2(wall.origin.x, wall.origin.y),
Vec2(wall.origin.x + wall.size.width, wall.origin.y + wall.size.height),
wallColor
);
this->addChild(wallDraw);
}
}
void StaticBodyScene::createDemoObjects() {
_objectCount = 0;
float32 ratio = 32.0f;
// 创建不同类型的动态刚体演示物体
std::vector<std::tuple<Vec2, b2BodyType, std::string, Color3B>> demoObjects = {
{Vec2(150, 500), b2_dynamicBody, "circle", Color3B::RED}, // 圆形
{Vec2(250, 500), b2_dynamicBody, "square", Color3B::GREEN}, // 方形
{Vec2(350, 500), b2_dynamicBody, "triangle", Color3B::BLUE}, // 三角形
{Vec2(450, 500), b2_dynamicBody, "pentagon", Color3B::YELLOW}, // 五边形
{Vec2(550, 500), b2_dynamicBody, "heavy", Color3B::MAGENTA} // 重物体
};
for (const auto& obj : demoObjects) {
Vec2 position = std::get<0>(obj);
b2BodyType bodyType = std::get<1>(obj);
std::string type = std::get<2>(obj);
Color3B color = std::get<3>(obj);
// 创建动态刚体
b2BodyDef bodyDef;
bodyDef.type = bodyType;
bodyDef.position.Set(position.x / ratio, position.y / ratio);
bodyDef.userData = new std::string("demo_" + type + "_" + std::to_string(_objectCount));
b2Body* body = _world->CreateBody(&bodyDef);
b2Shape* shape = nullptr;
float radius = 20.0f / ratio; // 基础半径
// 根据类型创建不同形状
if (type == "circle") {
auto circleShape = new b2CircleShape();
circleShape->m_radius = radius;
shape = circleShape;
}
else if (type == "square") {
auto boxShape = new b2PolygonShape();
boxShape->SetAsBox(radius, radius);
shape = boxShape;
}
else if (type == "triangle") {
auto polyShape = new b2PolygonShape();
b2Vec2 vertices[3];
vertices[0].Set(0.0f, radius * 2.0f / 3.0f); // 顶部
vertices[1].Set(-radius, -radius * 1.0f / 3.0f); // 左下
vertices[2].Set(radius, -radius * 1.0f / 3.0f); // 右下
polyShape->Set(vertices, 3);
shape = polyShape;
}
else if (type == "pentagon") {
auto polyShape = new b2PolygonShape();
b2Vec2 vertices[5];
for (int i = 0; i < 5; ++i) {
float angle = 2.0f * M_PI * i / 5.0f - M_PI / 2.0f;
vertices[i].Set(radius * cosf(angle), radius * sinf(angle));
}
polyShape->Set(vertices, 5);
shape = polyShape;
}
else if (type == "heavy") {
auto boxShape = new b2PolygonShape();
boxShape->SetAsBox(radius * 1.5f, radius * 1.5f); // 更大的方形
shape = boxShape;
// 设置更高密度模拟重物
radius = radius * 1.5f; // 调整显示大小
}
if (shape) {
b2FixtureDef fixtureDef;
fixtureDef.shape = shape;
fixtureDef.density = (type == "heavy") ? 3.0f : 1.0f; // 重物密度更高
fixtureDef.friction = 0.3f;
fixtureDef.restitution = 0.4f;
body->CreateFixture(&fixtureDef);
delete shape; // 清理形状内存
// 创建对应的精灵
auto sprite = Sprite::create();
if (sprite) {
// 根据形状创建不同的视觉表现
if (type == "circle") {
sprite->setTextureRect(Rect(0, 0, radius * 2 * ratio, radius * 2 * ratio));
}
else if (type == "square") {
sprite->setTextureRect(Rect(0, 0, radius * 2 * ratio, radius * 2 * ratio));
}
else {
// 多边形使用圆形近似显示
sprite->setTextureRect(Rect(0, 0, radius * 2 * ratio, radius * 2 * ratio));
}
sprite->setColor(color);
sprite->setPosition(position);
sprite->setTag(_objectCount);
this->addChild(sprite);
// 保存引用
_dynamicBodies.push_back(body);
_dynamicSprites.push_back(sprite);
_objectCount++;
// 给物体初始速度
if (type != "heavy") {
body->SetLinearVelocity(b2Vec2(
(rand() % 100 - 50) / 10.0f, // -5 to 5 m/s
(rand() % 50) / 10.0f // 0 to 5 m/s upward
));
}
}
}
}
}
void StaticBodyScene::createUI() {
// 信息标签
_infoLabel = Label::createWithTTF(
"静态刚体演示 - 观察动态物体在静态几何上的物理行为",
"fonts/arial.ttf", 18
);
_infoLabel->setPosition(Vec2(_visibleSize.width * 0.5f, _visibleSize.height * 0.95f));
_infoLabel->setColor(Color3B::WHITE);
_infoLabel->setHorizontalAlignment(TextHAlignment::CENTER);
this->addChild(_infoLabel);
// 统计标签
auto statsLabel = Label::createWithTTF(
StringUtils::format("动态物体数量: %d", _objectCount),
"fonts/arial.ttf", 14
);
statsLabel->setPosition(Vec2(_visibleSize.width * 0.15f, _visibleSize.height * 0.05f));
statsLabel->setColor(Color3B::CYAN);
statsLabel->setTag(100); // 用于后续更新
this->addChild(statsLabel);
// 下一场景按钮
auto nextButton = Button::create("button_normal.png", "button_pressed.png");
nextButton->setTitleText("动态刚体场景");
nextButton->setTitleFontSize(16);
nextButton->setPosition(Vec2(_visibleSize.width * 0.85f, _visibleSize.height * 0.05f));
nextButton->addClickEventListener(CC_CALLBACK_2(StaticBodyScene::onNextSceneClicked, this));
this->addChild(nextButton);
// 重置按钮
auto resetButton = Button::create("button_normal.png", "button_pressed.png");
resetButton->setTitleText("重置物体");
resetButton->setTitleFontSize(16);
resetButton->setPosition(Vec2(_visibleSize.width * 0.85f, _visibleSize.height * 0.12f));
resetButton->addClickEventListener([this](Ref* sender, Control::EventType) {
this->resetDemoObjects();
});
this->addChild(resetButton);
}
void StaticBodyScene::resetDemoObjects() {
float32 ratio = 32.0f;
// 重置所有动态物体到初始位置
std::vector<Vec2> initialPositions = {
Vec2(150, 500), Vec2(250, 500), Vec2(350, 500),
Vec2(450, 500), Vec2(550, 500)
};
for (size_t i = 0; i < _dynamicBodies.size() && i < initialPositions.size(); ++i) {
b2Body* body = _dynamicBodies[i];
Sprite* sprite = _dynamicSprites[i];
if (body && sprite) {
Vec2 pos = initialPositions[i];
body->SetTransform(b2Vec2(pos.x / ratio, pos.y / ratio), 0.0f);
body->SetLinearVelocity(b2Vec2(0.0f, 0.0f));
body->SetAngularVelocity(0.0f);
sprite->setPosition(pos);
sprite->setRotation(0.0f);
}
}
_infoLabel->setString("物体已重置 - 观察物理行为");
this->scheduleOnce([this](float dt) {
_infoLabel->setString("静态刚体演示 - 观察动态物体在静态几何上的物理行为");
}, 2.0f, "reset_message");
}
void StaticBodyScene::onNextSceneClicked(Ref* sender, Control::EventType controlEvent) {
// 切换到动态刚体场景
auto director = Director::getInstance();
director->replaceScene(DynamicBodyScene::createScene());
}
void StaticBodyScene::BeginContact(b2Contact* contact) {
// 处理碰撞开始
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
// 检查是否是动态物体与地面/平台的碰撞
bool isGroundContact = false;
// 检查fixtureA
if (fixtureA->GetBody()->GetType() == b2_staticBody) {
isGroundContact = true;
}
// 检查fixtureB
else if (fixtureB->GetBody()->GetType() == b2_staticBody) {
isGroundContact = true;
}
if (isGroundContact) {
// 可以在这里添加碰撞音效或视觉效果
// 例如:改变碰撞物体的颜色或播放声音
log("Static collision detected - Dynamic object hit static geometry");
}
}
void StaticBodyScene::EndContact(b2Contact* contact) {
// 处理碰撞结束
}
void StaticBodyScene::update(float delta) {
// 更新Box2D世界
float32 timeStep = 1.0f / 60.0f;
int32 velocityIterations = 8;
int32 positionIterations = 3;
_world->Step(timeStep, velocityIterations, positionIterations);
// 同步动态物体的精灵位置
float32 ratio = 32.0f;
for (size_t i = 0; i < _dynamicBodies.size(); ++i) {
b2Body* body = _dynamicBodies[i];
Sprite* sprite = _dynamicSprites[i];
if (body && sprite) {
b2Vec2 position = body->GetPosition();
sprite->setPosition(Vec2(position.x * ratio, position.y * ratio));
sprite->setRotation(CC_RADIANS_TO_DEGREES(body->GetAngle()));
}
}
// 更新统计信息
auto statsLabel = static_cast<Label*>(this->getChildByTag(100));
if (statsLabel) {
statsLabel->setString(StringUtils::format("动态物体数量: %zu", _dynamicBodies.size()));
}
// 清除调试绘制
_debugDraw->clear();
}
2. 动态刚体场景实现
头文件定义 (DynamicBodyScene.h)
#ifndef __DYNAMIC_BODY_SCENE_H__
#define __DYNAMIC_BODY_SCENE_H__
#include "cocos2d.h"
#include <Box2D/Box2D.h>
#include <vector>
#include <random>
USING_NS_CC;
class DynamicBodyScene : public Scene {
public:
static Scene* createScene();
virtual bool init() override;
CREATE_FUNC(DynamicBodyScene);
private:
// 初始化物理系统
void initPhysics();
// 创建动态物体演示
void createDynamicDemonstrations();
// 创建交互控制
void createInteractiveControls();
// 创建性能监控
void createPerformanceMonitor();
// 更新方法
void update(float delta) override;
// 交互方法
void spawnDynamicObject(const Vec2& position, const std::string& type);
void applyForceToObject(b2Body* body, const Vec2& force);
void applyImpulseToObject(b2Body* body, const Vec2& impulse);
// UI回调
void onSpawnBallClicked(Ref* sender, Control::EventType controlEvent);
void onSpawnBoxClicked(Ref* sender, Control::EventType controlEvent);
void onGravityToggleClicked(Ref* sender, Control::EventType controlEvent);
void onClearAllClicked(Ref* sender, Control::EventType controlEvent);
void onPrevSceneClicked(Ref* sender, Control::EventType controlEvent);
void onNextSceneClicked(Ref* sender, Control::EventType controlEvent);
// 碰撞回调
void BeginContact(b2Contact* contact);
// 成员变量
b2World* _world;
DrawNode* _debugDraw;
Size _visibleSize;
// 动态物体管理
std::vector<b2Body*> _dynamicBodies;
std::vector<Sprite*> _dynamicSprites;
std::vector<std::string> _bodyTypes;
// 控制变量
bool _gravityEnabled;
int _spawnCount;
std::random_device _rd;
std::mt19937 _gen;
// UI元素
Label* _infoLabel;
Label* _performanceLabel;
Label* _objectCountLabel;
};
#endif // __DYNAMIC_BODY_SCENE_H__
实现文件 (DynamicBodyScene.cpp)
#include "DynamicBodyScene.h"
#include "ui/CocosGUI.h"
using namespace ui;
Scene* DynamicBodyScene::createScene() {
return DynamicBodyScene::create();
}
bool DynamicBodyScene::init() {
if (!Scene::init()) {
return false;
}
_visibleSize = Director::getInstance()->getVisibleSize();
_gen = std::mt19937(_rd());
_gravityEnabled = true;
_spawnCount = 0;
// 创建背景
auto background = LayerColor::create(Color4B(20, 25, 35, 255));
this->addChild(background);
// 初始化物理系统
initPhysics();
// 创建动态物体演示
createDynamicDemonstrations();
// 创建交互控制
createInteractiveControls();
// 创建性能监控
createPerformanceMonitor();
// 启用更新
this->scheduleUpdate();
// 设置碰撞监听
_world->SetContactListener(this);
return true;
}
void DynamicBodyScene::initPhysics() {
// 创建Box2D世界
b2Vec2 gravity(0.0f, -9.8f); // 标准重力
_world = new b2World(gravity);
// 创建调试绘制
_debugDraw = DrawNode::create();
this->addChild(_debugDraw);
// 创建地面(静态刚体)
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0.0f, 0.0f);
b2Body* groundBody = _world->CreateBody(&groundBodyDef);
float32 ratio = 32.0f;
float32 width = _visibleSize.width / ratio;
float32 height = _visibleSize.height / ratio;
b2EdgeShape groundEdge;
groundEdge.Set(b2Vec2(0.0f, 50.0f / ratio), b2Vec2(width, 50.0f / ratio));
groundBody->CreateFixture(&groundEdge, 0.0f);
// 创建边界墙
b2EdgeShape wallEdge;
// 左墙
wallEdge.Set(b2Vec2(0.0f, 0.0f), b2Vec2(0.0f, height));
groundBody->CreateFixture(&wallEdge, 0.0f);
// 右墙
wallEdge.Set(b2Vec2(width, 0.0f), b2Vec2(width, height));
groundBody->CreateFixture(&wallEdge, 0.0f);
}
void DynamicBodyScene::createDynamicDemonstrations() {
// 创建演示用的静态几何体(作为动态物体的目标)
createStaticTargets();
// 创建一些初始的动态物体展示
std::vector<std::tuple<Vec2, std::string, Color3B>> initialObjects = {
{Vec2(200, 400), "sphere", Color3B::RED},
{Vec2(300, 400), "box", Color3B::GREEN},
{Vec2(400, 400), "complex", Color3B::BLUE}
};
for (const auto& obj : initialObjects) {
spawnDynamicObject(std::get<0>(obj), std::get<1>(obj));
}
}
void DynamicBodyScene::createStaticTargets() {
float32 ratio = 32.0f;
// 创建目标区域 - 静态刚体
std::vector<std::tuple<Vec2, Vec2, std::string>> targets = {
{Vec2(100, 200), Vec2(200, 200), "target_red"},
{Vec2(300, 200), Vec2(400, 200), "target_green"},
{Vec2(500, 200), Vec2(600, 200), "target_blue"}
};
for (const auto& target : targets) {
Vec2 start = std::get<0>(target);
Vec2 end = std::get<1>(target);
std::string name = std::get<2>(target);
// 静态目标平台
b2BodyDef targetBodyDef;
targetBodyDef.type = b2_staticBody;
targetBodyDef.position.Set(0.0f, 0.0f);
b2Body* targetBody = _world->CreateBody(&targetBodyDef);
b2EdgeShape targetShape;
targetShape.Set(
b2Vec2(start.x / ratio, start.y / ratio),
b2Vec2(end.x / ratio, end.y / ratio)
);
b2FixtureDef targetFixture;
targetFixture.shape = &targetShape;
targetFixture.density = 0.0f;
targetFixture.friction = 0.6f;
targetFixture.restitution = 0.8f; // 高弹性目标
targetBody->CreateFixture(&targetFixture);
// 可视化目标
auto drawNode = DrawNode::create();
Color4F targetColor;
if (name.find("red") != std::string::npos) targetColor = Color4F(1.0f, 0.3f, 0.3f, 0.8f);
else if (name.find("green") != std::string::npos) targetColor = Color4F(0.3f, 1.0f, 0.3f, 0.8f);
else targetColor = Color4F(0.3f, 0.3f, 1.0f, 0.8f);
drawNode->drawSegment(start, end, 12.0f, targetColor);
this->addChild(drawNode);
// 目标标签
auto label = Label::createWithTTF(name, "fonts/arial.ttf", 12);
label->setPosition(Vec2((start.x + end.x) / 2, start.y - 20));
label->setColor(Color3B::WHITE);
this->addChild(label);
}
}
void DynamicBodyScene::createInteractiveControls() {
// 信息标签
_infoLabel = Label::createWithTTF(
"动态刚体演示 - 创建和控制动态物体,观察物理行为",
"fonts/arial.ttf", 18
);
_infoLabel->setPosition(Vec2(_visibleSize.width * 0.5f, _visibleSize.height * 0.95f));
_infoLabel->setColor(Color3B::WHITE);
_infoLabel->setHorizontalAlignment(TextHAlignment::CENTER);
this->addChild(_infoLabel);
// 控制按钮
float buttonY = _visibleSize.height * 0.08f;
float buttonSpacing = 120.0f;
// 生成球体按钮
auto sphereButton = Button::create("button_normal.png", "button_pressed.png");
sphereButton->setTitleText("生成球体");
sphereButton->setTitleFontSize(14);
sphereButton->setPosition(Vec2(_visibleSize.width * 0.2f, buttonY));
sphereButton->addClickEventListener(CC_CALLBACK_2(DynamicBodyScene::onSpawnBallClicked, this));
this->addChild(sphereButton);
// 生成方块按钮
auto boxButton = Button::create("button_normal.png", "button_pressed.png");
boxButton->setTitleText("生成方块");
boxButton->setTitleFontSize(14);
boxButton->setPosition(Vec2(_visibleSize.width * 0.4f, buttonY));
boxButton->addClickEventListener(CC_CALLBACK_2(DynamicBodyScene::onSpawnBoxClicked, this));
this->addChild(boxButton);
// 重力开关按钮
auto gravityButton = Button::create("button_normal.png", "button_pressed.png");
gravityButton->setTitleText("重力: 开");
gravityButton->setTitleFontSize(14);
gravityButton->setPosition(Vec2(_visibleSize.width * 0.6f, buttonY));
gravityButton->addClickEventListener(CC_CALLBACK_2(DynamicBodyScene::onGravityToggleClicked, this));
gravityButton->setTag(200); // 用于更新按钮文本
this->addChild(gravityButton);
// 清空所有按钮
auto clearButton = Button::create("button_normal.png", "button_pressed.png");
clearButton->setTitleText("清空物体");
clearButton->setTitleFontSize(14);
clearButton->setPosition(Vec2(_visibleSize.width * 0.8f, buttonY));
clearButton->addClickEventListener(CC_CALLBACK_2(DynamicBodyScene::onClearAllClicked, this));
this->addChild(clearButton);
// 导航按钮
float navY = _visibleSize.height * 0.15f;
// 上一场景按钮
auto prevButton = Button::create("button_normal.png", "button_pressed.png");
prevButton->setTitleText("静态场景");
prevButton->setTitleFontSize(14);
prevButton->setPosition(Vec2(_visibleSize.width * 0.3f, navY));
prevButton->addClickEventListener(CC_CALLBACK_2(DynamicBodyScene::onPrevSceneClicked, this));
this->addChild(prevButton);
// 下一场景按钮
auto nextButton = Button::create("button_normal.png", "button_pressed.png");
nextButton->setTitleText("运动学场景");
nextButton->setTitleFontSize(14);
nextButton->setPosition(Vec2(_visibleSize.width * 0.7f, navY));
nextButton->addClickEventListener(CC_CALLBACK_2(DynamicBodyScene::onNextSceneClicked, this));
this->addChild(nextButton);
}
void DynamicBodyScene::createPerformanceMonitor() {
// 性能标签
_performanceLabel = Label::createWithTTF(
"FPS: 60 | 物体: 0 | 内存: 0MB",
"fonts/arial.ttf", 12
);
_performanceLabel->setPosition(Vec2(_visibleSize.width * 0.15f, _visibleSize.height * 0.03f));
_performanceLabel->setColor(Color3B::YELLOW);
_performanceLabel->setTag(101);
this->addChild(_performanceLabel);
// 物体计数标签
_objectCountLabel = Label::createWithTTF(
"动态物体: 0",
"fonts/arial.ttf", 12
);
_objectCountLabel->setPosition(Vec2(_visibleSize.width * 0.85f, _visibleSize.height * 0.03f));
_objectCountLabel->setColor(Color3B::CYAN);
_objectCountLabel->setTag(102);
this->addChild(_objectCountLabel);
}
void DynamicBodyScene::spawnDynamicObject(const Vec2& position, const std::string& type) {
float32 ratio = 32.0f;
// 创建动态刚体
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(position.x / ratio, position.y / ratio);
bodyDef.userData = new std::string("dynamic_" + type + "_" + std::to_string(_spawnCount++));
bodyDef.allowSleep = true; // 允许休眠以节省性能
bodyDef.awake = true;
b2Body* body = _world->CreateBody(&bodyDef);
b2Shape* shape = nullptr;
Color3B color;
float density = 1.0f;
float friction = 0.3f;
float restitution = 0.4f;
// 根据类型创建不同形状
if (type == "sphere" || type == "ball") {
auto circleShape = new b2CircleShape();
circleShape->m_radius = 15.0f / ratio;
shape = circleShape;
color = Color3B(rand() % 128 + 127, rand() % 128 + 127, rand() % 128 + 127); // 随机亮色
restitution = 0.8f; // 球体高弹性
}
else if (type == "box") {
auto boxShape = new b2PolygonShape();
boxShape->SetAsBox(12.0f / ratio, 12.0f / ratio);
shape = boxShape;
color = Color3B(rand() % 200, rand() % 200, rand() % 200); // 随机灰色调
friction = 0.5f; // 方块较高摩擦
}
else if (type == "complex") {
// 复杂多边形
auto polyShape = new b2PolygonShape();
b2Vec2 vertices[6];
for (int i = 0; i < 6; ++i) {
float angle = 2.0f * M_PI * i / 6.0f;
float radius = (rand() % 10 + 15.0f) / ratio; // 随机半径
vertices[i].Set(radius * cosf(angle), radius * sinf(angle));
}
polyShape->Set(vertices, 6);
shape = polyShape;
color = Color3B(rand() % 256, rand() % 256, rand() % 256); // 完全随机颜色
density = 1.5f; // 复杂形状密度稍高
}
else {
// 默认球形
auto circleShape = new b2CircleShape();
circleShape->m_radius = 15.0f / ratio;
shape = circleShape;
color = Color3B::WHITE;
}
if (shape) {
b2FixtureDef fixtureDef;
fixtureDef.shape = shape;
fixtureDef.density = density;
fixtureDef.friction = friction;
fixtureDef.restitution = restitution;
// 添加用户数据标识形状类型
std::string shapeType = "unknown";
if (dynamic_cast<b2CircleShape*>(shape)) shapeType = "circle";
else if (dynamic_cast<b2PolygonShape*>(shape)) shapeType = "polygon";
fixtureDef.userData = new std::string(shapeType);
body->CreateFixture(&fixtureDef);
delete shape;
// 创建对应的精灵
auto sprite = Sprite::create();
float displayRadius = 30.0f; // 显示半径
if (type == "box") {
sprite->setTextureRect(Rect(0, 0, displayRadius * 2, displayRadius * 2));
} else {
sprite->setTextureRect(Rect(0, 0, displayRadius * 2, displayRadius * 2));
}
sprite->setColor(color);
sprite->setPosition(position);
sprite->setTag(_dynamicSprites.size());
this->addChild(sprite);
// 保存引用
_dynamicBodies.push_back(body);
_dynamicSprites.push_back(sprite);
_bodyTypes.push_back(type);
// 给新物体一个随机的初始力
std::uniform_real_distribution<float> forceXDist(-50.0f, 50.0f);
std::uniform_real_distribution<float> forceYDist(0.0f, 100.0f);
b2Vec2 initialForce(forceXDist(_gen), forceYDist(_gen));
applyForceToObject(body, Vec2(initialForce.x, initialForce.y));
// 更新物体计数
updateObjectCount();
}
}
void DynamicBodyScene::applyForceToObject(b2Body* body, const Vec2& force) {
if (body && body->GetType() == b2_dynamicBody) {
b2Vec2 b2Force(force.x, force.y);
body->ApplyForceToCenter(b2Force, true);
}
}
void DynamicBodyScene::applyImpulseToObject(b2Body* body, const Vec2& impulse) {
if (body && body->GetType() == b2_dynamicBody) {
b2Vec2 b2Impulse(impulse.x, impulse.y);
body->ApplyLinearImpulse(b2Impulse, body->GetWorldCenter(), true);
}
}
void DynamicBodyScene::onSpawnBallClicked(Ref* sender, Control::EventType controlEvent) {
// 在随机位置生成球体
std::uniform_real_distribution<float> xDist(50.0f, _visibleSize.width - 50.0f);
std::uniform_real_distribution<float> yDist(100.0f, _visibleSize.height * 0.6f);
Vec2 spawnPos(xDist(_gen), yDist(_gen));
spawnDynamicObject(spawnPos, "sphere");
_infoLabel->setString("球体已生成 - 观察其物理行为");
this->scheduleOnce([this](float dt) {
_infoLabel->setString("动态刚体演示 - 创建和控制动态物体,观察物理行为");
}, 2.0f, "spawn_message");
}
void DynamicBodyScene::onSpawnBoxClicked(Ref* sender, Control::EventType controlEvent) {
// 在随机位置生成方块
std::uniform_real_distribution<float> xDist(50.0f, _visibleSize.width - 50.0f);
std::uniform_real_distribution<float> yDist(100.0f, _visibleSize.height * 0.6f);
Vec2 spawnPos(xDist(_gen), yDist(_gen));
spawnDynamicObject(spawnPos, "box");
_infoLabel->setString("方块已生成 - 观察其物理行为");
this->scheduleOnce([this](float dt) {
_infoLabel->setString("动态刚体演示 - 创建和控制动态物体,观察物理行为");
}, 2.0f, "spawn_message");
}
void DynamicBodyScene::onGravityToggleClicked(Ref* sender, Control::EventType controlEvent) {
_gravityEnabled = !_gravityEnabled;
if (_gravityEnabled) {
_world->SetGravity(b2Vec2(0.0f, -9.8f));
} else {
_world->SetGravity(b2Vec2(0.0f, 0.0f)); // 零重力
}
// 更新按钮文本
auto button = static_cast<Button*>(this->getChildByTag(200));
if (button) {
button->setTitleText(StringUtils::format("重力: %s", _gravityEnabled ? "开" : "关"));
}
_infoLabel->setString(StringUtils::format("重力已%s", _gravityEnabled ? "开启" : "关闭"));
this->scheduleOnce([this](float dt) {
_infoLabel->setString("动态刚体演示 - 创建和控制动态物体,观察物理行为");
}, 2.0f, "gravity_message");
}
void DynamicBodyScene::onClearAllClicked(Ref* sender, Control::EventType controlEvent) {
// 删除所有动态物体
for (size_t i = 0; i < _dynamicBodies.size(); ++i) {
b2Body* body = _dynamicBodies[i];
Sprite* sprite = _dynamicSprites[i];
if (body) {
// 清理夹具用户数据
b2Fixture* fixture = body->GetFixtureList();
while (fixture) {
if (fixture->GetUserData()) {
delete static_cast<std::string*>(fixture->GetUserData());
}
fixture = fixture->GetNext();
}
_world->DestroyBody(body);
}
if (sprite) {
sprite->removeFromParent();
}
}
// 清空容器
_dynamicBodies.clear();
_dynamicSprites.clear();
_bodyTypes.clear();
_spawnCount = 0;
updateObjectCount();
_infoLabel->setString("所有动态物体已清空");
this->scheduleOnce([this](float dt) {
_infoLabel->setString("动态刚体演示 - 创建和控制动态物体,观察物理行为");
}, 2.0f, "clear_message");
}
void DynamicBodyScene::onPrevSceneClicked(Ref* sender, Control::EventType controlEvent) {
auto director = Director::getInstance();
director->replaceScene(StaticBodyScene::createScene());
}
void DynamicBodyScene::onNextSceneClicked(Ref* sender, Control::EventType controlEvent) {
auto director = Director::getInstance();
director->replaceScene(KinematicBodyScene::createScene());
}
void DynamicBodyScene::BeginContact(b2Contact* contact) {
// 处理动态物体之间的碰撞
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
b2Body* bodyA = fixtureA->GetBody();
b2Body* bodyB = fixtureB->GetBody();
// 检查是否是动态物体之间的碰撞
if (bodyA->GetType() == b2_dynamicBody && bodyB->GetType() == b2_dynamicBody) {
// 动态物体碰撞 - 可以添加特殊效果
std::string* typeA = static_cast<std::string*>(fixtureA->GetUserData());
std::string* typeB = static_cast<std::string*>(fixtureB->GetUserData());
log("Dynamic-Dynamic collision: %s + %s",
typeA ? typeA->c_str() : "unknown",
typeB ? typeB->c_str() : "unknown");
// 碰撞视觉效果(可选)
// 可以通过改变精灵颜色来指示碰撞
}
}
void DynamicBodyScene::updateObjectCount() {
auto countLabel = static_cast<Label*>(this->getChildByTag(102));
if (countLabel) {
countLabel->setString(StringUtils::format("动态物体: %zu", _dynamicBodies.size()));
}
}
void DynamicBodyScene::update(float delta) {
// 更新Box2D世界
float32 timeStep = 1.0f / 60.0f;
int32 velocityIterations = 8;
int32 positionIterations = 3;
_world->Step(timeStep, velocityIterations, positionIterations);
// 同步动态物体的精灵位置
float32 ratio = 32.0f;
for (size_t i = 0; i < _dynamicBodies.size(); ++i) {
b2Body* body = _dynamicBodies[i];
Sprite* sprite = _dynamicSprites[i];
if (body && sprite) {
b2Vec2 position = body->GetPosition();
sprite->setPosition(Vec2(position.x * ratio, position.y * ratio));
sprite->setRotation(CC_RADIANS_TO_DEGREES(body->GetAngle()));
// 检查物体是否掉出屏幕,如果是则移除
if (position.y * ratio < -100) {
// 标记为待删除
body->SetUserData(nullptr); // 标记删除
}
}
}
// 清理掉落的物体
cleanupFallenBodies();
// 更新性能监控
updatePerformanceMonitor();
// 更新物体计数
updateObjectCount();
// 清除调试绘制
_debugDraw->clear();
}
void DynamicBodyScene::cleanupFallenBodies() {
// 清理掉出屏幕的动态物体
for (int i = _dynamicBodies.size() - 1; i >= 0; --i) {
b2Body* body = _dynamicBodies[i];
Sprite* sprite = _dynamicSprites[i];
if (body && body->GetUserData() == nullptr) {
// 销毁刚体
_world->DestroyBody(body);
_dynamicBodies.erase(_dynamicBodies.begin() + i);
// 移除精灵
if (sprite) {
sprite->removeFromParent();
_dynamicSprites.erase(_dynamicSprites.begin() + i);
_bodyTypes.erase(_bodyTypes.begin() + i);
}
}
}
}
void DynamicBodyScene::updatePerformanceMonitor() {
auto director = Director::getInstance();
float fps = director->getFrameRate();
// 估算内存使用(粗略计算)
size_t memoryUsage = 0;
memoryUsage += _dynamicBodies.size() * 256; // 每个刚体约256字节
memoryUsage += _dynamicSprites.size() * 512; // 每个精灵约512字节
memoryUsage = memoryUsage / (1024 * 1024); // 转换为MB
// 更新性能标签
auto perfLabel = static_cast<Label*>(this->getChildByTag(101));
if (perfLabel) {
perfLabel->setString(StringUtils::format(
"FPS: %.1f | 物体: %zu | 内存: %zuMB",
fps, _dynamicBodies.size(), memoryUsage
));
// 根据FPS改变颜色
if (fps < 30) {
perfLabel->setColor(Color3B::RED);
} else if (fps < 50) {
perfLabel->setColor(Color3B::YELLOW);
} else {
perfLabel->setColor(Color3B::GREEN);
}
}
}
3. 运动学刚体场景实现
头文件定义 (KinematicBodyScene.h)
#ifndef __KINEMATIC_BODY_SCENE_H__
#define __KINEMATIC_BODY_SCENE_H__
#include "cocos2d.h"
#include <Box2D/Box2D.h>
#include <vector>
#include <functional>
USING_NS_CC;
class KinematicBodyScene : public Scene {
public:
static Scene* createScene();
virtual bool init() override;
CREATE_FUNC(KinematicBodyScene);
private:
// 初始化物理系统
void initPhysics();
// 创建运动学刚体演示
void createKinematicDemonstrations();
// 创建交互控制
void createKinematicControls();
// 创建UI显示
void createUIDisplay();
// 更新方法
void update(float delta) override;
// 运动学刚体控制方法
void moveKinematicBody(b2Body* body, const b2Vec2& target, float speed);
void rotateKinematicBody(b2Body* body, float targetAngle, float angularSpeed);
void stopKinematicBody(b2Body* body);
// 动画和运动模式
void startMovingPlatformAnimation();
void startRotatingBladeAnimation();
void startConveyorBeltAnimation();
void startElevatorAnimation();
// UI回调
void onPlatformToggleClicked(Ref* sender, Control::EventType controlEvent);
void onBladeToggleClicked(Ref* sender, Control::EventType controlEvent);
void onConveyorToggleClicked(Ref* sender, Control::EventType controlEvent);
void onElevatorToggleClicked(Ref* sender, Control::EventType controlEvent);
void onResetSceneClicked(Ref* sender, Control::EventType controlEvent);
void onPrevSceneClicked(Ref* sender, Control::EventType controlEvent);
// 碰撞回调
void BeginContact(b2Contact* contact);
void EndContact(b2Contact* contact);
// 成员变量
b2World* _world;
DrawNode* _debugDraw;
Size _visibleSize;
// 运动学刚体
std::vector<b2Body*> _kinematicBodies;
std::vector<Sprite*> _kinematicSprites;
std::vector<std::string> _kinematicTypes;
// 动态物体(用于演示交互)
std::vector<b2Body*> _dynamicBodies;
std::vector<Sprite*> _dynamicSprites;
// 控制状态
std::unordered_map<std::string, bool> _animationStates;
std::unordered_map<std::string, std::function<void(float)>> _animations;
// UI元素
Label* _infoLabel;
std::unordered_map<std::string, Button*> _controlButtons;
};
#endif // __KINEMATIC_BODY_SCENE_H__
实现文件 (KinematicBodyScene.cpp)
#include "KinematicBodyScene.h"
#include "ui/CocosGUI.h"
using namespace ui;
Scene* KinematicBodyScene::createScene() {
return KinematicBodyScene::create();
}
bool KinematicBodyScene::init() {
if (!Scene::init()) {
return false;
}
_visibleSize = Director::getInstance()->getVisibleSize();
// 初始化动画状态
_animationStates = {
{"platform", false},
{"blade", false},
{"conveyor", false},
{"elevator", false}
};
// 创建背景
auto background = LayerColor::create(Color4B(30, 35, 45, 255));
this->addChild(background);
// 初始化物理系统
initPhysics();
// 创建运动学刚体演示
createKinematicDemonstrations();
// 创建交互控制
createKinematicControls();
// 创建UI显示
createUIDisplay();
// 启用更新
this->scheduleUpdate();
// 设置碰撞监听
_world->SetContactListener(this);
// 启动默认动画
startMovingPlatformAnimation();
_animationStates["platform"] = true;
return true;
}
void KinematicBodyScene::initPhysics() {
// 创建Box2D世界
b2Vec2 gravity(0.0f, -9.8f);
_world = new b2World(gravity);
// 创建调试绘制
_debugDraw = DrawNode::create();
this->addChild(_debugDraw);
// 创建地面(静态刚体)
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0.0f, 0.0f);
b2Body* groundBody = _world->CreateBody(&groundBodyDef);
float32 ratio = 32.0f;
float32 width = _visibleSize.width / ratio;
float32 height = _visibleSize.height / ratio;
// 地面
b2EdgeShape groundEdge;
groundEdge.Set(b2Vec2(0.0f, 50.0f / ratio), b2Vec2(width, 50.0f / ratio));
groundBody->CreateFixture(&groundEdge, 0.0f);
// 左墙
b2EdgeShape leftWall;
leftWall.Set(b2Vec2(0.0f, 0.0f), b2Vec2(0.0f, height));
groundBody->CreateFixture(&leftWall, 0.0f);
// 右墙
b2EdgeShape rightWall;
rightWall.Set(b2Vec2(width, 0.0f), b2Vec2(width, height));
groundBody->CreateFixture(&rightWall, 0.0f);
// 创建一些静态障碍物
createStaticObstacles();
}
void KinematicBodyScene::createStaticObstacles() {
float32 ratio = 32.0f;
// 创建静态障碍物供运动学刚体交互
std::vector<Rect> obstacles = {
Rect(200, 200, 20, 150), // 垂直支柱
Rect(500, 150, 20, 200), // 另一个支柱
Rect(350, 300, 100, 20) // 横梁
};
for (size_t i = 0; i < obstacles.size(); ++i) {
Rect obs = obstacles[i];
b2BodyDef obsBodyDef;
obsBodyDef.type = b2_staticBody;
obsBodyDef.position.Set(0.0f, 0.0f);
b2Body* obsBody = _world->CreateBody(&obsBodyDef);
b2PolygonShape obsShape;
obsShape.SetAsBox(
(obs.size.width / 2.0f) / ratio,
(obs.size.height / 2.0f) / ratio,
b2Vec2((obs.origin.x + obs.size.width / 2.0f) / ratio,
(obs.origin.y + obs.size.height / 2.0f) / ratio),
0.0f
);
b2FixtureDef obsFixture;
obsFixture.shape = &obsShape;
obsFixture.density = 0.0f;
obsFixture.friction = 0.5f;
obsFixture.restitution = 0.3f;
obsBody->CreateFixture(&obsFixture);
// 可视化障碍物
auto drawNode = DrawNode::create();
drawNode->drawSolidRect(
Vec2(obs.origin.x, obs.origin.y),
Vec2(obs.origin.x + obs.size.width, obs.origin.y + obs.size.height),
Color4F(0.4f, 0.4f, 0.4f, 1.0f)
);
this->addChild(drawNode);
}
}
void KinematicBodyScene::createKinematicDemonstrations() {
float32 ratio = 32.0f;
// 1. 移动平台 - 水平往复运动
{
b2BodyDef platformBodyDef;
platformBodyDef.type = b2_kinematicBody; // 运动学刚体
platformBodyDef.position.Set(200.0f / ratio, 180.0f / ratio);
b2Body* platformBody = _world->CreateBody(&platformBodyDef);
b2PolygonShape platformShape;
platformShape.SetAsBox(80.0f / ratio, 10.0f / ratio);
b2FixtureDef platformFixture;
platformFixture.shape = &platformShape;
platformFixture.density = 1.0f; // 运动学刚体可以有密度,但不会受重力影响
platformFixture.friction = 0.8f; // 高摩擦,物体能站在上面
platformFixture.restitution = 0.1f;
platformBody->CreateFixture(&platformFixture);
// 可视化平台
auto platformSprite = Sprite::create();
platformSprite->setTextureRect(Rect(0, 0, 160, 20));
platformSprite->setColor(Color3B(139, 69, 19)); // 棕色木制平台
platformSprite->setPosition(Vec2(200, 180));
this->addChild(platformSprite);
_kinematicBodies.push_back(platformBody);
_kinematicSprites.push_back(platformSprite);
_kinematicTypes.push_back("moving_platform");
}
// 2. 旋转刀片 - 绕中心点旋转
{
b2BodyDef bladeBodyDef;
bladeBodyDef.type = b2_kinematicBody;
bladeBodyDef.position.Set(600.0f / ratio, 250.0f / ratio);
b2Body* bladeBody = _world->CreateBody(&bladeBodyDef);
// 创建刀片形状(长条形)
b2PolygonShape bladeShape;
b2Vec2 vertices[4];
vertices[0].Set(-60.0f / ratio, -5.0f / ratio); // 左下
vertices[1].Set(-60.0f / ratio, 5.0f / ratio); // 左上
vertices[2].Set(60.0f / ratio, 5.0f / ratio); // 右上
vertices[3].Set(60.0f / ratio, -5.0f / ratio); // 右下
bladeShape.Set(vertices, 4);
b2FixtureDef bladeFixture;
bladeFixture.shape = &bladeShape;
bladeFixture.density = 2.0f;
bladeFixture.friction = 0.1f;
bladeFixture.restitution = 0.2f;
bladeBody->CreateFixture(&bladeFixture);
// 可视化刀片
auto bladeSprite = Sprite::create();
bladeSprite->setTextureRect(Rect(0, 0, 120, 10));
bladeSprite->setColor(Color3B(192, 192, 192)); // 银色金属
bladeSprite->setPosition(Vec2(600, 250));
this->addChild(bladeSprite);
_kinematicBodies.push_back(bladeBody);
_kinematicSprites.push_back(bladeSprite);
_kinematicTypes.push_back("rotating_blade");
}
// 3. 传送带 - 持续水平运动
{
b2BodyDef conveyorBodyDef;
conveyorBodyDef.type = b2_kinematicBody;
conveyorBodyDef.position.Set(400.0f / ratio, 120.0f / ratio);
b2Body* conveyorBody = _world->CreateBody(&conveyorBodyDef);
b2PolygonShape conveyorShape;
conveyorShape.SetAsBox(120.0f / ratio, 8.0f / ratio);
b2FixtureDef conveyorFixture;
conveyorFixture.shape = &conveyorShape;
conveyorFixture.density = 1.0f;
conveyorFixture.friction = 0.1f; // 低摩擦,物体会滑动
conveyorFixture.restitution = 0.0f;
conveyorBody->CreateFixture(&conveyorFixture);
// 可视化传送带
auto conveyorSprite = Sprite::create();
conveyorSprite->setTextureRect(Rect(0, 0, 240, 16));
conveyorSprite->setColor(Color3B(105, 105, 105)); // 深灰色
conveyorSprite->setPosition(Vec2(400, 120));
this->addChild(conveyorSprite);
_kinematicBodies.push_back(conveyorBody);
_kinematicSprites.push_back(conveyorSprite);
_kinematicTypes.push_back("conveyor_belt");
}
// 4. 电梯 - 垂直运动
{
b2BodyDef elevatorBodyDef;
elevatorBodyDef.type = b2_kinematicBody;
elevatorBodyDef.position.Set(700.0f / ratio, 100.0f / ratio);
b2Body* elevatorBody = _world->CreateBody(&elevatorBodyDef);
b2PolygonShape elevatorShape;
elevatorShape.SetAsBox(50.0f / ratio, 40.0f / ratio);
b2FixtureDef elevatorFixture;
elevatorFixture.shape = &elevatorShape;
elevatorFixture.density = 1.5f;
elevatorFixture.friction = 0.7f;
elevatorFixture.restitution = 0.1f;
elevatorBody->CreateFixture(&elevatorFixture);
// 可视化电梯
auto elevatorSprite = Sprite::create();
elevatorSprite->setTextureRect(Rect(0, 0, 100, 80));
elevatorSprite->setColor(Color3B(169, 169, 169)); // 浅灰色
elevatorSprite->setPosition(Vec2(700, 100));
this->addChild(elevatorSprite);
_kinematicBodies.push_back(elevatorBody);
_kinematicSprites.push_back(elevatorSprite);
_kinematicTypes.push_back("elevator");
}
// 创建一些动态物体用于演示交互
createDemoDynamicObjects();
}
void KinematicBodyScene::createDemoDynamicObjects() {
float32 ratio = 32.0f;
// 创建几个动态物体来演示与运动学刚体的交互
std::vector<Vec2> positions = {
Vec2(150, 300), Vec2(250, 300), Vec2(350, 300),
Vec2(450, 300), Vec2(550, 300), Vec2(650, 300)
};
std::vector<Color3B> colors = {
Color3B::RED, Color3B::GREEN, Color3B::BLUE,
Color3B::YELLOW, Color3B::MAGENTA, Color3B::CYAN
};
for (size_t i = 0; i < positions.size(); ++i) {
// 创建动态刚体
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(positions[i].x / ratio, positions[i].y / ratio);
bodyDef.userData = new std::string("demo_dynamic_" + std::to_string(i));
b2Body* body = _world->CreateBody(&bodyDef);
// 创建球形
b2CircleShape circleShape;
circleShape.m_radius = 12.0f / ratio;
b2FixtureDef fixtureDef;
fixtureDef.shape = &circleShape;
fixtureDef.density = 1.0f;
fixtureDef.friction = 0.3f;
fixtureDef.restitution = 0.5f;
body->CreateFixture(&fixtureDef);
// 创建精灵
auto sprite = Sprite::create();
sprite->setTextureRect(Rect(0, 0, 24, 24));
sprite->setColor(colors[i]);
sprite->setPosition(positions[i]);
sprite->setTag(i);
this->addChild(sprite);
_dynamicBodies.push_back(body);
_dynamicSprites.push_back(sprite);
// 给一个小的初始速度让物体动起来
if (i % 2 == 0) {
body->SetLinearVelocity(b2Vec2(2.0f, 0.0f)); // 向右移动
} else {
body->SetLinearVelocity(b2Vec2(-1.0f, 1.0f)); // 向左上移动
}
}
}
void KinematicBodyScene::createKinematicControls() {
// 信息标签
_infoLabel = Label::createWithTTF(
"运动学刚体演示 - 观察程序控制的物体如何影响动态物体",
"fonts/arial.ttf", 18
);
_infoLabel->setPosition(Vec2(_visibleSize.width * 0.5f, _visibleSize.height * 0.95f));
_infoLabel->setColor(Color3B::WHITE);
_infoLabel->setHorizontalAlignment(TextHAlignment::CENTER);
this->addChild(_infoLabel);
// 控制按钮
float startY = _visibleSize.height * 0.08f;
float buttonWidth = 120.0f;
float buttonHeight = 35.0f;
float spacing = 10.0f;
std::vector<std::tuple<std::string, std::string, Vec2>> controls = {
{"platform", "移动平台", Vec2(_visibleSize.width * 0.15f, startY)},
{"blade", "旋转刀片", Vec2(_visibleSize.width * 0.35f, startY)},
{"conveyor", "传送带", Vec2(_visibleSize.width * 0.55f, startY)},
{"elevator", "电梯", Vec2(_visibleSize.width * 0.75f, startY)}
};
for (const auto& control : controls) {
std::string key = std::get<0>(control);
std::string text = std::get<1>(control);
Vec2 position = std::get<2>(control);
auto button = Button::create("button_normal.png", "button_pressed.png");
button->setTitleText(text);
button->setTitleFontSize(12);
button->setPosition(position);
button->setContentSize(Size(buttonWidth, buttonHeight));
// 设置初始按钮状态
bool initialState = _animationStates[key];
button->setTitleText(StringUtils::format("%s [%s]", text.c_str(), initialState ? "ON" : "OFF"));
// 绑定回调
if (key == "platform") {
button->addClickEventListener(CC_CALLBACK_2(KinematicBodyScene::onPlatformToggleClicked, this));
} else if (key == "blade") {
button->addClickEventListener(CC_CALLBACK_2(KinematicBodyScene::onBladeToggleClicked, this));
} else if (key == "conveyor") {
button->addClickEventListener(CC_CALLBACK_2(KinematicBodyScene::onConveyorToggleClicked, this));
} else if (key == "elevator") {
button->addClickEventListener(CC_CALLBACK_2(KinematicBodyScene::onElevatorToggleClicked, this));
}
this->addChild(button);
_controlButtons[key] = button;
}
// 重置场景按钮
auto resetButton = Button::create("button_normal.png", "button_pressed.png");
resetButton->setTitleText("重置场景");
resetButton->setTitleFontSize(14);
resetButton->setPosition(Vec2(_visibleSize.width * 0.5f, startY + 50));
resetButton->addClickEventListener(CC_CALLBACK_2(KinematicBodyScene::onResetSceneClicked, this));
this->addChild(resetButton);
// 上一场景按钮
auto prevButton = Button::create("button_normal.png", "button_pressed.png");
prevButton->setTitleText("动态场景");
prevButton->setTitleFontSize(14);
prevButton->setPosition(Vec2(_visibleSize.width * 0.3f, startY + 100));
prevButton->addClickEventListener(CC_CALLBACK_2(KinematicBodyScene::onPrevSceneClicked, this));
this->addChild(prevButton);
}
void KinematicBodyScene::createUIDisplay() {
// 说明文字
auto instructions = Label::createWithTTF(
"操作说明:\n• 移动平台:承载物体水平移动\n• 旋转刀片:推动接触的物体\n• 传送带:持续传送物体\n• 电梯:垂直运送物体",
"fonts/arial.ttf", 14
);
instructions->setPosition(Vec2(_visibleSize.width * 0.5f, _visibleSize.height * 0.82f));
instructions->setColor(Color3B::LIGHT_BLUE);
instructions->setDimensions(_visibleSize.width * 0.8f, 100);
instructions->setHorizontalAlignment(TextHAlignment::CENTER);
this->addChild(instructions);
// 图例
float legendY = _visibleSize.height * 0.65f;
std::vector<std::tuple<std::string, Color3B, std::string>> legends = {
{"静态刚体", Color3B(100, 100, 100), "地面、墙壁"},
{"动态刚体", Color3B(255, 100, 100), "小球、方块"},
{"运动学刚体", Color3B(100, 100, 255), "平台、刀片"}
};
for (size_t i = 0; i < legends.size(); ++i) {
float x = _visibleSize.width * (0.2f + i * 0.3f);
// 颜色指示器
auto colorBox = DrawNode::create();
Color3B color = std::get<1>(legends[i]);
colorBox->drawSolidRect(
Vec2(x - 15, legendY),
Vec2(x + 15, legendY - 15),
Color4F(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, 1.0f)
);
this->addChild(colorBox);
// 文字说明
auto label = Label::createWithTTF(
StringUtils::format("%s: %s",
std::get<0>(legends[i]).c_str(),
std::get<2>(legends[i]).c_str()),
"fonts/arial.ttf", 12
);
label->setPosition(Vec2(x, legendY - 25));
label->setColor(Color3B::WHITE);
this->addChild(label);
}
}
void KinematicBodyScene::moveKinematicBody(b2Body* body, const b2Vec2& target, float speed) {
if (!body || body->GetType() != b2_kinematicBody) return;
b2Vec2 currentPos = body->GetPosition();
b2Vec2 direction = target - currentPos;
if (direction.LengthSquared() > 0.01f) {
direction.Normalize();
b2Vec2 velocity = direction * speed;
body->SetLinearVelocity(velocity);
} else {
body->SetLinearVelocity(b2Vec2_zero);
}
}
void KinematicBodyScene::rotateKinematicBody(b2Body* body, float targetAngle, float angularSpeed) {
if (!body || body->GetType() != b2_kinematicBody) return;
float currentAngle = body->GetAngle();
float angleDiff = targetAngle - currentAngle;
// 规范化角度差到 [-π, π]
while (angleDiff > M_PI) angleDiff -= 2 * M_PI;
while (angleDiff < -M_PI) angleDiff += 2 * M_PI;
if (fabs(angleDiff) > 0.01f) {
float direction = angleDiff > 0 ? 1.0f : -1.0f;
body->SetAngularVelocity(direction * angularSpeed);
} else {
body->SetAngularVelocity(0.0f);
}
}
void KinematicBodyScene::stopKinematicBody(b2Body* body) {
if (body && body->GetType() == b2_kinematicBody) {
body->SetLinearVelocity(b2Vec2_zero);
body->SetAngularVelocity(0.0f);
}
}
void KinematicBodyScene::startMovingPlatformAnimation() {
// 移动平台往复运动
auto animatePlatform = [this](float deltaTime) {
static float phase = 0.0f;
phase += deltaTime * 0.5f; // 控制速度
if (_kinematicBodies.size() > 0) {
b2Body* platform = _kinematicBodies[0];
if (platform && _animationStates["platform"]) {
float ratio = 32.0f;
float baseX = 200.0f / ratio;
float amplitude = 100.0f / ratio; // 移动幅度
float x = baseX + amplitude * sinf(phase);
float y = 180.0f / ratio;
// 设置运动学刚体位置(瞬时移动)
platform->SetTransform(b2Vec2(x, y), 0.0f);
// 同步精灵位置
if (_kinematicSprites.size() > 0) {
_kinematicSprites[0]->setPosition(Vec2(x * ratio, y * ratio));
}
}
}
};
_animations["platform"] = animatePlatform;
}
void KinematicBodyScene::startRotatingBladeAnimation() {
// 旋转刀片动画
auto animateBlade = [this](float deltaTime) {
static float angle = 0.0f;
angle += deltaTime * 2.0f; // 角速度
if (_kinematicBodies.size() > 1) {
b2Body* blade = _kinematicBodies[1];
if (blade && _animationStates["blade"]) {
float ratio = 32.0f;
float x = 600.0f / ratio;
float y = 250.0f / ratio;
blade->SetTransform(b2Vec2(x, y), angle);
// 同步精灵位置和旋转
if (_kinematicSprites.size() > 1) {
_kinematicSprites[1]->setPosition(Vec2(x * ratio, y * ratio));
_kinematicSprites[1]->setRotation(CC_RADIANS_TO_DEGREES(angle));
}
}
}
};
_animations["blade"] = animateBlade;
}
void KinematicBodyScene::startConveyorBeltAnimation() {
// 传送带持续运动
auto animateConveyor = [this](float deltaTime) {
if (_kinematicBodies.size() > 2) {
b2Body* conveyor = _kinematicBodies[2];
if (conveyor && _animationStates["conveyor"]) {
// 传送带给予物体持续的速度
float beltSpeed = 3.0f; // m/s
conveyor->SetLinearVelocity(b2Vec2(beltSpeed, 0.0f));
// 可视化传送带纹理滚动效果(可选)
if (_kinematicSprites.size() > 2) {
// 这里可以添加传送带纹理滚动的视觉效果
}
} else if (conveyor) {
conveyor->SetLinearVelocity(b2Vec2_zero);
}
}
};
_animations["conveyor"] = animateConveyor;
}
void KinematicBodyScene::startElevatorAnimation() {
// 电梯上下运动
auto animateElevator = [this](float deltaTime) {
static float phase = 0.0f;
phase += deltaTime * 0.3f;
if (_kinematicBodies.size() > 3) {
b2Body* elevator = _kinematicBodies[3];
if (elevator && _animationStates["elevator"]) {
float ratio = 32.0f;
float baseY = 100.0f / ratio;
float amplitude = 150.0f / ratio; // 上升下降幅度
float x = 700.0f / ratio;
float y = baseY + amplitude * (sinf(phase) + 0.5f) / 2.0f; // 保持在某个范围内
elevator->SetTransform(b2Vec2(x, y), 0.0f);
// 同步精灵位置
if (_kinematicSprites.size() > 3) {
_kinematicSprites[3]->setPosition(Vec2(x * ratio, y * ratio));
}
}
}
};
_animations["elevator"] = animateElevator;
}
void KinematicBodyScene::onPlatformToggleClicked(Ref* sender, Control::EventType controlEvent) {
_animationStates["platform"] = !_animationStates["platform"];
updateControlButton("platform");
if (!_animationStates["platform"]) {
// 停止平台运动
if (_kinematicBodies.size() > 0) {
stopKinematicBody(_kinematicBodies[0]);
}
}
}
void KinematicBodyScene::onBladeToggleClicked(Ref* sender, Control::EventType controlEvent) {
_animationStates["blade"] = !_animationStates["blade"];
updateControlButton("blade");
if (!_animationStates["blade"]) {
// 停止刀片旋转
if (_kinematicBodies.size() > 1) {
stopKinematicBody(_kinematicBodies[1]);
}
}
}
void KinematicBodyScene::onConveyorToggleClicked(Ref* sender, Control::EventType controlEvent) {
_animationStates["conveyor"] = !_animationStates["conveyor"];
updateControlButton("conveyor");
}
void KinematicBodyScene::onElevatorToggleClicked(Ref* sender, Control::EventType controlEvent) {
_animationStates["elevator"] = !_animationStates["elevator"];
updateControlButton("elevator");
if (!_animationStates["elevator"]) {
// 停止电梯运动
if (_kinematicBodies.size() > 3) {
stopKinematicBody(_kinematicBodies[3]);
}
}
}
void KinematicBodyScene::onResetSceneClicked(Ref* sender, Control::EventType controlEvent) {
// 重置所有动画状态
for (auto& state : _animationStates) {
state.second = false;
updateControlButton(state.first);
}
// 停止所有运动学刚体
for (auto body : _kinematicBodies) {
stopKinematicBody(body);
}
// 重置动态物体位置
float ratio = 32.0f;
std::vector<Vec2> resetPositions = {
Vec2(150, 300), Vec2(250, 300), Vec2(350, 300),
Vec2(450, 300), Vec2(550, 300), Vec2(650, 300)
};
for (size_t i = 0; i < _dynamicBodies.size() && i < resetPositions.size(); ++i) {
b2Body* body = _dynamicBodies[i];
Sprite* sprite = _dynamicSprites[i];
if (body && sprite) {
Vec2 pos = resetPositions[i];
body->SetTransform(b2Vec2(pos.x / ratio, pos.y / ratio), 0.0f);
body->SetLinearVelocity(b2Vec2(0.0f, 0.0f));
body->SetAngularVelocity(0.0f);
sprite->setPosition(pos);
sprite->setRotation(0.0f);
}
}
_infoLabel->setString("场景已重置");
this->scheduleOnce([this](float dt) {
_infoLabel->setString("运动学刚体演示 - 观察程序控制的物体如何影响动态物体");
}, 2.0f, "reset_message");
}
void KinematicBodyScene::onPrevSceneClicked(Ref* sender, Control::EventType controlEvent) {
auto director = Director::getInstance();
director->replaceScene(DynamicBodyScene::createScene());
}
void KinematicBodyScene::updateControlButton(const std::string& key) {
auto it = _controlButtons.find(key);
if (it != _controlButtons.end()) {
Button* button = it->second;
std::string baseText;
if (key == "platform") baseText = "移动平台";
else if (key == "blade") baseText = "旋转刀片";
else if (key == "conveyor") baseText = "传送带";
else if (key == "elevator") baseText = "电梯";
bool state = _animationStates[key];
button->setTitleText(StringUtils::format("%s [%s]", baseText.c_str(), state ? "ON" : "OFF"));
// 更新按钮颜色
if (state) {
button->setColor(Color3B(100, 200, 100)); // 绿色表示开启
} else {
button->setColor(Color3B(200, 100, 100)); // 红色表示关闭
}
}
}
void KinematicBodyScene::BeginContact(b2Contact* contact) {
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
b2Body* bodyA = fixtureA->GetBody();
b2Body* bodyB = fixtureB->GetBody();
// 检查运动学刚体与动态刚体的碰撞
bool kinematicDynamicContact = false;
b2Body* kinematicBody = nullptr;
b2Body* dynamicBody = nullptr;
if (bodyA->GetType() == b2_kinematicBody && bodyB->GetType() == b2_dynamicBody) {
kinematicDynamicContact = true;
kinematicBody = bodyA;
dynamicBody = bodyB;
} else if (bodyA->GetType() == b2_dynamicBody && bodyB->GetType() == b2_kinematicBody) {
kinematicDynamicContact = true;
kinematicBody = bodyB;
dynamicBody = bodyA;
}
if (kinematicDynamicContact && kinematicBody && dynamicBody) {
// 运动学刚体与动态刚体碰撞
std::string kinematicType = "unknown";
std::string dynamicType = "unknown";
// 确定具体的刚体类型
for (size_t i = 0; i < _kinematicBodies.size(); ++i) {
if (_kinematicBodies[i] == kinematicBody) {
kinematicType = _kinematicTypes[i];
break;
}
}
// 根据碰撞类型添加特殊效果
if (kinematicType == "moving_platform") {
// 平台碰撞 - 可以附加物体到平台上
log("Dynamic object landed on moving platform");
} else if (kinematicType == "rotating_blade") {
// 刀片碰撞 - 给物体一个推动力
b2Vec2 bladeVel = kinematicBody->GetLinearVelocity();
b2Vec2 bladeAngularVel = kinematicBody->GetAngularVelocity();
// 传递一些运动给动态物体
b2Vec2 impulse(bladeVel.x * 0.1f, bladeVel.y * 0.1f);
dynamicBody->ApplyLinearImpulse(impulse, dynamicBody->GetWorldCenter(), true);
} else if (kinematicType == "conveyor_belt") {
// 传送带碰撞 - 持续给物体速度
b2Vec2 conveyorVel = kinematicBody->GetLinearVelocity();
dynamicBody->SetLinearVelocity(dynamicBody->GetLinearVelocity() + conveyorVel * 0.5f);
} else if (kinematicType == "elevator") {
// 电梯碰撞 - 垂直运输
log("Dynamic object entered elevator");
}
}
}
void KinematicBodyScene::EndContact(b2Contact* contact) {
// 处理碰撞结束
}
void KinematicBodyScene::update(float delta) {
// 执行所有活动的动画
for (auto& animation : _animations) {
if (_animationStates[animation.first]) {
animation.second(delta);
}
}
// 更新Box2D世界
float32 timeStep = 1.0f / 60.0f;
int32 velocityIterations = 8;
int32 positionIterations = 3;
_world->Step(timeStep, velocityIterations, positionIterations);
// 同步运动学刚体的精灵(除了通过动画更新的)
syncKinematicSprites();
// 同步动态物体的精灵
syncDynamicSprites();
// 清除调试绘制
_debugDraw->clear();
}
void KinematicBodyScene::syncKinematicSprites() {
float32 ratio = 32.0f;
// 注意:大多数运动学刚体通过动画直接更新位置,这里处理特殊情况
for (size_t i = 0; i < _kinematicBodies.size(); ++i) {
b2Body* body = _kinematicBodies[i];
Sprite* sprite = _kinematicSprites[i];
if (body && sprite) {
// 只有没有被动画控制的刚体才需要在这里同步
// (目前所有运动学刚体都有对应的动画)
b2Vec2 position = body->GetPosition();
sprite->setPosition(Vec2(position.x * ratio, position.y * ratio));
sprite->setRotation(CC_RADIANS_TO_DEGREES(body->GetAngle()));
}
}
}
void KinematicBodyScene::syncDynamicSprites() {
float32 ratio = 32.0f;
for (size_t i = 0; i < _dynamicBodies.size(); ++i) {
b2Body* body = _dynamicBodies[i];
Sprite* sprite = _dynamicSprites[i];
if (body && sprite) {
b2Vec2 position = body->GetPosition();
sprite->setPosition(Vec2(position.x * ratio, position.y * ratio));
sprite->setRotation(CC_RADIANS_TO_DEGREES(body->GetAngle()));
// 检查是否掉出屏幕
if (position.y * ratio < -50) {
// 重置位置
std::vector<Vec2> resetPositions = {
Vec2(150, 300), Vec2(250, 300), Vec2(350, 300),
Vec2(450, 300), Vec2(550, 300), Vec2(650, 300)
};
if (i < resetPositions.size()) {
Vec2 resetPos = resetPositions[i];
body->SetTransform(b2Vec2(resetPos.x / ratio, resetPos.y / ratio), 0.0f);
body->SetLinearVelocity(b2Vec2(0.0f, 0.0f));
sprite->setPosition(resetPos);
}
}
}
}
}
4. 刚体管理器(通用工具类)
头文件定义 (RigidBodyManager.h)
#ifndef __RIGID_BODY_MANAGER_H__
#define __RIGID_BODY_MANAGER_H__
#include "cocos2d.h"
#include <Box2D/Box2D.h>
#include <unordered_map>
#include <memory>
#include <vector>
USING_NS_CC;
/**
* 刚体类型枚举
*/
enum class RigidBodyType {
STATIC = 0, // 静态刚体
DYNAMIC = 1, // 动态刚体
KINEMATIC = 2 // 运动学刚体
};
/**
* 刚体配置结构体
*/
struct RigidBodyConfig {
RigidBodyType type = RigidBodyType::DYNAMIC;
float density = 1.0f;
float friction = 0.3f;
float restitution = 0.2f;
bool allowSleep = true;
bool awake = true;
bool bullet = false; // 连续碰撞检测
std::string userData = "";
// 构造函数
RigidBodyConfig() = default;
RigidBodyConfig(RigidBodyType t, float d, float f, float r)
: type(t), density(d), friction(f), restitution(r) {}
};
/**
* 碰撞过滤配置
*/
struct CollisionFilter {
uint16 categoryBits = 0x0001; // 类别位
uint16 maskBits = 0xFFFF; // 掩码位
uint16 groupIndex = 0; // 组索引
CollisionFilter() = default;
CollisionFilter(uint16 cat, uint16 mask, uint16 group = 0)
: categoryBits(cat), maskBits(mask), groupIndex(group) {}
};
/**
* 刚体管理器类
* 提供统一的刚体创建、管理和销毁接口
*/
class RigidBodyManager {
public:
/**
* 单例模式
*/
static RigidBodyManager& getInstance() {
static RigidBodyManager instance;
return instance;
}
/**
* 初始化管理器
*/
void init(b2World* world);
/**
* 创建刚体
*/
b2Body* createBody(const b2BodyDef& bodyDef, const RigidBodyConfig& config);
/**
* 创建带形状的刚体
*/
b2Body* createBodyWithShape(const b2BodyDef& bodyDef,
const RigidBodyConfig& config,
b2Shape* shape,
const CollisionFilter& filter = CollisionFilter());
/**
* 创建标准形状刚体
*/
b2Body* createCircleBody(const Vec2& position, float radius,
const RigidBodyConfig& config = RigidBodyConfig(),
const CollisionFilter& filter = CollisionFilter());
b2Body* createBoxBody(const Vec2& position, const Size& size,
const RigidBodyConfig& config = RigidBodyConfig(),
const CollisionFilter& filter = CollisionFilter());
b2Body* createPolygonBody(const Vec2& position,
const std::vector<Vec2>& vertices,
const RigidBodyConfig& config = RigidBodyConfig(),
const CollisionFilter& filter = CollisionFilter());
/**
* 设置刚体类型
*/
void setBodyType(b2Body* body, RigidBodyType type);
/**
* 设置刚体材质
*/
void setBodyMaterial(b2Body* body, float density, float friction, float restitution);
/**
* 设置碰撞过滤
*/
void setCollisionFilter(b2Body* body, const CollisionFilter& filter);
/**
* 应用力到刚体
*/
void applyForce(b2Body* body, const Vec2& force, const Vec2& point = Vec2::ZERO);
/**
* 应用冲量到刚体
*/
void applyImpulse(b2Body* body, const Vec2& impulse, const Vec2& point = Vec2::ZERO);
/**
* 设置刚体线性速度
*/
void setLinearVelocity(b2Body* body, const Vec2& velocity);
/**
* 设置刚体角速度
*/
void setAngularVelocity(b2Body* body, float angularVelocity);
/**
* 销毁刚体
*/
void destroyBody(b2Body* body);
/**
* 获取刚体类型
*/
RigidBodyType getBodyType(b2Body* body) const;
/**
* 检查是否为静态刚体
*/
bool isStaticBody(b2Body* body) const { return getBodyType(body) == RigidBodyType::STATIC; }
/**
* 检查是否为动态刚体
*/
bool isDynamicBody(b2Body* body) const { return getBodyType(body) == RigidBodyType::DYNAMIC; }
/**
* 检查是否为运动学刚体
*/
bool isKinematicBody(b2Body* body) const { return getBodyType(body) == RigidBodyType::KINEMATIC; }
/**
* 批量创建静态几何体
*/
void createStaticGeometry(const std::vector<Rect>& rectangles,
const std::vector<std::pair<Vec2, Vec2>>& edges = {});
/**
* 批量创建动态物体
*/
void createDynamicObjects(const std::vector<std::tuple<Vec2, std::string, float>>& objects);
/**
* 清理所有管理的刚体
*/
void cleanup();
private:
RigidBodyManager() = default;
~RigidBodyManager() = default;
// 禁用拷贝和赋值
RigidBodyManager(const RigidBodyManager&) = delete;
RigidBodyManager& operator=(const RigidBodyManager&) = delete;
/**
* 内部方法:形状创建辅助
*/
b2Shape* createShapeFromType(const std::string& type, const Size& size, float radius,
const std::vector<Vec2>& vertices);
/**
* 内部方法:单位转换
*/
b2Vec2 pixelToMeter(const Vec2& pixel) const;
Vec2 meterToPixel(const b2Vec2& meter) const;
// 成员变量
b2World* _world = nullptr;
float _pixelToMeterRatio = 32.0f; // 像素到米的转换比例
// 管理的刚体列表(用于调试和清理)
std::vector<b2Body*> _managedBodies;
std::unordered_map<b2Body*, std::string> _bodyTags;
};
#endif // __RIGID_BODY_MANAGER_H__
实现文件 (RigidBodyManager.cpp)
#include "RigidBodyManager.h"
void RigidBodyManager::init(b2World* world) {
_world = world;
_pixelToMeterRatio = 32.0f; // 默认32像素 = 1米
}
b2Body* RigidBodyManager::createBody(const b2BodyDef& bodyDef, const RigidBodyConfig& config) {
if (!_world) return nullptr;
// 创建刚体
b2Body* body = _world->CreateBody(&bodyDef);
// 设置刚体类型
setBodyType(body, config.type);
// 设置其他属性
body->SetSleepingAllowed(config.allowSleep);
body->SetAwake(config.awake);
body->SetBullet(config.bullet);
// 设置用户数据
if (!config.userData.empty()) {
body->SetUserData(new std::string(config.userData));
}
// 添加到管理列表
_managedBodies.push_back(body);
return body;
}
b2Body* RigidBodyManager::createBodyWithShape(const b2BodyDef& bodyDef,
const RigidBodyConfig& config,
b2Shape* shape,
const CollisionFilter& filter) {
if (!_world || !shape) return nullptr;
// 创建刚体
b2Body* body = createBody(bodyDef, config);
if (!body) return nullptr;
// 创建夹具
b2FixtureDef fixtureDef;
fixtureDef.shape = shape;
fixtureDef.density = config.density;
fixtureDef.friction = config.friction;
fixtureDef.restitution = config.restitution;
// 设置碰撞过滤
b2Filter b2filter;
b2filter.categoryBits = filter.categoryBits;
b2filter.maskBits = filter.maskBits;
b2filter.groupIndex = filter.groupIndex;
fixtureDef.filter = b2filter;
// 添加用户数据标识形状类型
std::string shapeType = "unknown";
if (dynamic_cast<b2CircleShape*>(shape)) shapeType = "circle";
else if (dynamic_cast<b2PolygonShape*>(shape)) shapeType = "polygon";
else if (dynamic_cast<b2EdgeShape*>(shape)) shapeType = "edge";
fixtureDef.userData = new std::string(shapeType);
body->CreateFixture(&fixtureDef);
return body;
}
b2Body* RigidBodyManager::createCircleBody(const Vec2& position, float radius,
const RigidBodyConfig& config,
const CollisionFilter& filter) {
if (!_world) return nullptr;
// 创建刚体定义
b2BodyDef bodyDef;
bodyDef.type = static_cast<b2BodyType>(config.type);
bodyDef.position = pixelToMeter(position);
bodyDef.userData = new std::string(config.userData);
// 创建圆形形状
auto circleShape = new b2CircleShape();
circleShape->m_radius = radius / _pixelToMeterRatio;
// 创建刚体
b2Body* body = createBodyWithShape(bodyDef, config, circleShape, filter);
// 清理形状
delete circleShape;
return body;
}
b2Body* RigidBodyManager::createBoxBody(const Vec2& position, const Size& size,
const RigidBodyConfig& config,
const CollisionFilter& filter) {
if (!_world) return nullptr;
// 创建刚体定义
b2BodyDef bodyDef;
bodyDef.type = static_cast<b2BodyType>(config.type);
bodyDef.position = pixelToMeter(position);
bodyDef.userData = new std::string(config.userData);
// 创建多边形形状(矩形)
auto boxShape = new b2PolygonShape();
boxShape->SetAsBox(
(size.width / 2.0f) / _pixelToMeterRatio,
(size.height / 2.0f) / _pixelToMeterRatio
);
// 创建刚体
b2Body* body = createBodyWithShape(bodyDef, config, boxShape, filter);
// 清理形状
delete boxShape;
return body;
}
b2Body* RigidBodyManager::createPolygonBody(const Vec2& position,
const std::vector<Vec2>& vertices,
const RigidBodyConfig& config,
const CollisionFilter& filter) {
if (!_world || vertices.size() < 3) return nullptr;
// 创建刚体定义
b2BodyDef bodyDef;
bodyDef.type = static_cast<b2BodyType>(config.type);
bodyDef.position = pixelToMeter(position);
bodyDef.userData = new std::string(config.userData);
// 创建多边形形状
auto polyShape = new b2PolygonShape();
b2Vec2* b2Vertices = new b2Vec2[vertices.size()];
for (size_t i = 0; i < vertices.size(); ++i) {
b2Vertices[i] = pixelToMeter(vertices[i]);
}
polyShape->Set(b2Vertices, vertices.size());
delete[] b2Vertices;
// 创建刚体
b2Body* body = createBodyWithShape(bodyDef, config, polyShape, filter);
// 清理形状
delete polyShape;
return body;
}
void RigidBodyManager::setBodyType(b2Body* body, RigidBodyType type) {
if (!body) return;
b2BodyType b2Type;
switch (type) {
case RigidBodyType::STATIC:
b2Type = b2_staticBody;
break;
case RigidBodyType::DYNAMIC:
b2Type = b2_dynamicBody;
break;
case RigidBodyType::KINEMATIC:
b2Type = b2_kinematicBody;
break;
default:
b2Type = b2_staticBody;
break;
}
body->SetType(b2Type);
}
void RigidBodyManager::setBodyMaterial(b2Body* body, float density, float friction, float restitution) {
if (!body) return;
b2Fixture* fixture = body->GetFixtureList();
while (fixture) {
fixture->SetDensity(density);
fixture->SetFriction(friction);
fixture->SetRestitution(restitution);
fixture = fixture->GetNext();
}
// 重新计算质量数据
body->ResetMassData();
}
void RigidBodyManager::setCollisionFilter(b2Body* body, const CollisionFilter& filter) {
if (!body) return;
b2Filter b2filter;
b2filter.categoryBits = filter.categoryBits;
b2filter.maskBits = filter.maskBits;
b2filter.groupIndex = filter.groupIndex;
b2Fixture* fixture = body->GetFixtureList();
while (fixture) {
fixture->SetFilterData(b2filter);
fixture = fixture->GetNext();
}
}
void RigidBodyManager::applyForce(b2Body* body, const Vec2& force, const Vec2& point) {
if (!body || body->GetType() != b2_dynamicBody) return;
b2Vec2 b2Force = pixelToMeter(force);
b2Vec2 b2Point = point == Vec2::ZERO ? body->GetWorldCenter() : pixelToMeter(point);
body->ApplyForce(b2Force, b2Point, true);
}
void RigidBodyManager::applyImpulse(b2Body* body, const Vec2& impulse, const Vec2& point) {
if (!body || body->GetType() != b2_dynamicBody) return;
b2Vec2 b2Impulse = pixelToMeter(impulse);
b2Vec2 b2Point = point == Vec2::ZERO ? body->GetWorldCenter() : pixelToMeter(point);
body->ApplyLinearImpulse(b2Impulse, b2Point, true);
}
void RigidBodyManager::setLinearVelocity(b2Body* body, const Vec2& velocity) {
if (!body) return;
body->SetLinearVelocity(pixelToMeter(velocity));
}
void RigidBodyManager::setAngularVelocity(b2Body* body, float angularVelocity) {
if (!body) return;
body->SetAngularVelocity(angularVelocity);
}
void RigidBodyManager::destroyBody(b2Body* body) {
if (!body || !_world) return;
// 从管理列表中移除
auto it = std::find(_managedBodies.begin(), _managedBodies.end(), body);
if (it != _managedBodies.end()) {
_managedBodies.erase(it);
}
// 清理用户数据
if (body->GetUserData()) {
delete static_cast<std::string*>(body->GetUserData());
body->SetUserData(nullptr);
}
// 清理夹具用户数据
b2Fixture* fixture = body->GetFixtureList();
while (fixture) {
if (fixture->GetUserData()) {
delete static_cast<std::string*>(fixture->GetUserData());
fixture->SetUserData(nullptr);
}
fixture = fixture->GetNext();
}
// 销毁刚体
_world->DestroyBody(body);
}
RigidBodyType RigidBodyManager::getBodyType(b2Body* body) const {
if (!body) return RigidBodyType::STATIC;
switch (body->GetType()) {
case b2_staticBody:
return RigidBodyType::STATIC;
case b2_dynamicBody:
return RigidBodyType::DYNAMIC;
case b2_kinematicBody:
return RigidBodyType::KINEMATIC;
default:
return RigidBodyType::STATIC;
}
}
void RigidBodyManager::createStaticGeometry(const std::vector<Rect>& rectangles,
const std::vector<std::pair<Vec2, Vec2>>& edges) {
if (!_world) return;
// 创建矩形静态刚体
for (const auto& rect : rectangles) {
Vec2 center = Vec2(rect.origin.x + rect.size.width / 2,
rect.origin.y + rect.size.height / 2);
Size size = rect.size;
RigidBodyConfig config(RigidBodyType::STATIC, 0.0f, 0.5f, 0.3f);
config.userData = "static_geometry_rect";
createBoxBody(center, size, config);
}
// 创建边缘静态刚体
for (const auto& edge : edges) {
Vec2 start = edge.first;
Vec2 end = edge.second;
// 创建边缘形状
b2BodyDef bodyDef;
bodyDef.type = b2_staticBody;
bodyDef.position = pixelToMeter(Vec2::ZERO);
b2Body* body = _world->CreateBody(&bodyDef);
b2EdgeShape edgeShape;
edgeShape.Set(pixelToMeter(start), pixelToMeter(end));
b2FixtureDef fixtureDef;
fixtureDef.shape = &edgeShape;
fixtureDef.density = 0.0f;
fixtureDef.friction = 0.5f;
fixtureDef.restitution = 0.3f;
body->CreateFixture(&fixtureDef);
_managedBodies.push_back(body);
}
}
void RigidBodyManager::createDynamicObjects(const std::vector<std::tuple<Vec2, std::string, float>>& objects) {
if (!_world) return;
for (const auto& obj : objects) {
Vec2 position = std::get<0>(obj);
std::string type = std::get<1>(obj);
float scale = std::get<2>(obj);
RigidBodyConfig config(RigidBodyType::DYNAMIC, 1.0f, 0.3f, 0.4f);
config.userData = "dynamic_object_" + type;
b2Body* body = nullptr;
if (type == "circle") {
body = createCircleBody(position, 20.0f * scale, config);
} else if (type == "box") {
body = createBoxBody(position, Size(40.0f * scale, 40.0f * scale), config);
} else if (type == "triangle") {
std::vector<Vec2> vertices = {
Vec2(0, 30 * scale),
Vec2(-25 * scale, -15 * scale),
Vec2(25 * scale, -15 * scale)
};
body = createPolygonBody(position, vertices, config);
}
if (body) {
// 给物体随机初始速度
Vec2 randomVelocity((rand() % 200 - 100) / 10.0f, (rand() % 100) / 10.0f);
setLinearVelocity(body, randomVelocity);
}
}
}
void RigidBodyManager::cleanup() {
// 销毁所有管理的刚体
for (auto body : _managedBodies) {
if (body && _world) {
// 清理用户数据
if (body->GetUserData()) {
delete static_cast<std::string*>(body->GetUserData());
}
// 清理夹具用户数据
b2Fixture* fixture = body->GetFixtureList();
while (fixture) {
if (fixture->GetUserData()) {
delete static_cast<std::string*>(fixture->GetUserData());
}
fixture = fixture->GetNext();
}
_world->DestroyBody(body);
}
}
_managedBodies.clear();
_bodyTags.clear();
}
b2Vec2 RigidBodyManager::pixelToMeter(const Vec2& pixel) const {
return b2Vec2(pixel.x / _pixelToMeterRatio, pixel.y / _pixelToMeterRatio);
}
Vec2 RigidBodyManager::meterToPixel(const b2Vec2& meter) const {
return Vec2(meter.x * _pixelToMeterRatio, meter.y * _pixelToMeterRatio);
}
b2Shape* RigidBodyManager::createShapeFromType(const std::string& type, const Size& size,
float radius, const std::vector<Vec2>& vertices) {
b2Shape* shape = nullptr;
if (type == "circle") {
auto circle = new b2CircleShape();
circle->m_radius = radius / _pixelToMeterRatio;
shape = circle;
} else if (type == "box") {
auto box = new b2PolygonShape();
box->SetAsBox(
(size.width / 2.0f) / _pixelToMeterRatio,
(size.height / 2.0f) / _pixelToMeterRatio
);
shape = box;
} else if (type == "polygon" && vertices.size() >= 3) {
auto poly = new b2PolygonShape();
b2Vec2* b2Vertices = new b2Vec2[vertices.size()];
for (size_t i = 0; i < vertices.size(); ++i) {
b2Vertices[i] = pixelToMeter(vertices[i]);
}
poly->Set(b2Vertices, vertices.size());
delete[] b2Vertices;
shape = poly;
}
return shape;
}
运行结果
预期行为
-
地面、平台、墙壁等静态几何体保持不动 -
动态物体受重力影响下落并与静态几何体碰撞 -
不同形状的物体表现出不同的碰撞和滚动特性 -
物体可以在平台上停留、滑动或反弹
-
用户可以生成各种形状的动态物体 -
物体受重力影响自然下落和碰撞 -
可以开关重力观察无重力环境下的行为 -
物体间碰撞产生真实的物理反应 -
性能监控显示实时的FPS和物体数量
-
移动平台做往复水平运动,承载物体一起移动 -
旋转刀片持续转动,推动接触的物体 -
传送带给予物体持续的水平速度 -
电梯做垂直升降运动,运送物体上下 -
可以独立控制每个运动学刚体的开关
性能指标
-
帧率稳定性:在100+动态物体的情况下维持60FPS -
内存使用:每100个刚体约占用25MB内存 -
响应延迟:用户操作到物理响应的延迟<16ms -
CPU占用:物理模拟CPU占用率<20%(桌面端) -
电池续航:移动设备上连续运行2小时无明显发热
测试步骤
总结
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)