使用列表使目标在与子弹碰撞时消失
Making targets disappear upon collision with a bullet, using lists
我正在为学校课程开发一款小型打靶游戏。经过大约一个半小时的迭代和测试,我陷入了僵局。如果你看到下面的代码,我已经使用列表来制作它,以便当子弹击中目标时,它们会从列表中删除并且不再打印 'pygame.draw.rect' 但是对于目标来说似乎要困难得多因为我是使用 OOP 而不是一次性函数制作的。我已经尝试用目标复制项目符号列表,将 'IF' 语句放在相同的位置等,但我最终得到相同的结果,即目标被击中并且 'hit' 被打印出来。子弹消失了,但目标没有。我只使用了大约一个月的语言,所以虽然我越来越习惯了,但我仍然不是专家,我真的只是在这里走进了死胡同。任何形式的帮助将不胜感激。这可能是我必须完全重新考虑我的方法并彻底改变我的代码的情况,但也许有一些你可以看到但我看不到的东西。感谢您提供的任何帮助。 (对于代码的混乱,抱歉,它是经过大量更改和迭代之后的。另外请注意,我目前只编码以在 target_1 上测试它以节省时间)
import pygame
#Setting window dimensions and caption. (Module 1)
pygame.init()
window = pygame.display.set_mode((800, 575))
pygame.display.set_caption("TARGET PRACTICE")
#Colour variables. (Module 1)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (200, 0, 0)
GREEN = (0, 200, 0)
BLUE = (0, 0, 200)
#py_clock tracks framerate of program for other 'pygame.time' commands. (Module 8)
py_clock = pygame.time.Clock()
#Target class created. (Module 5)
class Target:
def __init__(self, x, y, h, w, v):
self.x = x
self.y = y
self.h = h
self.w = w
self.v = v
def hit(self):
print('hit')
all_bullets_keep.remove(item)
all_targets.remove(all_targets[0])
pass
#Instantiation of targets. (Module 5)
target_1 = Target(0, 80, 60, 40, 0.05)
target_2 = Target(0, 100, 60, 40, 0.5)
target_3 = Target(0, 50, 60, 40, 0.5)
target_4 = Target(0, 75, 60, 40, 0.5)
target_5 = Target(0, 45, 60, 40, 0.5)
target_6 = Target(0, 85, 60, 40, 0.5)
#Instantiation of hitboxes. (Module 9)
target_hbx1 = Target(-5, 75, 70, 50, 0.05)
target_hbx2 = Target(-5, 95, 70, 50, 0.5)
target_hbx3 = Target(-5, 45, 70, 50, 0.5)
target_hbx4 = Target(-5, 70, 70, 50, 0.5)
target_hbx5 = Target(-5, 40, 70, 50, 0.5)
target_hbx6 = Target(-5, 80, 70, 50, 0.5)
#Declaring variables to be used in the while loop. (Module 5)
clock = 0
target_2_threshold = 500
target_3_threshold = 1000
target_4_threshold = 1500
target_5_threshold = 2000
target_6_threshold = 2500
#Setting player sprite dimension variables. (Module 6)
player_sprite_x = 357.5
player_sprite_y = 450
player_sprite_h = 125
player_sprite_w = 85
#all_bullets list to store bullets made by function inside loop. (Module7)
all_bullets = []
all_targets = []
all_targets.append(target_1)
all_targets.append(target_2)
all_targets.append(target_3)
all_targets.append(target_4)
all_targets.append(target_5)
all_targets.append(target_6)
#Variables to track and limit shooting function. (Module 9.5)
bullet_delay = 1500
next_bullet_time = 0
exec = True
while exec:
#current_time uses a pygame_time command to track ticks. (Module 9.5)
current_time = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == pygame.QUIT:
exec = False
#'IF' statement to trigger the shooting function. (Module 7)
if event.type == pygame.MOUSEBUTTONDOWN:
#Condition set to only trigger the below code if the current_time is greater than the next_bullet time. (Module 9.5)
if event.button == 1 and current_time > next_bullet_time:
next_bullet_time = current_time + bullet_delay
dx = event.pos[0] - (player_sprite_x+ player_sprite_w//2)
dy = event.pos[1] - player_sprite_y
direction = pygame.math.Vector2(dx, dy).normalize()
bullet = {'x': player_sprite_x+42, 'y': player_sprite_y, 'direction': direction}
all_bullets.append(bullet)
#Defines movement of targets and sets delay between drawings. (Module 5)
for item in all_targets:
target_1.x += target_1.v
target_hbx1.x += target_hbx1.v
if clock > target_2_threshold:
target_2.x += target_2.v
target_hbx2.x += target_hbx2.v
if clock > target_3_threshold:
target_3.x += target_3.v
target_hbx3.x += target_hbx3.v
if clock > target_4_threshold:
target_4.x += target_4.v
target_hbx4.x += target_hbx4.v
if clock > target_5_threshold:
target_5.x += target_5.v
target_hbx5.x += target_hbx5.v
if clock > target_6_threshold:
target_6.x += target_6.v
target_hbx6.x += target_hbx6.v
#all_bullets_keep list combined with FOR loop retains only bullets in the arena. (Module 7)
all_bullets_keep = []
for item in all_bullets:
item['x'] += item['direction'][0] # item['direction'][0] * 2
item['y'] += item['direction'][1] # item['direction'][1] * 2
if 0 < item['x'] < 800 and 0 < item['y'] < 575:
all_bullets_keep.append(item)
all_bullets = all_bullets_keep
#Fill the background (Module 5)
window.fill(RED)
#Redraw each target in every frame. (Module 5)
all_targets_keep = []
for item in all_targets:
pygame.draw.rect(window, BLUE, (target_1.x, target_1.y, target_1.h, target_1.w))
pygame.draw.rect(window, BLUE, (target_hbx1.x, target_hbx1.y, target_hbx1.h,target_hbx1.w), 2)
if 0 < target_1.x < 800 and 0 < target_1.y < 575:
all_targets_keep.append(target_1)
if clock > target_2_threshold:
pygame.draw.rect(window, BLUE, (target_2.x, target_2.y, target_2.h, target_2.w))
pygame.draw.rect(window, BLUE, (target_hbx2.x, target_hbx2.y, target_hbx2.h,target_hbx2.w), 2)
all_targets_keep.append(target_2)
if clock > target_3_threshold:
pygame.draw.rect(window, BLUE, (target_3.x, target_3.y, target_3.h, target_3.w))
pygame.draw.rect(window, BLUE, (target_hbx3.x, target_hbx3.y, target_hbx3.h,target_hbx3.w), 2)
all_targets_keep.append(target_3)
if clock > target_4_threshold:
pygame.draw.rect(window, BLUE, (target_4.x, target_4.y, target_4.h, target_4.w))
pygame.draw.rect(window, BLUE, (target_hbx4.x, target_hbx4.y, target_hbx4.h,target_hbx4.w), 2)
all_targets_keep.append(target_4)
if clock > target_5_threshold:
pygame.draw.rect(window, BLUE, (target_5.x, target_5.y, target_5.h, target_5.w))
pygame.draw.rect(window, BLUE, (target_hbx5.x, target_hbx5.y, target_hbx5.h,target_hbx5.w), 2)
all_targets_keep.append(target_5)
if clock > target_6_threshold:
pygame.draw.rect(window, BLUE, (target_6.x, target_6.y, target_6.h, target_6.w))
pygame.draw.rect(window, BLUE, (target_hbx6.x, target_hbx6.y, target_hbx6.h,target_hbx6.w), 2)
all_targets_keep.append(target_6)
all_targets = all_targets_keep
#Draw the player sprite. (Module 6)
pygame.draw.rect(window, BLUE, (player_sprite_x, player_sprite_y, player_sprite_w, player_sprite_h))
#Draw each item in all_bullets. (Module 7)
for item in all_bullets:
pygame.draw.rect(window, BLUE, (item['x']-5, item['y']-5, 10, 10))
b_hitbox = (item['x']-10, item['y']-10, 20, 20)
pygame.draw.rect(window, BLUE, b_hitbox, 2)
for item in all_bullets_keep:
if item['y']-30 < (target_hbx1.y) + (target_hbx1.h) and item['y']+30 > target_hbx1.y:
if item['x']+10 > target_hbx1.x and item['x']-30 < (target_hbx1.x) + (target_hbx1.w):
target_1.hit()
pygame.display.update()
#tick_busy_loop limits number of times the game can refresh per second. (Module 8)
py_clock.tick_busy_loop(120)
pygame.quit()
您的代码中存在一些小错误。我认为目标不会因为 all_targets_keep
列表中发生的事情而消失。看起来代码正在将目标重新添加到 "keep" 列表中,无论它是否被命中。
您使用 class 保存所有 Target
代码的方法是个好主意。但是所有目标代码仍然分布在您的主循环中。这导致代码混乱并使任务更加困难。
通过将此代码移动到 class,它使代码不再具有每个目标的 "special case"。目标知道关于它自己的一切,并且可以在它自己内部进行任何测试。例如,将目标绘制到屏幕上。
class Target:
def __init__(self, x, y, h, w, v, threshold):
self.x = x
self.y = y
self.h = h
self.w = w
self.v = v
self.threshold = threshold
self.hit = False
def draw( self, window ):
# body
pygame.draw.rect( window, BLUE, ( self.x, self.y, self.w, self.h ), 0 )
# hit-box
pygame.draw.rect( window, BLUE, ( self.x-5, self.y-5, self.w+10, self.h+10 ), 1 )
当目标有一个成员函数来绘制自己时,整个绘制代码就会消失,成为一个简单的函数。那么all个目标的绘制就变成了:
clock += 1
for item in all_targets:
if ( clock > item.threshold and not item.hit ):
item.draw( window )
目标的"hit box"也只是现有点的函数,没有必要将它们分开。因此,类似地,使用 PyGame rect,目标可以检查它是否被子弹击中:
class Target:
...
def collidesWith( self, bullet_pos ):
# hit-box is 5 pixels offset from target
target_rect = pygame.Rect( self.x-5, self.y-5, self.w+10, self.h+10 )
self.hit = target_rect.collidepoint( bullet_pos )
return self.hit
无论如何,你的进步很大。但是评论者@importrandom 说的是真的 - 使用 PyGame built-in sprite class 确实 更容易 (最终)。他们已经处理了很多您自己编写的代码。如果你不想,没关系,这是你的代码。
我正在为学校课程开发一款小型打靶游戏。经过大约一个半小时的迭代和测试,我陷入了僵局。如果你看到下面的代码,我已经使用列表来制作它,以便当子弹击中目标时,它们会从列表中删除并且不再打印 'pygame.draw.rect' 但是对于目标来说似乎要困难得多因为我是使用 OOP 而不是一次性函数制作的。我已经尝试用目标复制项目符号列表,将 'IF' 语句放在相同的位置等,但我最终得到相同的结果,即目标被击中并且 'hit' 被打印出来。子弹消失了,但目标没有。我只使用了大约一个月的语言,所以虽然我越来越习惯了,但我仍然不是专家,我真的只是在这里走进了死胡同。任何形式的帮助将不胜感激。这可能是我必须完全重新考虑我的方法并彻底改变我的代码的情况,但也许有一些你可以看到但我看不到的东西。感谢您提供的任何帮助。 (对于代码的混乱,抱歉,它是经过大量更改和迭代之后的。另外请注意,我目前只编码以在 target_1 上测试它以节省时间)
import pygame
#Setting window dimensions and caption. (Module 1)
pygame.init()
window = pygame.display.set_mode((800, 575))
pygame.display.set_caption("TARGET PRACTICE")
#Colour variables. (Module 1)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (200, 0, 0)
GREEN = (0, 200, 0)
BLUE = (0, 0, 200)
#py_clock tracks framerate of program for other 'pygame.time' commands. (Module 8)
py_clock = pygame.time.Clock()
#Target class created. (Module 5)
class Target:
def __init__(self, x, y, h, w, v):
self.x = x
self.y = y
self.h = h
self.w = w
self.v = v
def hit(self):
print('hit')
all_bullets_keep.remove(item)
all_targets.remove(all_targets[0])
pass
#Instantiation of targets. (Module 5)
target_1 = Target(0, 80, 60, 40, 0.05)
target_2 = Target(0, 100, 60, 40, 0.5)
target_3 = Target(0, 50, 60, 40, 0.5)
target_4 = Target(0, 75, 60, 40, 0.5)
target_5 = Target(0, 45, 60, 40, 0.5)
target_6 = Target(0, 85, 60, 40, 0.5)
#Instantiation of hitboxes. (Module 9)
target_hbx1 = Target(-5, 75, 70, 50, 0.05)
target_hbx2 = Target(-5, 95, 70, 50, 0.5)
target_hbx3 = Target(-5, 45, 70, 50, 0.5)
target_hbx4 = Target(-5, 70, 70, 50, 0.5)
target_hbx5 = Target(-5, 40, 70, 50, 0.5)
target_hbx6 = Target(-5, 80, 70, 50, 0.5)
#Declaring variables to be used in the while loop. (Module 5)
clock = 0
target_2_threshold = 500
target_3_threshold = 1000
target_4_threshold = 1500
target_5_threshold = 2000
target_6_threshold = 2500
#Setting player sprite dimension variables. (Module 6)
player_sprite_x = 357.5
player_sprite_y = 450
player_sprite_h = 125
player_sprite_w = 85
#all_bullets list to store bullets made by function inside loop. (Module7)
all_bullets = []
all_targets = []
all_targets.append(target_1)
all_targets.append(target_2)
all_targets.append(target_3)
all_targets.append(target_4)
all_targets.append(target_5)
all_targets.append(target_6)
#Variables to track and limit shooting function. (Module 9.5)
bullet_delay = 1500
next_bullet_time = 0
exec = True
while exec:
#current_time uses a pygame_time command to track ticks. (Module 9.5)
current_time = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == pygame.QUIT:
exec = False
#'IF' statement to trigger the shooting function. (Module 7)
if event.type == pygame.MOUSEBUTTONDOWN:
#Condition set to only trigger the below code if the current_time is greater than the next_bullet time. (Module 9.5)
if event.button == 1 and current_time > next_bullet_time:
next_bullet_time = current_time + bullet_delay
dx = event.pos[0] - (player_sprite_x+ player_sprite_w//2)
dy = event.pos[1] - player_sprite_y
direction = pygame.math.Vector2(dx, dy).normalize()
bullet = {'x': player_sprite_x+42, 'y': player_sprite_y, 'direction': direction}
all_bullets.append(bullet)
#Defines movement of targets and sets delay between drawings. (Module 5)
for item in all_targets:
target_1.x += target_1.v
target_hbx1.x += target_hbx1.v
if clock > target_2_threshold:
target_2.x += target_2.v
target_hbx2.x += target_hbx2.v
if clock > target_3_threshold:
target_3.x += target_3.v
target_hbx3.x += target_hbx3.v
if clock > target_4_threshold:
target_4.x += target_4.v
target_hbx4.x += target_hbx4.v
if clock > target_5_threshold:
target_5.x += target_5.v
target_hbx5.x += target_hbx5.v
if clock > target_6_threshold:
target_6.x += target_6.v
target_hbx6.x += target_hbx6.v
#all_bullets_keep list combined with FOR loop retains only bullets in the arena. (Module 7)
all_bullets_keep = []
for item in all_bullets:
item['x'] += item['direction'][0] # item['direction'][0] * 2
item['y'] += item['direction'][1] # item['direction'][1] * 2
if 0 < item['x'] < 800 and 0 < item['y'] < 575:
all_bullets_keep.append(item)
all_bullets = all_bullets_keep
#Fill the background (Module 5)
window.fill(RED)
#Redraw each target in every frame. (Module 5)
all_targets_keep = []
for item in all_targets:
pygame.draw.rect(window, BLUE, (target_1.x, target_1.y, target_1.h, target_1.w))
pygame.draw.rect(window, BLUE, (target_hbx1.x, target_hbx1.y, target_hbx1.h,target_hbx1.w), 2)
if 0 < target_1.x < 800 and 0 < target_1.y < 575:
all_targets_keep.append(target_1)
if clock > target_2_threshold:
pygame.draw.rect(window, BLUE, (target_2.x, target_2.y, target_2.h, target_2.w))
pygame.draw.rect(window, BLUE, (target_hbx2.x, target_hbx2.y, target_hbx2.h,target_hbx2.w), 2)
all_targets_keep.append(target_2)
if clock > target_3_threshold:
pygame.draw.rect(window, BLUE, (target_3.x, target_3.y, target_3.h, target_3.w))
pygame.draw.rect(window, BLUE, (target_hbx3.x, target_hbx3.y, target_hbx3.h,target_hbx3.w), 2)
all_targets_keep.append(target_3)
if clock > target_4_threshold:
pygame.draw.rect(window, BLUE, (target_4.x, target_4.y, target_4.h, target_4.w))
pygame.draw.rect(window, BLUE, (target_hbx4.x, target_hbx4.y, target_hbx4.h,target_hbx4.w), 2)
all_targets_keep.append(target_4)
if clock > target_5_threshold:
pygame.draw.rect(window, BLUE, (target_5.x, target_5.y, target_5.h, target_5.w))
pygame.draw.rect(window, BLUE, (target_hbx5.x, target_hbx5.y, target_hbx5.h,target_hbx5.w), 2)
all_targets_keep.append(target_5)
if clock > target_6_threshold:
pygame.draw.rect(window, BLUE, (target_6.x, target_6.y, target_6.h, target_6.w))
pygame.draw.rect(window, BLUE, (target_hbx6.x, target_hbx6.y, target_hbx6.h,target_hbx6.w), 2)
all_targets_keep.append(target_6)
all_targets = all_targets_keep
#Draw the player sprite. (Module 6)
pygame.draw.rect(window, BLUE, (player_sprite_x, player_sprite_y, player_sprite_w, player_sprite_h))
#Draw each item in all_bullets. (Module 7)
for item in all_bullets:
pygame.draw.rect(window, BLUE, (item['x']-5, item['y']-5, 10, 10))
b_hitbox = (item['x']-10, item['y']-10, 20, 20)
pygame.draw.rect(window, BLUE, b_hitbox, 2)
for item in all_bullets_keep:
if item['y']-30 < (target_hbx1.y) + (target_hbx1.h) and item['y']+30 > target_hbx1.y:
if item['x']+10 > target_hbx1.x and item['x']-30 < (target_hbx1.x) + (target_hbx1.w):
target_1.hit()
pygame.display.update()
#tick_busy_loop limits number of times the game can refresh per second. (Module 8)
py_clock.tick_busy_loop(120)
pygame.quit()
您的代码中存在一些小错误。我认为目标不会因为 all_targets_keep
列表中发生的事情而消失。看起来代码正在将目标重新添加到 "keep" 列表中,无论它是否被命中。
您使用 class 保存所有 Target
代码的方法是个好主意。但是所有目标代码仍然分布在您的主循环中。这导致代码混乱并使任务更加困难。
通过将此代码移动到 class,它使代码不再具有每个目标的 "special case"。目标知道关于它自己的一切,并且可以在它自己内部进行任何测试。例如,将目标绘制到屏幕上。
class Target:
def __init__(self, x, y, h, w, v, threshold):
self.x = x
self.y = y
self.h = h
self.w = w
self.v = v
self.threshold = threshold
self.hit = False
def draw( self, window ):
# body
pygame.draw.rect( window, BLUE, ( self.x, self.y, self.w, self.h ), 0 )
# hit-box
pygame.draw.rect( window, BLUE, ( self.x-5, self.y-5, self.w+10, self.h+10 ), 1 )
当目标有一个成员函数来绘制自己时,整个绘制代码就会消失,成为一个简单的函数。那么all个目标的绘制就变成了:
clock += 1
for item in all_targets:
if ( clock > item.threshold and not item.hit ):
item.draw( window )
目标的"hit box"也只是现有点的函数,没有必要将它们分开。因此,类似地,使用 PyGame rect,目标可以检查它是否被子弹击中:
class Target:
...
def collidesWith( self, bullet_pos ):
# hit-box is 5 pixels offset from target
target_rect = pygame.Rect( self.x-5, self.y-5, self.w+10, self.h+10 )
self.hit = target_rect.collidepoint( bullet_pos )
return self.hit
无论如何,你的进步很大。但是评论者@importrandom 说的是真的 - 使用 PyGame built-in sprite class 确实 更容易 (最终)。他们已经处理了很多您自己编写的代码。如果你不想,没关系,这是你的代码。