Обнаружение столкновения на уровне пикселей PyQt5 - PullRequest
0 голосов
/ 11 апреля 2020

Я учусь Python и подумал сделать простую платформенную игру с PyQ5t. В настоящее время у меня проблемы, когда я хочу сделать пиксельное обнаружение столкновения между формами в моей игре. Я установил прозрачный QPixMap, а также попытался использовать режим фигуры QGraphicsPixmapItem.HeuristicMaskShape, но обнаружение столкновений не работает.

Если я создаю фигуру (в данном случае мышь), backgroun, например, серый, а затем убираю режим фигуры происходит прямое angular обнаружение столкновений.

Что мне здесь не хватает? Я потратил несколько часов, копаясь в Inte rnet, но пока не нашел решения ...

Вот мой код, чтобы показать проблему, пожалуйста, используйте клавиши со стрелками для перемещения красной «мыши» вокруг :) Я надеюсь увидеть текст, обнаруженный при столкновении, когда первый пиксель в красном круге коснется коричневой платформы.

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_())

1 Ответ

0 голосов
/ 11 апреля 2020

Вы предоставляете пустое растровое изображение (self.canvas), поэтому независимо от того, какой режим формы вы выберете, оно всегда будет иметь форму null , если вы не используете BoundingRectShape, в котором используется растровое изображение ограничивающий прямоугольник.

Тот факт, что вы рисуете круг вручную, действительно не имеет значения, поскольку рисование не учитывается при обнаружении столкновений (и не должно).

Вы можете переопределить shape() метод для предоставления вашей собственной фигуры (используя QPainterPath):

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

Но, поскольку вы рисуете только эллипс, просто используйте вместо него QGraphicsEllipseItem .


Некоторые незапрошенные предложения:

  • Я бы избегал использовать имя «parent», если вы намереваетесь использовать сцену (родительский элемент QGraphicsItem может быть только другой QGraphicsItem), и элемент обычно не должен «добавлять себя» в сцену;
  • не перезаписывать self.x и self.y, так как они QGraphicsItem существующий properties ;
  • , если у вас есть несколько элементов в сценарии Если вы хотите управлять одним из них с помощью клавиатуры, обычно лучше переписать ключевые события события сцены или представления (особенно если вы используете клавиши со стрелками: даже если полосы прокрутки скрыты, они все равно могут перехватывать эти движения) ;
  • , если вы все еще хотите использовать события клавиатуры на элементе, убедитесь, что элемент сохраняет фокус клавиатуры: недостаточно использовать setFocus, поскольку он потеряет фокус, как только пользователь нажмет в другом месте; возможное решение - использовать QGraphicsItem.grabKeyboard() (который работает только при добавлении элемента в сцену);
  • , если вы устанавливаете таблицу стилей в QFrame, очень вероятно, что он будет по крайней мере частично игнорировать любой набор параметров фрейма (shape, shadow, et c), так как они зависят от стиля / платформы; обычно лучше не смешивать таблицы стилей с настройками виджетов Qt, которые связаны с визуализацией, поэтому в вашем случае вам, вероятно, нужно будет просто добавить простой QFrame с правильным набором параметров таблицы стилей ;
  • хотя технически это не проблема, обычно предпочтительнее соответствовать «стилям» импорта, особенно при использовании сложных модулей, таких как Qt: вы либо импортируете отдельные классы (from PyQt5.QtWidgets import QApplication, ...), либо подмодули (from PyQt5 import QtCore, ...) иначе это может сделать код просто запутанным;
...