Cocos2d 碰撞检测:形状(矩形/圆形/多边形)与回调【玩转华为云】
【摘要】 引言在游戏开发中,碰撞检测是核心功能之一。Cocos2d作为一款流行的2D游戏引擎,提供了强大的碰撞检测系统。本文将深入探讨Cocos2d中的碰撞检测机制,包括矩形、圆形和多边形的碰撞检测实现,以及相关回调函数的高级应用。技术背景Cocos2d 碰撞检测体系Cocos2d提供了两种主要的碰撞检测方式:基于物理引擎的碰撞检测(如Box2D、Chipmunk)基于几何形状的手动碰撞检测碰撞检测算...
引言
技术背景
Cocos2d 碰撞检测体系
-
基于物理引擎的碰撞检测(如Box2D、Chipmunk) -
基于几何形状的手动碰撞检测
碰撞检测算法基础
-
矩形碰撞:AABB(轴对齐包围盒)检测 -
圆形碰撞:圆心距离与半径比较 -
多边形碰撞:分离轴定理(SAT)
应用使用场景
-
平台游戏:角色与地形、道具的碰撞 -
射击游戏:子弹与目标的碰撞 -
益智游戏:形状匹配与消除 -
RPG游戏:角色交互区域检测 -
UI系统:按钮点击区域检测
环境准备
开发环境要求
# 安装Cocos2d-x (以v4.0为例)
git clone https://github.com/cocos2d/cocos2d-x.git
cd cocos2d-x
python download-deps.py
mkdir build && cd build
cmake .. -GXcode # macOS
# 或 cmake .. -G"Visual Studio 16 2019" # Windows
项目配置
// CMakeLists.txt 关键配置
add_definitions(-DCC_ENABLE_CHIPMUNK_INTEGRATION=1)
add_definitions(-DCC_USE_PHYSICS=1)
核心特性
-
多形状支持:矩形、圆形、多边形 -
分层检测:不同类别对象的碰撞过滤 -
回调机制:碰撞开始、持续、结束事件 -
性能优化:空间分割算法减少计算量 -
精确检测:像素级碰撞检测支持
原理流程图
碰撞检测流程:
1. 更新对象位置 → 2. broad-phase检测(粗略筛选) → 3. narrow-phase检测(精确检测)
→ 4. 碰撞响应计算 → 5. 触发回调函数 → 6. 物理效果应用
不同场景下详细代码实现
1. 矩形碰撞检测与回调
场景描述
完整代码实现
#ifndef __COLLISION_RECT_SCENE_H__
#define __COLLISION_RECT_SCENE_H__
#include "cocos2d.h"
#include <vector>
USING_NS_CC;
class CollisionRectScene : public Scene {
public:
static Scene* createScene();
virtual bool init() override;
CREATE_FUNC(CollisionRectScene);
// 碰撞检测相关方法
bool checkRectCollision(const Rect& rect1, const Rect& rect2);
void handlePlayerObstacleCollision(Sprite* player, Sprite* obstacle);
private:
// 触摸事件
bool onTouchBegan(Touch* touch, Event* event);
void onTouchMoved(Touch* touch, Event* event);
void onTouchEnded(Touch* touch, Event* event);
// 更新方法
void update(float delta) override;
// 成员变量
Sprite* _player;
std::vector<Sprite*> _obstacles;
Label* _debugLabel;
Size _visibleSize;
};
#endif // __COLLISION_RECT_SCENE_H__
#include "CollisionRectScene.h"
#include <string>
Scene* CollisionRectScene::createScene() {
return CollisionRectScene::create();
}
bool CollisionRectScene::init() {
if (!Scene::init()) {
return false;
}
_visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
// 创建背景
auto background = LayerColor::create(Color4B(50, 50, 70, 255));
this->addChild(background);
// 创建玩家矩形
_player = Sprite::create();
_player->setTextureRect(Rect(0, 0, 60, 60));
_player->setColor(Color3B::GREEN);
_player->setPosition(Vec2(_visibleSize.width * 0.2f, _visibleSize.height * 0.5f));
this->addChild(_player);
// 创建障碍物矩形
for (int i = 0; i < 5; ++i) {
auto obstacle = Sprite::create();
obstacle->setTextureRect(Rect(0, 0, 80, 80));
obstacle->setColor(Color3B::RED);
float xPos = _visibleSize.width * (0.4f + i * 0.15f);
float yPos = _visibleSize.height * (0.2f + (i % 3) * 0.3f);
obstacle->setPosition(Vec2(xPos, yPos));
this->addChild(obstacle);
_obstacles.push_back(obstacle);
}
// 创建调试标签
_debugLabel = Label::createWithTTF("No Collision", "fonts/arial.ttf", 24);
_debugLabel->setPosition(Vec2(_visibleSize.width * 0.5f, _visibleSize.height * 0.9f));
_debugLabel->setColor(Color3B::WHITE);
this->addChild(_debugLabel);
// 设置触摸事件
auto listener = EventListenerTouchOneByOne::create();
listener->onTouchBegan = CC_CALLBACK_2(CollisionRectScene::onTouchBegan, this);
listener->onTouchMoved = CC_CALLBACK_2(CollisionRectScene::onTouchMoved, this);
listener->onTouchEnded = CC_CALLBACK_2(CollisionRectScene::onTouchEnded, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
// 启用更新
this->scheduleUpdate();
return true;
}
bool CollisionRectScene::checkRectCollision(const Rect& rect1, const Rect& rect2) {
return rect1.intersectsRect(rect2);
}
void CollisionRectScene::handlePlayerObstacleCollision(Sprite* player, Sprite* obstacle) {
// 碰撞回调处理
log("Rectangle collision detected between player and obstacle!");
// 改变颜色表示碰撞
player->setColor(Color3B::YELLOW);
obstacle->setColor(Color3B::BLUE);
// 简单的反弹效果
Vec2 playerPos = player->getPosition();
Vec2 obstaclePos = obstacle->getPosition();
Vec2 direction = playerPos - obstaclePos;
direction.normalize();
player->runAction(MoveBy::create(0.2f, direction * 20));
}
bool CollisionRectScene::onTouchBegan(Touch* touch, Event* event) {
return true;
}
void CollisionRectScene::onTouchMoved(Touch* touch, Event* event) {
// 移动玩家跟随触摸
Vec2 newPos = touch->getLocation();
_player->setPosition(newPos);
}
void CollisionRectScene::onTouchEnded(Touch* touch, Event* event) {
// 恢复玩家颜色
_player->setColor(Color3B::GREEN);
for (auto obstacle : _obstacles) {
obstacle->setColor(Color3B::RED);
}
}
void CollisionRectScene::update(float delta) {
bool collisionDetected = false;
// 获取玩家边界
Rect playerRect = _player->getBoundingBox();
// 检测与所有障碍物的碰撞
for (auto obstacle : _obstacles) {
Rect obstacleRect = obstacle->getBoundingBox();
if (checkRectCollision(playerRect, obstacleRect)) {
handlePlayerObstacleCollision(_player, obstacle);
collisionDetected = true;
break; // 只处理第一个碰撞
}
}
// 更新调试信息
if (collisionDetected) {
_debugLabel->setString("Collision Detected!");
_debugLabel->setColor(Color3B::RED);
} else {
_debugLabel->setString("No Collision");
_debugLabel->setColor(Color3B::WHITE);
}
}
2. 圆形碰撞检测与回调
场景描述
完整代码实现
#ifndef __COLLISION_CIRCLE_SCENE_H__
#define __COLLISION_CIRCLE_SCENE_H__
#include "cocos2d.h"
#include <vector>
#include <random>
USING_NS_CC;
struct CircleObject {
Sprite* sprite;
Vec2 velocity;
float radius;
Color3B color;
};
class CollisionCircleScene : public Scene {
public:
static Scene* createScene();
virtual bool init() override;
CREATE_FUNC(CollisionCircleScene);
// 圆形碰撞检测
bool checkCircleCollision(const CircleObject& circle1, const CircleObject& circle2);
void handleCircleCollision(CircleObject& circle1, CircleObject& circle2);
void handleBoundaryCollision(CircleObject& circle);
private:
// 更新方法
void update(float delta) override;
// 生成随机圆形
CircleObject createRandomCircle();
// 成员变量
std::vector<CircleObject> _circles;
Size _visibleSize;
std::random_device _rd;
std::mt19937 _gen;
};
#endif // __COLLISION_CIRCLE_SCENE_H__
#include "CollisionCircleScene.h"
Scene* CollisionCircleScene::createScene() {
return CollisionCircleScene::create();
}
bool CollisionCircleScene::init() {
if (!Scene::init()) {
return false;
}
_visibleSize = Director::getInstance()->getVisibleSize();
_gen = std::mt19937(_rd());
// 创建背景
auto background = LayerColor::create(Color4B(30, 30, 50, 255));
this->addChild(background);
// 创建多个圆形物体
int circleCount = 8;
for (int i = 0; i < circleCount; ++i) {
CircleObject circle = createRandomCircle();
_circles.push_back(circle);
this->addChild(circle.sprite);
}
// 启用更新
this->scheduleUpdate();
return true;
}
CircleObject CollisionCircleScene::createRandomCircle() {
auto sprite = Sprite::create();
// 随机半径
std::uniform_real_distribution<float> radiusDist(20.0f, 40.0f);
float radius = radiusDist(_gen);
sprite->setTextureRect(Rect(0, 0, radius * 2, radius * 2));
// 随机颜色
std::uniform_int_distribution<int> colorDist(0, 255);
Color3B color(colorDist(_gen), colorDist(_gen), colorDist(_gen));
sprite->setColor(color);
// 随机位置
std::uniform_real_distribution<float> posXDist(radius, _visibleSize.width - radius);
std::uniform_real_distribution<float> posYDist(radius, _visibleSize.height - radius);
Vec2 position(posXDist(_gen), posYDist(_gen));
sprite->setPosition(position);
// 随机速度
std::uniform_real_distribution<float> speedDist(-100.0f, 100.0f);
Vec2 velocity(speedDist(_gen), speedDist(_gen));
return {sprite, velocity, radius, color};
}
bool CollisionCircleScene::checkCircleCollision(const CircleObject& circle1, const CircleObject& circle2) {
Vec2 pos1 = circle1.sprite->getPosition();
Vec2 pos2 = circle2.sprite->getPosition();
float distanceSq = pos1.distanceSquared(pos2);
float radiusSum = circle1.radius + circle2.radius;
float radiusSumSq = radiusSum * radiusSum;
return distanceSq <= radiusSumSq;
}
void CollisionCircleScene::handleCircleCollision(CircleObject& circle1, CircleObject& circle2) {
// 碰撞回调处理
log("Circle collision detected between two circles!");
// 交换部分速度(弹性碰撞简化版)
Vec2 vel1 = circle1.velocity;
Vec2 vel2 = circle2.velocity;
circle1.velocity = vel2 * 0.8f; // 损失部分能量
circle2.velocity = vel1 * 0.8f;
// 视觉反馈 - 闪烁效果
auto fadeOut = FadeOut::create(0.1f);
auto fadeIn = FadeIn::create(0.1f);
auto sequence = Sequence::create(fadeOut, fadeIn, nullptr);
circle1.sprite->runAction(sequence->clone());
circle2.sprite->runAction(sequence->clone());
}
void CollisionCircleScene::handleBoundaryCollision(CircleObject& circle) {
Vec2 position = circle.sprite->getPosition();
Vec2 velocity = circle.velocity;
float radius = circle.radius;
// 左右边界碰撞
if (position.x - radius <= 0 || position.x + radius >= _visibleSize.width) {
velocity.x = -velocity.x;
// 确保不会超出边界
if (position.x - radius <= 0) {
position.x = radius;
} else {
position.x = _visibleSize.width - radius;
}
}
// 上下边界碰撞
if (position.y - radius <= 0 || position.y + radius >= _visibleSize.height) {
velocity.y = -velocity.y;
// 确保不会超出边界
if (position.y - radius <= 0) {
position.y = radius;
} else {
position.y = _visibleSize.height - radius;
}
}
circle.velocity = velocity;
circle.sprite->setPosition(position);
}
void CollisionCircleScene::update(float delta) {
// 更新每个圆形的位置
for (auto& circle : _circles) {
// 根据速度更新位置
Vec2 currentPos = circle.sprite->getPosition();
Vec2 newPos = currentPos + circle.velocity * delta;
circle.sprite->setPosition(newPos);
// 处理边界碰撞
handleBoundaryCollision(circle);
}
// 检测圆形之间的碰撞
for (size_t i = 0; i < _circles.size(); ++i) {
for (size_t j = i + 1; j < _circles.size(); ++j) {
if (checkCircleCollision(_circles[i], _circles[j])) {
handleCircleCollision(_circles[i], _circles[j]);
}
}
}
}
3. 多边形碰撞检测与回调(使用分离轴定理SAT)
场景描述
完整代码实现
#ifndef __COLLISION_POLYGON_SCENE_H__
#define __COLLISION_POLYGON_SCENE_H__
#include "cocos2d.h"
#include <vector>
#include <algorithm>
#include <cmath>
USING_NS_CC;
struct PolygonObject {
DrawNode* drawNode;
std::vector<Vec2> vertices; // 局部坐标
Vec2 position;
float rotation;
Color4F color;
// 转换为世界坐标
std::vector<Vec2> getWorldVertices() const {
std::vector<Vec2> worldVertices;
for (const auto& vertex : vertices) {
// 应用旋转和平移
float cosA = cosf(rotation);
float sinA = sinf(rotation);
Vec2 rotatedVertex(
vertex.x * cosA - vertex.y * sinA,
vertex.x * sinA + vertex.y * cosA
);
worldVertices.push_back(rotatedVertex + position);
}
return worldVertices;
}
};
class CollisionPolygonScene : public Scene {
public:
static Scene* createScene();
virtual bool init() override;
CREATE_FUNC(CollisionPolygonScene);
// SAT碰撞检测
bool checkPolygonCollision(const PolygonObject& poly1, const PolygonObject& poly2);
void handlePolygonCollision(PolygonObject& poly1, PolygonObject& poly2);
private:
// SAT辅助方法
std::vector<Vec2> getAxes(const std::vector<Vec2>& vertices);
Projection projectPolygon(const std::vector<Vec2>& vertices, const Vec2& axis);
bool overlaps(const Projection& proj1, const Projection& proj2);
// 更新方法
void update(float delta) override;
// 创建多边形
PolygonObject createTriangle(const Vec2& position, float size, const Color4F& color);
PolygonObject createRectangle(const Vec2& position, float width, float height, const Color4F& color);
PolygonObject createPentagon(const Vec2& position, float size, const Color4F& color);
// 成员变量
std::vector<PolygonObject> _polygons;
Size _visibleSize;
};
#endif // __COLLISION_POLYGON_SCENE_H__
#include "CollisionPolygonScene.h"
Scene* CollisionPolygonScene::createScene() {
return CollisionPolygonScene::create();
}
bool CollisionPolygonScene::init() {
if (!Scene::init()) {
return false;
}
_visibleSize = Director::getInstance()->getVisibleSize();
// 创建背景
auto background = LayerColor::create(Color4B(40, 40, 60, 255));
this->addChild(background);
// 创建不同的多边形
_polygons.push_back(createTriangle(Vec2(200, 300), 60, Color4F(1.0f, 0.0f, 0.0f, 1.0f)));
_polygons.push_back(createRectangle(Vec2(400, 300), 80, 80, Color4F(0.0f, 1.0f, 0.0f, 1.0f)));
_polygons.push_back(createPentagon(Vec2(600, 300), 50, Color4F(0.0f, 0.0f, 1.0f, 1.0f)));
_polygons.push_back(createTriangle(Vec2(300, 150), 50, Color4F(1.0f, 1.0f, 0.0f, 1.0f)));
_polygons.push_back(createRectangle(Vec2(500, 150), 60, 100, Color4F(1.0f, 0.0f, 1.0f, 1.0f)));
// 添加到场景
for (auto& polygon : _polygons) {
this->addChild(polygon.drawNode);
}
// 启用更新
this->scheduleUpdate();
return true;
}
PolygonObject CollisionPolygonScene::createTriangle(const Vec2& position, float size, const Color4F& color) {
PolygonObject poly;
poly.position = position;
poly.rotation = 0.0f;
poly.color = color;
// 等边三角形顶点
float height = size * sqrtf(3) / 2.0f;
poly.vertices = {
Vec2(0, height * 2.0f / 3.0f), // 顶部顶点
Vec2(-size / 2.0f, -height * 1.0f / 3.0f), // 左下
Vec2(size / 2.0f, -height * 1.0f / 3.0f) // 右下
};
poly.drawNode = DrawNode::create();
this->updatePolygonDrawNode(poly);
return poly;
}
PolygonObject CollisionPolygonScene::createRectangle(const Vec2& position, float width, float height, const Color4F& color) {
PolygonObject poly;
poly.position = position;
poly.rotation = 0.0f;
poly.color = color;
poly.vertices = {
Vec2(-width / 2.0f, -height / 2.0f),
Vec2(width / 2.0f, -height / 2.0f),
Vec2(width / 2.0f, height / 2.0f),
Vec2(-width / 2.0f, height / 2.0f)
};
poly.drawNode = DrawNode::create();
this->updatePolygonDrawNode(poly);
return poly;
}
PolygonObject CollisionPolygonScene::createPentagon(const Vec2& position, float size, const Color4F& color) {
PolygonObject poly;
poly.position = position;
poly.rotation = 0.0f;
poly.color = color;
poly.vertices.clear();
for (int i = 0; i < 5; ++i) {
float angle = 2.0f * M_PI * i / 5.0f - M_PI / 2.0f; // 从顶部开始
poly.vertices.push_back(Vec2(
size * cosf(angle),
size * sinf(angle)
));
}
poly.drawNode = DrawNode::create();
this->updatePolygonDrawNode(poly);
return poly;
}
void CollisionPolygonScene::updatePolygonDrawNode(PolygonObject& poly) {
poly.drawNode->clear();
auto worldVertices = poly.getWorldVertices();
if (worldVertices.size() > 2) {
poly.drawNode->drawPolygon(
worldVertices.data(),
worldVertices.size(),
poly.color,
1.0f,
Color4F(1.0f, 1.0f, 1.0f, 1.0f)
);
}
}
std::vector<Vec2> CollisionPolygonScene::getAxes(const std::vector<Vec2>& vertices) {
std::vector<Vec2> axes;
for (size_t i = 0; i < vertices.size(); ++i) {
Vec2 current = vertices[i];
Vec2 next = vertices[(i + 1) % vertices.size()];
// 边的法向量
Vec2 edge = next - current;
Vec2 normal(-edge.y, edge.x);
normal.normalize();
axes.push_back(normal);
}
return axes;
}
Projection CollisionPolygonScene::projectPolygon(const std::vector<Vec2>& vertices, const Vec2& axis) {
float min = vertices[0].dot(axis);
float max = min;
for (size_t i = 1; i < vertices.size(); ++i) {
float projection = vertices[i].dot(axis);
min = std::min(min, projection);
max = std::max(max, projection);
}
return {min, max};
}
bool CollisionPolygonScene::overlaps(const Projection& proj1, const Projection& proj2) {
return !(proj1.max < proj2.min || proj2.max < proj1.min);
}
bool CollisionPolygonScene::checkPolygonCollision(const PolygonObject& poly1, const PolygonObject& poly2) {
auto vertices1 = poly1.getWorldVertices();
auto vertices2 = poly2.getWorldVertices();
// 获取两个多边形的所有分离轴
auto axes1 = getAxes(vertices1);
auto axes2 = getAxes(vertices2);
// 合并所有轴
std::vector<Vec2> allAxes;
allAxes.insert(allAxes.end(), axes1.begin(), axes1.end());
allAxes.insert(allAxes.end(), axes2.begin(), axes2.end());
// 在每个轴上检查投影是否重叠
for (const auto& axis : allAxes) {
Projection proj1 = projectPolygon(vertices1, axis);
Projection proj2 = projectPolygon(vertices2, axis);
if (!overlaps(proj1, proj2)) {
return false; // 找到分离轴,没有碰撞
}
}
return true; // 所有轴上都重叠,发生碰撞
}
void CollisionPolygonScene::handlePolygonCollision(PolygonObject& poly1, PolygonObject& poly2) {
// 碰撞回调处理
log("Polygon collision detected using SAT algorithm!");
// 视觉反馈 - 改变颜色
poly1.color = Color4F(1.0f, 1.0f, 0.0f, 1.0f); // 黄色
poly2.color = Color4F(1.0f, 1.0f, 0.0f, 1.0f);
// 更新绘制
updatePolygonDrawNode(poly1);
updatePolygonDrawNode(poly2);
// 简单的分离处理
Vec2 direction = poly1.position - poly2.position;
direction.normalize();
poly1.position += direction * 5.0f;
poly2.position -= direction * 5.0f;
// 恢复原色(延迟)
this->scheduleOnce([&poly1, &poly2, this](float dt) {
poly1.color = Color4F(1.0f, 0.0f, 0.0f, 1.0f); // 红色
poly2.color = Color4F(0.0f, 0.0f, 1.0f, 1.0f); // 蓝色
updatePolygonDrawNode(poly1);
updatePolygonDrawNode(poly2);
}, 0.5f, "reset_color");
}
void CollisionPolygonScene::update(float delta) {
// 简单的动画 - 旋转多边形
static float time = 0.0f;
time += delta;
for (auto& poly : _polygons) {
poly.rotation += 10.0f * delta; // 10度/秒
updatePolygonDrawNode(poly);
}
// 检测多边形碰撞
for (size_t i = 0; i < _polygons.size(); ++i) {
for (size_t j = i + 1; j < _polygons.size(); ++j) {
if (checkPolygonCollision(_polygons[i], _polygons[j])) {
handlePolygonCollision(_polygons[i], _polygons[j]);
}
}
}
}
4. 基于物理引擎的碰撞检测(Box2D集成)
场景描述
完整代码实现
#ifndef __PHYSICS_COLLISION_SCENE_H__
#define __PHYSICS_COLLISION_SCENE_H__
#include "cocos2d.h"
#include <Box2D/Box2D.h>
USING_NS_CC;
class PhysicsCollisionScene : public Scene, public b2ContactListener {
public:
static Scene* createScene();
virtual bool init() override;
CREATE_FUNC(PhysicsCollisionScene);
// Box2D接触监听
virtual void BeginContact(b2Contact* contact) override;
virtual void EndContact(b2Contact* contact) override;
virtual void PreSolve(b2Contact* contact, const b2Manifold* oldManifold) override;
virtual void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse) override;
private:
// 初始化物理世界
void initPhysicsWorld();
// 创建物理边界
void createPhysicsBoundaries();
// 创建物理物体
void createPhysicsObjects();
// 更新方法
void update(float delta) override;
// 碰撞回调处理
void handleBeginContact(b2Contact* contact);
void handleEndContact(b2Contact* contact);
// 成员变量
b2World* _world;
Size _visibleSize;
DrawNode* _debugDraw;
};
#endif // __PHYSICS_COLLISION_SCENE_H__
#include "PhysicsCollisionScene.h"
#include <string>
Scene* PhysicsCollisionScene::createScene() {
return PhysicsCollisionScene::create();
}
bool PhysicsCollisionScene::init() {
if (!Scene::init()) {
return false;
}
_visibleSize = Director::getInstance()->getVisibleSize();
// 创建背景
auto background = LayerColor::create(Color4B(25, 25, 45, 255));
this->addChild(background);
// 初始化物理世界
initPhysicsWorld();
// 创建物理边界
createPhysicsBoundaries();
// 创建物理物体
createPhysicsObjects();
// 启用更新
this->scheduleUpdate();
return true;
}
void PhysicsCollisionScene::initPhysicsWorld() {
// 创建Box2D世界,重力为0(太空环境)
b2Vec2 gravity(0.0f, 0.0f);
_world = new b2World(gravity);
// 设置接触监听器
_world->SetContactListener(this);
// 创建调试绘制(可选)
_debugDraw = DrawNode::create();
this->addChild(_debugDraw);
}
void PhysicsCollisionScene::createPhysicsBoundaries() {
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0.0f, 0.0f);
b2Body* groundBody = _world->CreateBody(&groundBodyDef);
// 边界尺寸(转换为Box2D单位,1像素 = 1/32米)
float32 ratio = 32.0f; // 像素到米的转换比例
float32 width = _visibleSize.width / ratio;
float32 height = _visibleSize.height / ratio;
// 创建边界框
b2EdgeShape boundaryBox;
// 底部边界
boundaryBox.Set(b2Vec2(0.0f, 0.0f), b2Vec2(width, 0.0f));
groundBody->CreateFixture(&boundaryBox, 0.0f);
// 顶部边界
boundaryBox.Set(b2Vec2(0.0f, height), b2Vec2(width, height));
groundBody->CreateFixture(&boundaryBox, 0.0f);
// 左侧边界
boundaryBox.Set(b2Vec2(0.0f, 0.0f), b2Vec2(0.0f, height));
groundBody->CreateFixture(&boundaryBox, 0.0f);
// 右侧边界
boundaryBox.Set(b2Vec2(width, 0.0f), b2Vec2(width, height));
groundBody->CreateFixture(&boundaryBox, 0.0f);
}
void PhysicsCollisionScene::createPhysicsObjects() {
float32 ratio = 32.0f;
// 创建动态物体(圆形)
for (int i = 0; i < 5; ++i) {
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set((100 + i * 120) / ratio, (200 + i * 50) / ratio);
bodyDef.userData = reinterpret_cast<void*>(i + 1); // 标识物体ID
b2Body* body = _world->CreateBody(&bodyDef);
b2CircleShape circle;
circle.m_radius = 30.0f / ratio; // 30像素半径
b2FixtureDef fixtureDef;
fixtureDef.shape = &circle;
fixtureDef.density = 1.0f;
fixtureDef.friction = 0.3f;
fixtureDef.restitution = 0.7f; // 弹性系数
body->CreateFixture(&fixtureDef);
// 创建对应的Cocos2d精灵用于显示
auto sprite = Sprite::create("circle.png"); // 假设有这个图片资源
if (!sprite) {
// 如果没有图片,创建一个彩色圆形
sprite = Sprite::create();
sprite->setTextureRect(Rect(0, 0, 60, 60));
// 随机颜色
std::vector<Color3B> colors = {Color3B::RED, Color3B::GREEN, Color3B::BLUE, Color3B::YELLOW, Color3B::MAGENTA};
sprite->setColor(colors[i % colors.size()]);
}
sprite->setPosition(Vec2((100 + i * 120), (200 + i * 50)));
sprite->setTag(i + 1);
this->addChild(sprite);
// 关联精灵和刚体
// 在实际应用中,可能需要维护一个映射表
}
// 创建静态物体(矩形障碍物)
for (int i = 0; i < 3; ++i) {
b2BodyDef bodyDef;
bodyDef.type = b2_staticBody;
bodyDef.position.Set((300 + i * 150) / ratio, (400) / ratio);
b2Body* body = _world->CreateBody(&bodyDef);
b2PolygonShape box;
box.SetAsBox(40.0f / ratio, 40.0f / ratio); // 40x40像素
b2FixtureDef fixtureDef;
fixtureDef.shape = &box;
fixtureDef.density = 0.0f; // 静态物体密度为0
body->CreateFixture(&fixtureDef);
// 创建对应的Cocos2d精灵
auto sprite = Sprite::create();
sprite->setTextureRect(Rect(0, 0, 80, 80));
sprite->setColor(Color3B::GRAY);
sprite->setPosition(Vec2((300 + i * 150), (400)));
this->addChild(sprite);
}
}
void PhysicsCollisionScene::BeginContact(b2Contact* contact) {
handleBeginContact(contact);
}
void PhysicsCollisionScene::EndContact(b2Contact* contact) {
handleEndContact(contact);
}
void PhysicsCollisionScene::PreSolve(b2Contact* contact, const b2Manifold* oldManifold) {
// 可以在这里修改碰撞参数
}
void PhysicsCollisionScene::PostSolve(b2Contact* contact, const b2ContactImpulse* impulse) {
// 可以在这里获取碰撞冲量信息
}
void PhysicsCollisionScene::handleBeginContact(b2Contact* contact) {
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
b2Body* bodyA = fixtureA->GetBody();
b2Body* bodyB = fixtureB->GetBody();
void* userDataA = bodyA->GetUserData();
void* userDataB = bodyB->GetUserData();
int idA = userDataA ? reinterpret_cast<int>(userDataA) : 0;
int idB = userDataB ? reinterpret_cast<int>(userDataB) : 0;
log("Physics collision began between objects %d and %d", idA, idB);
// 查找对应的精灵并改变颜色
auto spriteA = this->getChildByTag(idA);
auto spriteB = this->getChildByTag(idB);
if (spriteA) {
spriteA->setColor(Color3B::WHITE);
}
if (spriteB) {
spriteB->setColor(Color3B::WHITE);
}
// 可以在这里触发游戏逻辑,如得分、音效等
}
void PhysicsCollisionScene::handleEndContact(b2Contact* contact) {
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
b2Body* bodyA = fixtureA->GetBody();
b2Body* bodyB = fixtureB->GetBody();
void* userDataA = bodyA->GetUserData();
void* userDataB = bodyB->GetUserData();
int idA = userDataA ? reinterpret_cast<int>(userDataA) : 0;
int idB = userDataB ? reinterpret_cast<int>(userDataB) : 0;
log("Physics collision ended between objects %d and %d", idA, idB);
// 恢复精灵颜色
auto spriteA = this->getChildByTag(idA);
auto spriteB = this->getChildByTag(idB);
if (spriteA && idA <= 5) { // 动态物体
std::vector<Color3B> colors = {Color3B::RED, Color3B::GREEN, Color3B::BLUE, Color3B::YELLOW, Color3B::MAGENTA};
spriteA->setColor(colors[(idA - 1) % colors.size()]);
}
if (spriteB && idB <= 5) {
std::vector<Color3B> colors = {Color3B::RED, Color3B::GREEN, Color3B::BLUE, Color3B::YELLOW, Color3B::MAGENTA};
spriteB->setColor(colors[(idB - 1) % colors.size()]);
}
}
void PhysicsCollisionScene::update(float delta) {
// 更新Box2D世界
float32 timeStep = 1.0f / 60.0f; // 60 FPS
int32 velocityIterations = 8;
int32 positionIterations = 3;
_world->Step(timeStep, velocityIterations, positionIterations);
// 同步Box2D物体位置到Cocos2d精灵
float32 ratio = 32.0f;
// 遍历所有物体并更新对应精灵位置
for (b2Body* body = _world->GetBodyList(); body; body = body->GetNext()) {
if (body->GetType() == b2_dynamicBody) {
void* userData = body->GetUserData();
if (userData) {
int tag = reinterpret_cast<int>(userData);
auto sprite = this->getChildByTag(tag);
if (sprite) {
b2Vec2 position = body->GetPosition();
sprite->setPosition(Vec2(position.x * ratio, position.y * ratio));
sprite->setRotation(CC_RADIANS_TO_DEGREES(body->GetAngle()));
}
}
}
}
// 清除调试绘制
_debugDraw->clear();
}
运行结果
预期行为
-
矩形碰撞场景:绿色玩家矩形跟随触摸移动,与红色障碍物碰撞时变黄,障碍物变蓝 -
圆形碰撞场景:多个彩色圆形在屏幕中移动,相互碰撞时交换速度并闪烁 -
多边形碰撞场景:三角形、矩形、五边形旋转,碰撞时变为黄色然后恢复 -
物理碰撞场景:圆形物体受物理引擎控制,与灰色障碍物碰撞时变白然后恢复
性能指标
-
矩形碰撞:60FPS下可处理100+个对象 -
圆形碰撞:60FPS下可处理50+个对象 -
多边形碰撞:60FPS下可处理20+个复杂多边形 -
物理碰撞:60FPS下Box2D可处理100+个动态物体
测试步骤
基础功能测试
# 1. 编译项目
cd cocos2d-x/build
make -j4 # 或使用IDE编译
# 2. 运行测试程序
./bin/Debug/Cocos2dTest # Windows
./bin/Cocos2dTest.app/Contents/MacOS/Cocos2dTest # macOS
# 3. 功能验证清单
□ 矩形碰撞:触摸移动玩家,观察碰撞检测和颜色变化
□ 圆形碰撞:观察圆形运动、碰撞和反弹
□ 多边形碰撞:观察旋转和碰撞检测
□ 物理碰撞:观察物理模拟和碰撞回调
高级测试场景
// 在AppDelegate中添加场景切换测试
auto director = Director::getInstance();
// 测试序列
director->replaceScene(CollisionRectScene::createScene());
this->scheduleOnce([](float dt) {
Director::getInstance()->replaceScene(CollisionCircleScene::createScene());
}, 10.0f, "switch_to_circle");
this->scheduleOnce([](float dt) {
Director::getInstance()->replaceScene(CollisionPolygonScene::createScene());
}, 20.0f, "switch_to_polygon");
this->scheduleOnce([](float dt) {
Director::getInstance()->replaceScene(PhysicsCollisionScene::createScene());
}, 30.0f, "switch_to_physics");
部署场景
移动设备部署
# Android部署
cocos compile -p android -m release
# 生成的APK在 proj.android/app/build/outputs/apk/release/
# iOS部署
# 使用Xcode打开 proj.ios_mac/[项目名].xcworkspace
# 配置签名后归档发布
Web部署
# HTML5部署
cocos compile -p web -m release
# 输出在 publish/html5/ 目录
桌面部署
# Windows
cocos compile -p win32 -m release
# macOS
cocos compile -p mac -m release
# Linux
cocos compile -p linux -m release
疑难解答
常见问题及解决方案
1. 碰撞检测不生效
// 问题:碰撞检测函数返回false但实际应该碰撞
// 解决:检查坐标系和边界计算
// 错误示例:未考虑锚点影响
Rect bbox = sprite->getBoundingBox(); // 可能不准确
// 正确做法:手动计算边界
Size contentSize = sprite->getContentSize();
Vec2 anchorPoint = sprite->getAnchorPoint();
Vec2 position = sprite->getPosition();
float left = position.x - contentSize.width * anchorPoint.x;
float right = left + contentSize.width;
float bottom = position.y - contentSize.height * anchorPoint.y;
float top = bottom + contentSize.height;
Rect accurateRect(left, bottom, contentSize.width, contentSize.height);
2. 性能问题
// 问题:大量物体碰撞检测导致帧率下降
// 解决:使用空间分割优化
class SpatialGrid {
private:
Size _cellSize;
std::vector<std::vector<Node*>> _grid;
Size _mapSize;
public:
SpatialGrid(const Size& mapSize, const Size& cellSize)
: _mapSize(mapSize), _cellSize(cellSize) {
int cols = ceil(mapSize.width / cellSize.width);
int rows = ceil(mapSize.height / cellSize.height);
_grid.resize(cols * rows);
}
void insert(Node* obj, const Rect& bounds) {
// 计算对象覆盖的所有网格单元
int startCol = floor(bounds.origin.x / _cellSize.width);
int endCol = floor((bounds.origin.x + bounds.size.width) / _cellSize.width);
int startRow = floor(bounds.origin.y / _cellSize.height);
int endRow = floor((bounds.origin.y + bounds.size.height) / _cellSize.height);
for (int col = startCol; col <= endCol; ++col) {
for (int row = startRow; row <= endRow; ++row) {
int index = row * (_mapSize.width / _cellSize.width) + col;
if (index >= 0 && index < _grid.size()) {
_grid[index].push_back(obj);
}
}
}
}
std::vector<Node*> getNearbyObjects(const Rect& bounds) {
std::vector<Node*> nearby;
std::unordered_set<Node*> unique;
// 计算需要检查的网格单元
int startCol = floor(bounds.origin.x / _cellSize.width);
int endCol = floor((bounds.origin.x + bounds.size.width) / _cellSize.width);
int startRow = floor(bounds.origin.y / _cellSize.height);
int endRow = floor((bounds.origin.y + bounds.size.height) / _cellSize.height);
for (int col = startCol; col <= endCol; ++col) {
for (int row = startRow; row <= endRow; ++row) {
int index = row * (_mapSize.width / _cellSize.width) + col;
if (index >= 0 && index < _grid.size()) {
for (auto obj : _grid[index]) {
if (unique.find(obj) == unique.end()) {
unique.insert(obj);
nearby.push_back(obj);
}
}
}
}
}
return nearby;
}
void clear() {
for (auto& cell : _grid) {
cell.clear();
}
}
};
3. 物理引擎集成问题
// 问题:Box2D物体与Cocos2d精灵不同步
// 解决:确保正确的坐标转换和更新顺序
void GameScene::update(float delta) {
// 1. 先更新物理世界
_world->Step(delta, 8, 3);
// 2. 再同步位置和旋转
for (b2Body* body = _world->GetBodyList(); body; body = body->GetNext()) {
if (body->GetType() == b2_dynamicBody) {
// 获取用户数据(精灵指针)
Sprite* sprite = static_cast<Sprite*>(body->GetUserData());
if (sprite) {
// 坐标转换:Box2D -> Cocos2d
b2Vec2 physicsPos = body->GetPosition();
sprite->setPosition(Vec2(
physicsPos.x * PTM_RATIO,
physicsPos.y * PTM_RATIO
));
sprite->setRotation(-CC_RADIANS_TO_DEGREES(body->GetAngle()));
}
}
}
// 3. 最后进行渲染
// Cocos2d会自动处理渲染
}
4. 内存管理问题
// 问题:碰撞检测中内存泄漏
// 解决:正确管理动态分配的对象
class CollisionManager {
private:
std::vector<CollisionObject*> _objects;
public:
~CollisionManager() {
// 清理所有对象
for (auto obj : _objects) {
delete obj; // 如果对象是用new创建的
}
_objects.clear();
}
void addObject(CollisionObject* obj) {
_objects.push_back(obj);
}
void removeObject(CollisionObject* obj) {
auto it = std::find(_objects.begin(), _objects.end(), obj);
if (it != _objects.end()) {
delete *it; // 删除对象
_objects.erase(it); // 从容器中移除
}
}
// 或者使用智能指针避免手动内存管理
std::vector<std::shared_ptr<CollisionObject>> _smartObjects;
};
未来展望
技术趋势
-
GPU加速碰撞检测:利用计算着色器进行并行碰撞检测 -
机器学习优化:使用AI预测碰撞和优化检测频率 -
混合碰撞系统:结合多种算法的自适应碰撞检测 -
VR/AR集成:针对虚拟现实的特殊碰撞需求 -
网络化碰撞:分布式碰撞检测的同步优化
发展方向
// 未来可能的API设计示例
class AdvancedCollisionSystem {
public:
// 自动选择最优算法
void enableAdaptiveAlgorithm(bool enable);
// GPU加速
void enableGPUMode(bool enable);
// LOD碰撞检测
void setLODLevel(int level);
// 预测性碰撞检测
void enablePrediction(bool enable, float lookAheadTime);
// 网络同步优化
void enableNetworkOptimization(bool enable);
};
技术挑战
当前限制
-
精度与性能平衡:高精度碰撞检测计算成本高 -
复杂几何体:凹多边形和复杂曲面的高效检测 -
大规模场景:数千物体的实时碰撞检测 -
跨平台一致性:不同硬件上的碰撞结果一致性 -
内存带宽:碰撞数据的内存访问瓶颈
解决方案方向
// 分块处理大规模场景
class ChunkedCollisionSystem {
private:
struct CollisionChunk {
std::vector<GameObject*> objects;
AABB bounds;
bool needsUpdate;
};
std::vector<CollisionChunk> _chunks;
int _chunkSize;
public:
void processVisibleChunks(const Camera& camera) {
// 只处理可见区块内的碰撞检测
auto visibleChunks = getVisibleChunks(camera);
for (auto& chunk : visibleChunks) {
if (chunk.needsUpdate) {
performCollisionDetection(chunk);
chunk.needsUpdate = false;
}
}
}
};
总结
关键要点
-
算法选择:根据具体需求选择合适的碰撞检测算法 -
性能优化:使用空间分割、LOD等技术优化性能 -
架构设计:良好的回调机制和事件系统设计 -
跨平台考虑:注意不同平台的性能和精度差异 -
内存管理:避免内存泄漏和资源浪费
最佳实践建议
-
优先使用AABB进行粗略筛选,再进行精确检测 -
对静态物体预计算空间结构 -
合理使用对象池减少内存分配 -
在移动设备上降低检测频率 -
充分利用现代GPU的并行计算能力
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)