2D物理(12)Shape

By pocaster

刚体形状系统设计与实现


I. 形状类架构设计

1. 基类Shape:统一接口定义

class Shape {
public:
    virtual ~Shape() = default;
  
    // 计算几何属性
    virtual Vec2 ComputeCenterOfMass() const = 0;
    virtual float ComputeMomentOfInertia(float mass) const = 0;
  
    // 图形支持
    virtual void Render(const Transform& transform) const = 0;
  
    // 碰撞检测接口
    virtual bool CollideWith(const Shape* other, 
                            const Transform& this_tf,
                            const Transform& other_tf,
                            CollisionInfo& info) const = 0;

    // 形状类型标识
    enum Type { CIRCLE, POLYGON, BOX };
    virtual Type GetType() const = 0;
};

2. 继承体系

classDiagram
    class Shape{
        <<abstract>>
        +ComputeCenterOfMass()
        +ComputeMomentOfInertia()
        +Render()
        +CollideWith()
        +GetType()
    }
  
    class Circle{
        -float radius
        +GetType() CIRCLE
    }
  
    class Polygon{
        -vector<Vec2> local_vertices
        +GetType() POLYGON
    }
  
    class Box{
        -float width
        -float height
        +GetType() BOX
    }
  
    Shape <|-- Circle
    Shape <|-- Polygon
    Polygon <|-- Box

II. 圆形(Circle)类实现

1. 数据结构与构造函数

class Circle : public Shape {
public:
    explicit Circle(float r) : radius(r) {}

    Type GetType() const override { return CIRCLE; }
    float GetRadius() const { return radius; }

    // 几何属性计算
    Vec2 ComputeCenterOfMass() const override {
        return Vec2::Zero(); // 圆形质心始终在原点
    }

    float ComputeMomentOfInertia(float mass) const override {
        return 0.5f * mass * radius * radius; // I = ½mr²
    }

private:
    float radius;
};

2. 图形渲染实现

void Circle::Render(const Transform& tf) const {
    const Vec2 world_center = tf.position;
    DrawCircle(world_center, radius * tf.scale, tf.rotation_angle); 
}

III. 多边形(Polygon)基类实现

1. 核心数据结构

class Polygon : public Shape {
public:
    // 从顶点列表构造(自动计算包围盒)
    explicit Polygon(const vector<Vec2>& vertices) {
        local_vertices = vertices;
        ComputeConvexHull(); // 确保顶点按序排列
        CalculateAABB(); 
    }

    Type GetType() const override { return POLYGON; }

    // 访问顶点(局部坐标系)
    size_t GetVertexCount() const { return local_vertices.size(); }
    Vec2 GetVertex(int index) const { return local_vertices[index]; }

protected:
    vector<Vec2> local_vertices;
    AABB local_aabb; // 轴对齐包围盒(缓存优化)

private:
    void ComputeConvexHull(); // 凸包计算(具体实现参见Andrew算法)
    void CalculateAABB();     // 计算局部坐标包围盒
};

2. 质心与转动惯量计算

// 质心计算(按均匀密度假设)
Vec2 Polygon::ComputeCenterOfMass() const {
    float area_total = 0.0f;
    Vec2 center(0,0);
  
    // 通过三角分片累加质心
    const Vec2 p0 = local_vertices[0];
    for (int i=1; i<local_vertices.size()-1; ++i) {
        Vec2 p1 = local_vertices[i];
        Vec2 p2 = local_vertices[i+1];
      
        float area = 0.5f * Cross(p1-p0, p2-p0);
        center += (p0 + p1 + p2)/3.0f * area;
        area_total += area;
    }
  
    return center / area_total; 
}

// 转动惯量计算(绕质心)
float Polygon::ComputeMomentOfInertia(float mass) const {
    Vec2 com = ComputeCenterOfMass();
    float inertia = 0.0f;
  
    // 离散积分法(适用于任意凸多边形)
    for (int i=0; i<local_vertices.size(); ++i) {
        Vec2 p1 = local_vertices[i] - com;
        Vec2 p2 = local_vertices[(i+1)%local_vertices.size()] - com;
      
        float cross_term = Cross(p1, p2);
        inertia += cross_term * (Dot(p1,p1) + Dot(p1,p2) + Dot(p2,p2));
    }
  
    return (mass / (6.0f * Area())) * inertia; 
}

IV. 盒子(Box)类实现

