首页 > 解决方案 > Pygame子弹向量在一次发射多个时无意中加在一起

问题描述

我一直在做我的游戏来打发呆在家里的时间。在开发的早期,我添加了发射子弹的能力,效果很好。现在我添加了一些额外的功能,每当我尝试一次发射不止一颗子弹时,所有子弹的行为都非常奇怪。

我已经浏览了相当多的内容,但没有看到任何与我的问题相似的东西。

重现步骤:

结果:

我说“消失”是因为子弹实际上从未消失,我认为它只是堆叠在另一个上面。我这样说是因为world.bullets组中精灵的数量是正确的数字。

我相信这个问题与班级Slingshot()Bullet()班级有关。

我在下面添加了一个可运行的示例:

import pygame as pyg
import random
import math
import os

os.environ['SDL_VIDEO_CENTERED'] = '1'

FPS = 30


class Player(pyg.sprite.Sprite):
    def __init__(self, window, world):
        super().__init__()
        self.window = window
        self.world = world

        self.image = pyg.Surface((30, 30))
        self.image.fill((0, 0, 255))

        self.rect = self.image.get_rect()

        self.draw_rect = self.rect

        self.vel = pyg.Vector2((0, 0))
        self.speed = 12

        self.cur_weapon = Slingshot(window, world)

    def move_x(self, amount):
        self.rect.x += int(amount)

         # Edges of the screen
        if self.rect.right > self.world.rect.width:
            self.rect.right = self.world.rect.width
            self.vel.x = 0
        if self.rect.left < 0:
            self.rect.left = 0
            self.vel.x = 0

    def move_y(self, amount):
        self.rect.y += int(amount)

         # Edges of the screen
        if self.rect.top < 0:
            self.rect.top = 0
            self.vel.y = 0
        if self.rect.bottom > self.world.rect.height:
            self.rect.bottom = self.world.rect.height
            self.vel.y = 0

    def fire(self):
        bullet = self.cur_weapon.new_bullet()
        self.world.bullets.add(bullet)
        self.cur_weapon.fire()

    def update(self):
        self.move_x(self.vel.x)
        self.move_y(self.vel.y)

        self.vel.x *= .3
        if abs(self.vel.x) < .2: self.vel.x = 0
        self.vel.y *= .3
        if abs(self.vel.y) < .2: self.vel.y = 0

        self.cur_weapon.set_pos(self.draw_rect)

    def render(self):
        self.window.blit(self.image, self.draw_rect)

        self.cur_weapon.render()

class Slingshot():
    def __init__(self, window, world):
        self.window = window
        self.world = world

        self.image = pyg.Surface((30, 20)).convert_alpha()
        self.image.fill((0, 0, 50))
        self.rect = self.image.get_rect()

        self.image_source = self.image

        self.b_image = pyg.Surface((10, 10))
        self.b_image.fill((200, 200, 200))
        self.b_rect = self.b_image.get_rect()

        self.bullet_data = {'image': self.b_image,
                            'rect': self.b_rect,
                            'owner': 'player',
                            'source': self.rect.center,
                            'target': camera.get_world_pos(pyg.mouse.get_pos()),
                            'speed': 40,
                            'invulnerable': False,
                            'bouncy': False}

    def new_bullet(self):
        self.bullet_data['source'] = self.rect.center
        self.bullet_data['target'] = camera.get_world_pos(pyg.mouse.get_pos())

        bullet = Bullet(self.window, self.world, self.bullet_data)

        return bullet

    def fire(self):
        ...

    def set_pos(self, p_rect):
        m_pos = pyg.mouse.get_pos()

        dir = math.atan2((m_pos[1] - p_rect.centery), (m_pos[0] - p_rect.centerx))
        x = math.cos(dir)
        y = math.sin(dir)

        self.rect.center = p_rect.center
        self.rect.move_ip((x * 30, y * 30))

        self.image = pyg.transform.rotate(self.image_source, math.degrees(-dir))

    def render(self):
        self.window.blit(self.image, self.rect)


class Bullet(pyg.sprite.Sprite):
    """
         XXXXXXXXXXXX
         XXXX      XXXX
         XXXX      XXXX
         XXXXXXXXXXX
         XXXX      XXXX
         XXXX      XXXX
         XXXXXXXXXXXX

    Minimap label
    """
    def __init__(self, window, world, data):
        super().__init__()
        self.window = window
        self.world = world

        self.data = data

        self.image = self.data['image']
        self.rect = self.data['rect']
        self.rect.center = camera.get_world_pos(self.data['source'])
        self.draw_rect = self.rect

        self.speed = self.data['speed']
        target = self.data['target']
        dir = math.atan2((target[1] - self.rect.centery), (target[0] - self.rect.centerx))
        # A number in the range (-1, 1), multiplied by speed to move each tick
        self.x = math.cos(dir)
        self.y = math.sin(dir)

    def update(self):
        self.rect.x += int(self.x * self.speed)
        self.rect.y += int(self.y * self.speed)

         # Edges of the screen
        if self.rect.right > self.world.rect.width:
            self.kill()
        if self.rect.left < 0:
            self.kill()
        if self.rect.top < 0:
            self.kill()
        if self.rect.bottom > self.world.rect.height:
            self.kill()

    def render(self):
        self.window.blit(self.image, self.draw_rect)

