0

我正在学习 Python,并想用 PyQ5t 制作一个简单的平台游戏。目前,当我想在游戏中的形状之间进行像素级碰撞检测时,我遇到了麻烦。我已将 QPixMap 设置为透明,并尝试使用 QGraphicsPixmapItem.HeuristicMaskShape 形状模式,但碰撞检测不起作用。

如果我将形状(在这种情况下为鼠标)背景设为灰色并删除形状模式,则会发生矩形碰撞检测。

我在这里缺少什么?我花了几个小时在互联网上挖掘,但还没有解决方案......

这是我显示问题的代码,请使用箭头键移动红色的“鼠标”:) 我希望在红色圆圈中的第一个像素接触棕色平台时看到碰撞检测文本。

import sys
from PyQt5.QtGui import QPen, QBrush
from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import Qt, QPoint
from PyQt5.QtWidgets import QApplication, QWidget, QGraphicsView, QGraphicsScene, QLabel, QGraphicsPixmapItem, QFrame

class Mouse(QGraphicsPixmapItem):

    def __init__(self, parent):
        super().__init__()
        self.canvas = QtGui.QPixmap(40,40)
        self.canvas.fill(Qt.transparent)
        self.setPixmap(self.canvas)
        self.x = 100
        self.y = 100
        self.setPos(self.x, self.y)
        self.setFlag(QGraphicsPixmapItem.ItemIsMovable)
        self.setFlag(QGraphicsPixmapItem.ItemIsFocusable)
        self.setShapeMode(QGraphicsPixmapItem.HeuristicMaskShape)
        self.setFocus()
        parent.addItem(self)

    def paint(self, painter, option, widget=None):
        super().paint(painter, option, widget)
        pen = QPen(Qt.black, 4, Qt.SolidLine)
        brush = QBrush(Qt.red, Qt.SolidPattern)
        painter.save()
        painter.setPen(pen)
        painter.setBrush(brush)
        painter.drawEllipse(QPoint(20,20),16,16)
        painter.restore()

    def keyPressEvent(self, e):
        if e.key() == Qt.Key_Right:
            self.x += 5
        if e.key() == Qt.Key_Left:
            self.x -= 5
        if e.key() == Qt.Key_Up:
            self.y -= 5
        if e.key() == Qt.Key_Down:
            self.y += 5
        self.setPos(self.x, self.y)
        collides_with_items = self.collidingItems(mode=Qt.IntersectsItemShape)
        if collides_with_items:
            print("Collision detected!")
            for item in collides_with_items:
                print(item)

class Platform(QFrame):

    PLATFORM_STYLE = "QFrame { color: rgb(153, 0, 0); \
                               background: rgba(0,0,0,0%); }"

    def __init__(self, parent, x, y, width, height):
        super().__init__()
        self.setGeometry(QtCore.QRect(x, y, width, height))
        self.setFrameShadow(QFrame.Plain)
        self.setLineWidth(10)
        self.setFrameShape(QFrame.HLine)
        self.setStyleSheet(Platform.PLATFORM_STYLE)
        parent.addWidget(self)

class GameScreen(QGraphicsScene):
    def __init__(self):
        super().__init__()

        # Draw background
        background = QLabel()
        background.setEnabled(True)
        background.setScaledContents(True)
        background.setGeometry(0, 0, 1280, 720)
        background.setPixmap(QtGui.QPixmap("StartScreen.png"))

        background.setText("")
        background.setTextFormat(QtCore.Qt.RichText)
        self.addWidget(background)
        self.line_5 = Platform(self, 0, 80, 431, 16)
        self.mouse = Mouse(self)

class Game(QGraphicsView):
    def __init__(self):  
        super().__init__()
        self.setWindowTitle("Running Mouse")
        self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.gamescreen = GameScreen()
        self.setScene(self.gamescreen)
        self.show()

if __name__ == '__main__':
    app = QApplication([])
    game = Game()
    sys.exit(app.exec_())
4

1 回答 1

0

您提供的是一个空的像素图 ( self.canvas),因此无论您选择哪种形状模式,它都将始终为形状,除非您使用BoundingRectShape,它使用像素图边界矩形。

您手动绘制圆圈的事实并不重要,因为不考虑绘制碰撞检测(也不应该)。

您可以覆盖该shape()方法以提供您自己的形状(使用 QPainterPath):

def shape(self):
    path = QtGui.QPainterPath()
    path.addEllipse(12, 12, 16, 16)
    return path

但是,由于您只绘制一个椭圆,因此只需使用QGraphicsEllipseItem代替。


一些未经请求的建议:

  • 如果您打算使用场景(QGraphicsItem 的父项只能是另一个 QGraphicsItem),我会避免使用名称“父项”,并且项通常不应该“添加自身”到场景中;
  • 不要覆盖self.xand self.y,因为它们是 QGraphicsItem现有 属性
  • 如果您在一个场景中有多个项目并且只想使用键盘控制一个项目,通常最好覆盖场景或视图的键事件方法(特别是如果您使用箭头键:即使滚动条被隐藏,他们仍然可以拦截这些动作);
  • 如果您仍然想在项目上使用键盘事件,请确保项目保持键盘焦点:使用setFocus是不够的,因为一旦用户点击其他地方它就会失去焦点;一种可能的解决方案是使用QGraphicsItem.grabKeyboard()(仅在将项目添加到场景后才有效);
  • 如果您在 QFrame 上设置样式表,它很可能至少会部分忽略任何框架选项集(形状、阴影等),因为它们取决于样式/平台;通常最好不要将样式表与与可视化相关的 Qt 小部件的设置混合在一起,因此在您的情况下,您可能只需要添加一个带有正确样式表参数集的简单 QFrame ;
  • 虽然从技术上讲这不是问题,但通常最好与导入“样式”保持一致,特别是在使用像 Qt 这样的复杂模块时:你要么导入单个类 ( from PyQt5.QtWidgets import QApplication, ...) 要么导入子模块 ( from PyQt5 import QtCore, ...),否则它将使代码只是令人困惑;
于 2020-04-11T16:41:20.820 回答