如何实现旋转扇形(Circle Slice)与固定矩形的碰撞检测?
解决旋转扇形与固定矩形的碰撞检测问题
你的代码核心问题是坐标系不匹配:扇形路径是在经过translate和rotate后的局部坐标系中创建的,但检测碰撞时用的是全局坐标系下的barrier矩形,导致path.contains(barrier)的判断完全错误。
解决思路
要正确检测碰撞,需要将固定矩形barrier转换到扇形所在的局部坐标系中,步骤如下:
- 在执行坐标系变换(translate/rotate)前,获取
painter的变换矩阵; - 将全局坐标系的
barrier矩形,通过逆变换转换到局部坐标系; - 用转换后的矩形与扇形路径做相交检测。
修改后的关键代码
在paintEvent中,调整碰撞检测的部分:
def paintEvent(self, event: QPaintEvent) -> super: # ... 其他代码保持不变 ... painter.setPen(QPen(QColor(20, 20, 20), -1, Qt.PenStyle.SolidLine)) painter.setBrush(QBrush(QColor(20, 20, 160), Qt.BrushStyle.SolidPattern)) barrier = QRectF(1920//2-25, 1080//2-25-40, 50, 20) painter.drawRect(barrier) # 保存初始变换状态,获取逆变换用于转换barrier painter.save() painter.translate(QPoint(1920//2, 1080//2)) painter.rotate(self.angle) # 获取当前变换的逆变换,将全局坐标的barrier转换到局部坐标系 transform = painter.worldTransform().inverted()[0] local_barrier = transform.mapRect(barrier) painter.setBrush(QBrush(QColor(200, 200, 200, 50), Qt.BrushStyle.SolidPattern)) r = 200 a = 40 b = a * 2 rect = QRectF(-r/2, -r/2, r, r) # 前视角扇形 path = QPainterPath() path.arcTo(rect, -a, b) path.closeSubpath() # 用转换后的local_barrier检测相交 if path.intersects(local_barrier): painter.setBrush(QBrush(QColor(200, 20, 20, 50), Qt.BrushStyle.SolidPattern)) else: painter.setBrush(QBrush(QColor(20, 200, 20, 50), Qt.BrushStyle.SolidPattern)) painter.drawPath(path) # 右视角扇形(原+90度) path = QPainterPath() path.arcTo(rect, -a+90, b) path.closeSubpath() if path.intersects(local_barrier): painter.setBrush(QBrush(QColor(200, 20, 20, 50), Qt.BrushStyle.SolidPattern)) else: painter.setBrush(QBrush(QColor(20, 200, 20, 50), Qt.BrushStyle.SolidPattern)) painter.drawPath(path) # 左视角扇形(原-90度) path = QPainterPath() path.arcTo(rect, -a-90, b) path.closeSubpath() if path.intersects(local_barrier): painter.setBrush(QBrush(QColor(200, 20, 20, 50), Qt.BrushStyle.SolidPattern)) else: painter.setBrush(QBrush(QColor(20, 200, 20, 50), Qt.BrushStyle.SolidPattern)) painter.drawPath(path) # ... 后续绘制代码保持不变 ... painter.restore() # 恢复初始变换状态 painter.end() self.angle += 1 if self.angle == 360: self.angle = 0 return super().paintEvent(event)
关键修改点说明
- 坐标系转换:通过
painter.worldTransform().inverted()获取当前变换的逆矩阵,将全局坐标系的barrier转换到旋转后的局部坐标系,得到local_barrier; - 碰撞检测方法:使用
path.intersects()替代path.contains(),因为contains()要求矩形完全在路径内,而intersects()只要有重叠就会返回True,更符合“相交/碰撞”的需求; - 状态保存与恢复:用
painter.save()和painter.restore()确保后续绘制不受当前变换影响,这是GUI绘制的良好习惯。
其他优化建议
- 可以将扇形的创建逻辑封装成函数,避免重复代码;
- 把窗口尺寸(1920、1080)定义为类变量,避免硬编码。
内容的提问来源于stack exchange,提问作者DSA5252
相关产品推荐
相关产品推荐