class Camera():
    """
            XXXXXXXXX
         XXXX       XXXX
         XXXX
         XXXX
         XXXX
         XXXX       XXXX
            XXXXXXXXX

    Minimap label
    """
    def __init__(self):
        self.rect = pyg.rect.Rect(0, 0, WIDTH, HEIGHT)

    def get_world_pos(self, pos):
        return (pos[0] - self.rect.left, pos[1] - self.rect.top)

    def apply_lens(self, player, world):
        player.draw_rect = player.rect.move(self.rect.topleft)

        everything = (world.statics.sprites() +
                      world.bullets.sprites())

        for sprite in everything:
            sprite.draw_rect = sprite.rect.move(self.rect.topleft)

    def follow(self, sprite):
        pos = sprite.rect.center

         # Subtract half of the screen to center the sprite
        top = pos[0] - WIDTH//2
        left = pos[1] - HEIGHT//2
        width = self.rect.width
        height = self.rect.height

        self.rect = pyg.rect.Rect(-top, -left, width, height)


class World():
    """
        XXX         XXX
        XXX         XXX
        XXX   XXX   XXX
        XXX  XXXXX  XXX
        XXX XXX XXX XXX
        XXXXX     XXXXX
        XXX         XXX

    Minimap label
    """
    def __init__(self, window):
        self.window = window
        self.rooms = []
        self.statics = pyg.sprite.Group()
        self.bullets = pyg.sprite.Group()

        self.rooms = []
        self.statics.empty()
        self.bullets.empty()

        self.image = pyg.Surface((1000, 750))
        self.image.fill((150, 150, 150))
        self.rect = self.image.get_rect()
        self.draw_rect = self.rect
        self.statics.add(StaticObject(self.window, self.rect.center, self.rect.size))
        self.statics.add(StaticObject(self.window, self.rect.center, (50, 50)))


class StaticObject(pyg.sprite.Sprite):
    def __init__(self, window, pos, size):
        super().__init__()
        self.image = pyg.Surface(size)
        self.image.fill((random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)))
        self.window = window
        self.window_rect = window.get_rect()
        self.rect = self.image.get_rect()
        self.rect.center = pos
        self.draw_rect = self.rect

    def render(self):
        if self.draw_rect.colliderect(self.window_rect):
            self.window.blit(self.image, self.draw_rect)


def terminate():
    pyg.quit()
    raise SystemExit()

    """
        XXX         XXX
        XXXXX     XXXXX
        XXX XXX XXX XXX
        XXX  XXXXX  XXX
        XXX   XXX   XXX
        XXX         XXX
        XXX         XXX

    Minimap label
    """
if __name__ == '__main__':
    pyg.init()
    pyg.display.set_caption("Reversal")
    window = pyg.display.set_mode((1000, 700))
    WIDTH, HEIGHT = pyg.display.get_window_size()
    camera = Camera()
    world = World(window)

    player = Player(window, world)
    player.rect.center = world.rect.center

    clock = pyg.time.Clock()

    while True:
        for event in pyg.event.get():
            if event.type == pyg.QUIT:
                terminate()
            elif event.type == pyg.KEYDOWN:
                if event.key == pyg.K_ESCAPE:
                    terminate()
            elif event.type == pyg.MOUSEBUTTONDOWN:
                if event.button == 1:
                    player.fire()

        event_keys = pyg.key.get_pressed()
        if event_keys[pyg.K_LEFT] or event_keys[pyg.K_a]:
            player.vel.x -= player.speed
        if event_keys[pyg.K_RIGHT] or event_keys[pyg.K_d]:
            player.vel.x += player.speed
        if event_keys[pyg.K_UP] or event_keys[pyg.K_w]:
            player.vel.y -= player.speed
        if event_keys[pyg.K_DOWN] or event_keys[pyg.K_s]:
            player.vel.y += player.speed

        camera.follow(player)
        camera.apply_lens(player, world)

        window.fill((255, 255, 255))

        for s in world.statics:
            s.render()

        for b in world.bullets:
            b.update()
            b.render()

        player.update()
        player.render()

        pyg.display.flip()
        clock.tick(FPS)

感谢您提供的任何帮助。

标签: pythonpygame

解决方案


您的所有Bullet对象共享同一个pygame.Rect对象。该指令self.rect = self.data ['rect']不会创建新pygame.Rect对象。它只存储self.data['rect']对属性的引用self.rect

当发射新子弹时,使用该copy方法创建子弹开始矩形的副本:

self.rect = self.data['rect']

self.rect = self.data['rect'].copy()

推荐阅读