python - 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)
感谢您提供的任何帮助。
解决方案
您的所有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()
推荐阅读
- c - mmap() 中的 MAP_ANONYMOUS 和 MAP_SHARED_VALIDATE 标志未分配内存
- c# - 使用 C# 的交付程序 - 在 switch case 语句后更改值
- python-3.x - 修复几何没有 z 几何时的 z 列错误
- javascript - 如何从下拉菜单中读取值并使用 javascript 带入 IF 语句?
- python - Pandas 的 dataframe.dropna() 中的“na”是什么意思?
- python - 通过python将excel导入SQL
- python - 使用python将嵌套的json拆分为两个/多个文件
- python - python3 mariadb ssl:请求的数据不可用
- python - cuDNN 错误 无法获得卷积算法。这可能是因为 cuDNN 未能初始化
- php - Array_unique 和 XLS 导出