如何通过父类Shape访问子类Circle/Square的成员变量?
针对2D物理引擎形状属性访问的解决方案
方法1:虚函数封装碰撞逻辑(推荐方案)
把碰撞检测逻辑封装到各Shape子类中,基类定义纯虚接口,Body只需调用接口即可,无需直接访问子类属性,符合面向对象的开闭原则。
示例代码:
#include <memory> // 基础2D向量类型 struct Vec2 { float x, y; float lengthSq() const { return x*x + y*y; } Vec2 operator-(const Vec2& other) const { return {x-other.x, y-other.y}; } }; class Body; class Shape { public: virtual ~Shape() = default; // 纯虚函数:检测与另一个Body的形状是否碰撞 virtual bool collidesWith(const Body* other) const = 0; // 获取所属Body(用于获取位置) virtual const Body* getBody() const = 0; // 形状类型枚举,用于子类判断对方类型 enum Type { Circle, Square }; virtual Type getType() const = 0; }; class Body { public: Body(std::unique_ptr<Shape> shape, Vec2 pos) : m_shape(std::move(shape)), m_position(pos) {} const Vec2& getPosition() const { return m_position; } const Shape* getShape() const { return m_shape.get(); } // Body的碰撞检测入口 bool collidesWith(const Body* other) const { return m_shape->collidesWith(other); } private: std::unique_ptr<Shape> m_shape; Vec2 m_position; }; class Circle : public Shape { public: Circle(float radius, const Body* body) : m_radius(radius), m_body(body) {} Type getType() const override { return Circle; } const Body* getBody() const override { return m_body; } bool collidesWith(const Body* other) const override { const Shape* otherShape = other->getShape(); if (otherShape->getType() == Circle) { const Circle* otherCircle = static_cast<const Circle*>(otherShape); // 两圆碰撞检测:距离平方 <= 半径和平方 Vec2 delta = getBody()->getPosition() - other->getPosition(); float distSq = delta.lengthSq(); float radiusSum = m_radius + otherCircle->m_radius; return distSq <= radiusSum * radiusSum; } else if (otherShape->getType() == Square) { const Square* otherSquare = static_cast<const Square*>(otherShape); // 实现圆与正方形的碰撞逻辑,可直接访问m_radius和otherSquare->m_side_length return true; // 替换为实际逻辑 } return false; } float m_radius; private: const Body* m_body; }; class Square : public Shape { public: Square(float sideLength, const Body* body) : m_side_length(sideLength), m_body(body) {} Type getType() const override { return Square; } const Body* getBody() const override { return m_body; } bool collidesWith(const Body* other) const override { const Shape* otherShape = other->getShape(); if (otherShape->getType() == Circle) { const Circle* otherCircle = static_cast<const Circle*>(otherShape); // 实现正方形与圆的碰撞逻辑,访问m_side_length和otherCircle->m_radius return true; } else if (otherShape->getType() == Square) { const Square* otherSquare = static_cast<const Square*>(otherShape); // 正方形碰撞检测逻辑 return true; } return false; } float m_side_length; private: const Body* m_body; };
方法2:动态类型转换(快速实现方案)
如果需要在外部直接访问子类属性,可使用dynamic_cast判断形状类型并转换,适合小型项目快速迭代。
示例代码(Body类的碰撞检测方法):
bool Body::collidesWith(const Body* other) const { const Shape* s1 = m_shape.get(); const Shape* s2 = other->getShape(); // 圆-圆碰撞 if (const Circle* c1 = dynamic_cast<const Circle*>(s1)) { if (const Circle* c2 = dynamic_cast<const Circle*>(s2)) { Vec2 delta = getPosition() - other->getPosition(); float distSq = delta.lengthSq(); float radiusSum = c1->m_radius + c2->m_radius; return distSq <= radiusSum * radiusSum; } } // 圆-正方形碰撞 if (const Circle* circle = dynamic_cast<const Circle*>(s1)) { if (const Square* square = dynamic_cast<const Square*>(s2)) { // 圆与正方形碰撞逻辑,访问circle->m_radius和square->m_side_length return true; } } // 正方形-圆碰撞 if (const Square* square = dynamic_cast<const Square*>(s1)) { if (const Circle* circle = dynamic_cast<const Circle*>(s2)) { // 复用圆-正方形逻辑或单独实现 return true; } } // 正方形-正方形碰撞 if (const Square* sq1 = dynamic_cast<const Square*>(s1)) { if (const Square* sq2 = dynamic_cast<const Square*>(s2)) { // 正方形碰撞逻辑,访问sq1->m_side_length和sq2->m_side_length return true; } } return false; }
方法3:访问者模式(复杂形状体系方案)
若后续会添加大量形状类型,访问者模式可避免频繁修改碰撞逻辑,将检测逻辑分离到访问者类中。
示例代码:
// 访问者接口 class ShapeVisitor { public: virtual ~ShapeVisitor() = default; virtual void visit(const Circle* circle, const Body* other) = 0; virtual void visit(const Square* square, const Body* other) = 0; }; // 碰撞检测访问者 class CollisionChecker : public ShapeVisitor { public: CollisionChecker() : m_result(false) {} bool getResult() const { return m_result; } void visit(const Circle* circle, const Body* other) override { const Shape* otherShape = other->getShape(); if (const Circle* otherCircle = dynamic_cast<const Circle*>(otherShape)) { Vec2 delta = circle->getBody()->getPosition() - other->getPosition(); float distSq = delta.lengthSq(); float radiusSum = circle->m_radius + otherCircle->m_radius; m_result = distSq <= radiusSum * radiusSum; } else if (const Square* otherSquare = dynamic_cast<const Square*>(otherShape)) { // 圆-正方形碰撞逻辑 m_result = true; } } void visit(const Square* square, const Body* other) override { const Shape* otherShape = other->getShape(); if (const Circle* otherCircle = dynamic_cast<const Circle*>(otherShape)) { // 正方形-圆碰撞逻辑 m_result = true; } else if (const Square* otherSquare = dynamic_cast<const Square*>(otherShape)) { // 正方形-正方形碰撞逻辑 m_result = true; } } private: bool m_result; }; // 修改Shape基类,添加accept方法 class Shape { public: virtual ~Shape() = default; virtual void accept(ShapeVisitor& visitor, const Body* other) const = 0; virtual const Body* getBody() const = 0; enum Type { Circle, Square }; virtual Type getType() const = 0; }; // Circle类实现accept void Circle::accept(ShapeVisitor& visitor, const Body* other) const { visitor.visit(this, other); } // Square类实现accept void Square::accept(ShapeVisitor& visitor, const Body* other) const { visitor.visit(this, other); } // Body的碰撞检测调用 bool Body::collidesWith(const Body* other) const { CollisionChecker checker; m_shape->accept(checker, other); return checker.getResult(); }
内容的提问来源于stack exchange,提问作者bboan
相关产品推荐
相关产品推荐

