5.2 KiB
5.2 KiB
Extra2D API 教程 - 06. 碰撞检测
空间索引系统
Extra2D 内置了空间索引系统,用于高效地进行碰撞检测。
启用空间索引
void onEnter() override {
Scene::onEnter();
// 启用空间索引
setSpatialIndexingEnabled(true);
}
碰撞节点
创建可碰撞节点
class PhysicsNode : public Node {
public:
PhysicsNode(float size, const Color &color)
: size_(size), color_(color), isColliding_(false) {
// 启用空间索引(关键!)
setSpatialIndexed(true);
}
// 必须实现 getBoundingBox()
Rect getBoundingBox() const override {
Vec2 pos = getPosition();
return Rect(pos.x - size_ / 2, pos.y - size_ / 2, size_, size_);
}
void setColliding(bool colliding) { isColliding_ = colliding; }
void onRender(RenderBackend &renderer) override {
Vec2 pos = getPosition();
// 碰撞时变红色
Color fillColor = isColliding_ ? Color(1.0f, 0.2f, 0.2f, 0.9f) : color_;
renderer.fillRect(Rect(pos.x - size_ / 2, pos.y - size_ / 2, size_, size_),
fillColor);
}
private:
float size_;
Color color_;
bool isColliding_;
};
碰撞检测
查询所有碰撞
void performCollisionDetection() {
// 清除之前的碰撞状态
for (auto &node : nodes_) {
node->setColliding(false);
}
// 查询所有碰撞(使用空间索引)
auto collisions = queryCollisions();
// 标记碰撞的节点
for (const auto &[nodeA, nodeB] : collisions) {
if (auto boxA = dynamic_cast<PhysicsNode *>(nodeA)) {
boxA->setColliding(true);
}
if (auto boxB = dynamic_cast<PhysicsNode *>(nodeB)) {
boxB->setColliding(true);
}
}
}
空间索引策略
切换策略
void onEnter() override {
Scene::onEnter();
// 启用空间索引
setSpatialIndexingEnabled(true);
// 设置空间索引策略
auto &spatialManager = getSpatialManager();
spatialManager.setStrategy(SpatialStrategy::QuadTree); // 四叉树
// 或
spatialManager.setStrategy(SpatialStrategy::SpatialHash); // 空间哈希
}
// 切换策略
void toggleStrategy() {
auto &spatialManager = getSpatialManager();
SpatialStrategy current = spatialManager.getCurrentStrategy();
if (current == SpatialStrategy::QuadTree) {
spatialManager.setStrategy(SpatialStrategy::SpatialHash);
} else {
spatialManager.setStrategy(SpatialStrategy::QuadTree);
}
}
策略对比
| 策略 | 适用场景 | 特点 |
|---|---|---|
| QuadTree | 节点分布不均匀 | 适合稀疏场景 |
| SpatialHash | 节点分布均匀 | 适合密集场景 |
完整示例
class CollisionScene : public Scene {
public:
void onEnter() override {
Scene::onEnter();
// 启用空间索引
setSpatialIndexingEnabled(true);
// 创建碰撞节点
createNodes(100);
}
void onUpdate(float dt) override {
Scene::onUpdate(dt);
// 更新节点位置
for (auto &node : nodes_) {
node->update(dt);
}
// 执行碰撞检测
performCollisionDetection();
}
void onRender(RenderBackend &renderer) override {
Scene::onRender(renderer);
// 绘制碰撞统计
std::string text = "Collisions: " + std::to_string(collisionCount_);
// ...
}
private:
std::vector<Ptr<PhysicsNode>> nodes_;
size_t collisionCount_ = 0;
void createNodes(size_t count) {
for (size_t i = 0; i < count; ++i) {
auto node = makePtr<PhysicsNode>(20.0f, Color(0.5f, 0.5f, 0.9f, 0.7f));
node->setPosition(randomPosition());
addChild(node);
nodes_.push_back(node);
}
}
void performCollisionDetection() {
// 清除碰撞状态
for (auto &node : nodes_) {
node->setColliding(false);
}
// 查询碰撞
auto collisions = queryCollisions();
collisionCount_ = collisions.size();
// 标记碰撞节点
for (const auto &[nodeA, nodeB] : collisions) {
if (auto boxA = dynamic_cast<PhysicsNode *>(nodeA)) {
boxA->setColliding(true);
}
if (auto boxB = dynamic_cast<PhysicsNode *>(nodeB)) {
boxB->setColliding(true);
}
}
}
};
注意事项
必须调用 Scene::onEnter()
void onEnter() override {
Scene::onEnter(); // 必须调用!
// 否则子节点无法注册到空间索引
setSpatialIndexingEnabled(true);
}
必须实现 getBoundingBox()
class MyNode : public Node {
public:
MyNode() {
setSpatialIndexed(true);
}
// 必须实现!
Rect getBoundingBox() const override {
Vec2 pos = getPosition();
return Rect(pos.x - width_/2, pos.y - height_/2, width_, height_);
}
};