1. 继承与构造优化

class Box : public Polygon {
public:
    Box(float w, float h) 
        : Polygon(GenerateVertices(w, h)), width(w), height(h) {}

    Type GetType() const override { return BOX; }

    // 便捷访问属性
    float GetWidth() const { return width * 2.0f; }  // 全宽
    float GetHeight() const { return height * 2.0f; } 

private:
    static vector<Vec2> GenerateVertices(float half_w, float half_h) {
        // 生成局部坐标系顶点(中心在原点)
        return {
            Vec2(-half_w, -half_h),
            Vec2( half_w, -half_h),
            Vec2( half_w,  half_h),
            Vec2(-half_w,  half_h)
        };
    }

    float width;   // 半宽
    float height;  // 半高
};

2. 盒子转动惯量优化

由于盒子是矩形的特例,转动惯量公式可优化为:

float Box::ComputeMomentOfInertia(float mass) override {
    return mass * (width*width + height*height) / 12.0f; // I = ¹⁄₁₂m(w²+h²)
}

V. 形状与刚体的关联

1. 扩展刚体数据结构

struct RigidBody {
    Transform transform;   // 包含位置+旋转
    Shape* shape;          // 持有的形状对象
  
    Vec2 velocity;         // 线速度
    float angular_vel;     // 角速度
    // ...其他成员同前
};

2. 世界坐标顶点转换

通过变换矩阵将局部顶点转为世界坐标:

vector<Vec2> GetWorldVertices(const Polygon* poly, const Transform& tf) {
    vector<Vec2> world_verts;
    for (const auto& v : poly->local_vertices) {
        // 应用缩放、旋转、平移
        Vec2 rotated = v.Rotate(tf.rotation_angle);
        world_verts.push_back(tf.position + rotated * tf.scale);
    }
    return world_verts;
}

VI. 形状工厂模式示例

1. 统一创建接口

unique_ptr<Shape> ShapeFactory::Create(ShapeType type, const ParamBundle& params) {
    switch(type) {
        case CIRCLE:
            return make_unique<Circle>(params.GetFloat("radius"));
        case BOX:
            return make_unique<Box>(params.GetFloat("width"), 
                                   params.GetFloat("height"));
        case POLYGON:
            return make_unique<Polygon>(params.GetVec2Array("vertices"));
        default:
            throw std::invalid_argument("Unknown shape type");
    }
}

VII. 碰撞检测基础框架

1. 类型分发处理

bool Shape::CollideWith(const Shape* other, 
                       const Transform& this_tf,
                       const Transform& other_tf,
                       CollisionInfo& info) const {
    // 类型分发矩阵
    const Type this_type = GetType();
    const Type other_type = other->GetType();
  
    if (this_type == CIRCLE && other_type == CIRCLE) {
        return CircleVsCircle(this, other, this_tf, other_tf, info);
    }
    else if ((this_type == CIRCLE && other_type == POLYGON) || 
            (this_type == POLYGON && other_type == CIRCLE)) {
        return CircleVsPolygon(this, other, this_tf, other_tf, info);
    }
    else if (this_type == POLYGON && other_type == POLYGON) {
        return PolygonVsPolygon(this, other, this_tf, other_tf, info);
    }
    return false;
}

VIII. 形状属性验证测试

1. 单元测试示例

TEST(ShapeTest, BoxInertia) {
    Box box(1.0f, 2.0f); // 宽2m,高4m
    float expected = (4.0f*4.0f + 16.0f)/12.0f; // (w²+h²)/12
    EXPECT_NEAR(box.ComputeMomentOfInertia(1.0f), expected, 0.001f);
}

TEST(ShapeTest, CircleCOM) {
    Circle circ(3.0f);
    EXPECT_EQ(circ.ComputeCenterOfMass(), Vec2(0,0));
}

IX. 性能优化策略

1. 快速计算优化表


操作 优化手段 适用场景
转动惯量计算 使用解析公式替代离散积分 标准形状(圆、矩形)
凸包生成 预计算+缓存 静态多边形
顶点变换 SIMD并行计算(如SSE指令集) 高频更新的动态物体
质心计算 在构造时缓存结果 不变形刚体

通过层次化设计形状系统,开发者可灵活扩展新形状类型,同时保持核心物理逻辑的统一性。下一步可重点实现分离轴定理(SAT)与GJK碰撞检测算法,完善多边形间的精确交互。