0

我想在pyqt中构建一个简单的游戏,我想当敌人与子弹碰撞时增加分数,我创建了一个从QGraphicsTextItem扩展的Score类,我创建了分数文本我还添加了一个增加方法。之后我在我的 Bullet.py 文件中添加了这个类,因为在 Bullet.py 发生碰撞时,我也在我的 Window.py 文件中添加了这个类,因为我希望那个文本应该在场景中,但是当我运行游戏得分为0,碰撞后没有任何反应,这些是我的文件

窗口.py

from PyQt6.QtWidgets import QGraphicsScene, QApplication, QGraphicsView, QGraphicsItem
from PyQt6.QtCore import Qt, QTimer
import sys
from Player import Player
from Enemy import Enemy
from Score import Score

class Window(QGraphicsView):

    def __init__(self):
        super().__init__()

        self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)

        self.setFixedSize(800, 600)
        self.create_scene()

        self.show()


    def create_scene(self):
        self.scene = QGraphicsScene()

        # create an item to put in the scene
        player = Player()

        # make rect focusable
        player.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsFocusable)
        player.setFocus()

        # by default QGraphicsRectItem has 0 length and width
        player.setRect(0, 0, 100, 100)

        # add item to the scene
        self.scene.addItem(player)

        # set size of the scene
        self.scene.setSceneRect(0, 0, 800, 600)

        # set the player at the botoom
        player.setPos(self.width() / 2, self.height() - player.rect().height())
        

        #adding the score to the scene
        score = Score()
        self.scene.addItem(score)


        self.setScene(self.scene)

        self.timer = QTimer()
        self.timer.timeout.connect(self.spawn)
        self.timer.start(2000)


    def spawn(self):
        enemy = Enemy()
        self.scene.addItem(enemy)


App = QApplication(sys.argv)
window = Window()
sys.exit(App.exec())

播放器.py

from PyQt6.QtWidgets import QGraphicsRectItem
from PyQt6.QtGui import QKeyEvent
from PyQt6.QtCore import Qt
from Bullet import MyBullet




class Player(QGraphicsRectItem):

    def __init__(self):
       super().__init__()



    def keyPressEvent(self, event: QKeyEvent):
        if (event.key() == Qt.Key.Key_Left):

            if self.pos().x() > 0:
                self.setPos(self.x() - 10, self.y())

        elif (event.key() == Qt.Key.Key_Right):
            if (self.pos().x() + 100 < 800):
                self.setPos(self.x() + 10, self.y())


        elif (event.key() == Qt.Key.Key_Space):
            mybullet = MyBullet()
            mybullet.setPos(self.x(), self.y())
            self.scene().addItem(mybullet)

敌人.py

from PyQt6.QtWidgets import QGraphicsRectItem
from random import randint
from PyQt6.QtCore import QTimer


class Enemy(QGraphicsRectItem):
    def __init__(self):
        super().__init__()


        random_number = randint(10,1000) % 700
        self.setPos(random_number , 0)


        self.setRect(0,0,100,100)

        self.timer = QTimer()
        self.timer.timeout.connect(self.move)
        self.timer.start(50)




    def move(self):
        #move enemy to down
        self.setPos(self.x(), self.y()+5)

        if self.pos().y() + self.rect().height() < 0:
            self.scene().removeItem(self)
            print("Bullet deleted")

子弹.py

from PyQt6.QtWidgets import QGraphicsRectItem, QGraphicsItem
from PyQt6.QtCore import QTimer
from Enemy import Enemy
from Score import Score


class MyBullet(QGraphicsRectItem):
    def __init__(self):
        super().__init__()


        self.setRect(0, 0, 10, 50)

        self.timer = QTimer()
        self.timer.timeout.connect(self.move)
        self.timer.start(50)


    def move(self):
        # This is the place for the collision
        colliding = self.collidingItems()
        for item in colliding:
            if isinstance(item, Enemy):

                # increase the score
                score = Score()
                score.increase()

                self.scene().removeItem(item)
                self.scene().removeItem(self)

        self.setPos(self.x(), self.y() - 10)

        if self.pos().y() + self.rect().height() < 0:
            self.scene().removeItem(self)
            print("Bullet deleted")

这是我的 Score.py 文件

from PyQt6.QtWidgets import QGraphicsTextItem
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QFont

