首页 > 解决方案 > Pygame 坐标在不同对象之间同步

问题描述

我正在使用 pygame 制作一个简单的 2d 游戏。我试图让玩家用他的枪射击子弹。然而,出于某种原因,子弹总是跟随玩家并与他同步他们的位置。

from pygame.locals import *
from threading import Thread
import time


class GameObject:
    """Base object class"""

    def __init__(self):
        """Init all base values"""
        self.hp = 100
        self.coordinates = [0, 0]
        self.speed = 3

    def move_x(self, where):
        """Moving player by x axis"""
        self.coordinates[0] += where * self.speed

    def move_y(self, where):
        """Moving player by y axis"""
        self.coordinates[1] += where * self.speed


class Gun(GameObject):
    def __init__(self):
        super().__init__()
        self.coordinates = [25, 25]
        self.bullets = []
        self.direction = [1, 1]

    def draw(self):
        pygame.draw.rect(WINDOW, (28, 28, 28),
                         pygame.Rect(self.coordinates[0], self.coordinates[1], 40, 10))

    def shoot(self):
        self.bullets.append(Bullet())
        self.bullets[-1].shoot(self.coordinates)


class Bullet(GameObject):
    def __init__(self):
        super().__init__()
        self.direction = [1, 0]

    def draw(self):
        pygame.draw.rect(WINDOW, (255, 0, 43),
                         pygame.Rect(self.coordinates[0], self.coordinates[1], 40, 10))

    def shoot(self, start_pos):
        print("shooting!!!")
        self.coordinates = start_pos
        shoot_thread = Thread(target=self.shoot_thread)
        shoot_thread.start()

    def shoot_thread(self):

        for i in range(15):
            print("MOVING")
            self.move_x(self.direction[0] * self.speed)
            self.move_y(self.direction[1] * self.speed)
            self.draw()
            time.sleep(0.1)


class Player(GameObject):

    def __init__(self):
        """Initiating all base values"""
        super().__init__()
        self.direction = "UP"
        self.gun = Gun()

    def check_move(self):
        if pygame.key.get_mods() == 1:
            self.speed = 5
        else:
            self.speed = 3
        if pygame.key.get_pressed()[K_e]:
            self.gun.shoot()
        if pygame.key.get_pressed()[K_a]:
            self.move_x(-1)
            self.gun.move_x(-1)
        if pygame.key.get_pressed()[K_d]:
            self.move_x(1)
            self.gun.move_x(1)
        if pygame.key.get_pressed()[K_w]:
            self.move_y(-1)
            self.gun.move_y(-1)
        if pygame.key.get_pressed()[K_s]:
            self.move_y(1)
            self.gun.move_y(1)

    def draw(self):
        pygame.draw.rect(WINDOW, (255, 0, 0),
                         pygame.Rect(self.coordinates[0], self.coordinates[1], 50, 50))
        self.gun.draw()


# class Bullet(GameObject):


pygame.init()

# Colours
BACKGROUND = (255, 255, 255)

# Game Setup
FPS = 60
fpsClock = pygame.time.Clock()
WINDOW_WIDTH = 1080
WINDOW_HEIGHT = 720

WINDOW = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
pygame.display.set_caption('super_shooter228')


# The main function that controls the game
def main():
    """Main function that does all the render"""
    looping = True
    # Get input

    player = Player()  # Init of the player structure
    while looping:
        pygame.event.get()
        player.check_move()  # Moving the player
        WINDOW.fill(BACKGROUND)  # This fills the backgroud with whatever you want, do all rendering after this!!
        player.draw()

        pygame.display.update()
        fpsClock.tick(FPS


main()

我尽力在互联网上找到解决方案,但我失败了。我真的很感激这个问题的任何帮助。谢谢!

标签: pythonpygamegame-development

解决方案


问题很可能来自shoot方法共享数据的方式:

# Gun
    def shoot(self):
        self.bullets.append(Bullet())
        self.bullets[-1].shoot(self.coordinates)

# Bullet
    def shoot(self, start_pos):
        print("shooting!!!")
        self.coordinates = start_pos

coordinates是一个列表,它是一个可变容器——列表可以在您将它传递给另一个函数后随时修改。当你Gun将它的坐标传递给 时Bullet,两个对象最终共享同一个列表——所以当枪移动时(每当玩家移动时都会发生这种情况),所有的子弹都会移动——同样,当子弹移动时,枪也动!

我建议防止此类错误的方法是将您存储coordinates为元组而不是列表:

class GameObject:
    """Base object class"""

    def __init__(self):
        """Init all base values"""
        self.hp = 100
        self.coordinates = (0, 0)
        self.speed = 3

    def move_x(self, where):
        """Moving player by x axis"""
        x, y = self.coordinates
        self.coordinates = x + where * self.speed, y

    def move_y(self, where):
        """Moving player by y axis"""
        x, y = self.coordinates
        self.coordinates = x, y + where * self.speed

元组是不可变的,这意味着:

  • 您不能修改其中的单个元素。所以...
  • 您只能通过创建一个全新的元组来修改它们。所以...
  • 您与之共享元组的任何其他内容仍将具有它的原始版本

对于跟踪独立游戏状态的片段,这是一件好事,因为它可以防止对一个对象状态的更改无意影响另一个对象。

一种选择是继续使用列表,但在传递列表时要非常小心,例如:

# Gun
    def shoot(self):
        self.bullets.append(Bullet())
        self.bullets[-1].shoot(self.coordinates.copy())

对于这个特定的错误,这是一个稍微容易一点的修复——但是通过这个修复,将来类似的错误也很容易弹出,这就是为什么我推荐使用元组的路线。


推荐阅读