class Score(QGraphicsTextItem):

    def __init__(self):
       super().__init__()
       self.score = 0
       # draw the text
       self.setPlainText("Score : " + str(self.score))
       self.setDefaultTextColor(Qt.GlobalColor.red)
       self.setFont(QFont("Sanserif", 18))


    def increase(self):
        self.score += 1
        self.setPlainText(str(self.score))
4

1 回答 1

0

score问题是每次检测到碰撞时您都在创建一个项目,但该项目并未添加到场景中,因此它在 结束时被删除move,使其无用;但是,即使您确实将该项目添加到场景中,这也是没有意义的,因为score场景中已经存在一个项目,但是您没有保持对它的持久引用,因此您不能调用它的increase方法。

因此,需要进行三个更改:

  1. 找到一种方法来告知场景发生了碰撞并且分数必须增加;这样做的正确方法是通过信号,但由于基本的 QGraphicsItems 不继承自 QObject,因此您无法为它们创建信号,因此您可以使用充当“信号代理”的类;
class BulletSignals(QObject):
    enemyHit = pyqtSignal()

class MyBullet(QGraphicsRectItem):
    def __init__(self):
        super().__init__()
        self.proxy = BulletSignals()
        self.enemyHit = self.proxy.enemyHit
        # ...

    def move(self):
        colliding = self.collidingItems()
        isHit = False
        for item in colliding:
            if isinstance(item, Enemy):
                self.scene().removeItem(item)
                isHit = True
        if isHit:
            self.scene().removeItem(self)
            self.enemyHit.emit()

        else:
            self.setY(self.y() - 10)

            if self.pos().y() + self.rect().height() < 0:
                self.scene().removeItem(self)
            print("Bullet deleted")
  1. 连接子弹的信号;这需要对逻辑进行小而重要的更改,这是因为更好、更正确的 OOP 实现将使场景的职责(或者,更好的是,“主控制器”)添加项目符号;所以,我们也会为播放器添加一个信号代理;
class PlayerSignals(QObject):
    fire = pyqtSignal(QPointF)

class Player(QGraphicsRectItem):

    def __init__(self):
        super().__init__()
        self.proxy = PlayerSignal()
        self.fire = self.proxy.fire

    def keyPressEvent(self, event: QKeyEvent):
        if (event.key() == Qt.Key.Key_Left):
            if self.pos().x() > 0:
                self.setX(max(0, self.x() - 10))

        elif (event.key() == Qt.Key.Key_Right):
            if (self.pos().x() + 10 < 800):
                self.setX(min(800, self.x() + 10))

        elif (event.key() == Qt.Key.Key_Space):
            self.fire.emit(self.pos()
  1. 创建两个scoreplayer实例成员,并在需要时连接信号:
class Window(QGraphicsView):
    # ...
    def create_scene(self):
        self.scene = QGraphicsScene()

        # create an item to put in the scene
        self.player = Player()
        self.player.fire.connect(self.fire)

        # ...
        self.score = Score()
        self.scene.addItem(self.score)
        # ...

    def fire(self, pos):
        bullet = MyBullet()
        bullet.setPos(pos)
        self.scene.addItem(bullet)
        bullet.enemyHit.connect(self.score.increase)

注意:

  1. 我改变了一些小东西(最值得注意的是,如果你只需要在一个轴上移动,只需使用setX()or setY()
  2. 尽管添加项目应该是场景/控制器的责任,但也应该删除它们;我没有更改它以避免使事情复杂化,但请记住,一个项目只能由场景或其祖先之一删除;此外,您不应该多次尝试移除一个项目;
  3. 应该小心使用可能移除项目的计时器,因为结果是有时move会在项目从场景中移除后立即调用,这可能会引发异常(因为self.scene()会返回None);以下更改将解决此问题:
class Enemy(QGraphicsRectItem):
    # ...
    def itemChange(self, change, value):
        if change == self.ItemSceneChange:
            if value:
                self.timer.start(50)
            else:
                self.timer.stop()
        return super().itemChange(change, value)

MyBullet 显然也需要同样的,你应该self.timer.start(50)从这两个__init__类中删除;

最后,虽然视图的场景在程序的生命周期中通常总是相同的,但这并不总是正确的;scene()是QGraphicsView现有的动态函数,所以不要用self.scene.

于 2021-07-23T13:01:13.523 回答