我正在尝试创建一个导弹命令克隆来挑战/扩展我的Python和Pygame技能和知识。下面是到目前为止游戏的代码(它是基本的,我是初学者):
import math
import pygame
class Missile:
def __init__(self, surface):
self.surface = surface
self.missileList = []
self.color = (0, 255, 0)
def draw(self):
if len(self.missileList) > 0:
self.missileList.sort()
for i in range(0, len(self.missileList)):
if self.missileList[i][1] < self.missileList[i][4]:
self.missileList.pop(i)
else:
self.update(i)
self.surface.set_at((int(self.missileList[i][0]), int(self.missileList[i][1])), self.color)
def update(self, i):
self.missileList[i][0] -= self.missileList[i][3] * math.cos(self.missileList[i][2])
self.missileList[i][1] -= self.missileList[i][3] * math.sin(self.missileList[i][2])
width = 640
height = 480
BGCOLOR = (0, 0, 0)
mousex = 0
mousey = 0
screen = pygame.display.set_mode((width, height))
clock = pygame.time.Clock()
startx = int(width / 2)
starty = height - 10
speed = 2
missile = Missile(screen)
running = True
while running:
screen.fill(BGCOLOR)
missile.draw()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.quit()
elif event.type == pygame.MOUSEMOTION:
mousex, mousey = event.pos
elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
endx = mousex
endy = mousey
trajectory = math.atan2(height - endy, (width / 2) - endx)
missile.missileList += [[startx, starty, trajectory, speed, endy]]
pygame.display.flip()
clock.tick(75)
因此,每次单击鼠标按钮时,navigation.missileList都会附加从开始到结束获取“导弹”所需的所有信息。当导弹到达其终点时,那些列表条目将被删除。问题是,这会抛出列表索引,如果有另一个导弹被跟踪,则会抛出错误(列表索引超出范围)。我认为在每次draw()调用开始时对列表进行排序会有所帮助,但事实并非如此。有什么想法吗?
答案 0 :(得分:4)
您在迭代时修改列表,这通常是一个错误。在这种情况下,使代码工作的最简单方法是向后迭代:
for i in range(len(self.missileList)-1, -1, -1)
这是有效的,因为你只删除循环中的当前项 - 当你向后迭代时,删除当前项只会影响你已经看过的那些项的索引。
答案 1 :(得分:3)
最好的解决方案可能是首先使用列表推导过滤missileList
。
self.missileList = [m for m in self.missileList if m[i] >= m[4]]
然后做
for i, m in enumerate(self.missileList): # enumerate instead of range
# process missile
As is well documented,在迭代它们时从列表中删除项目不起作用,甚至(尤其)使用范围,因为你最终得到的索引比列表项更多。如果您需要就地更改列表,请参阅here。
列表理解非常简单,我强烈建议再玩一些。这是一个快速入门。列表推导只是通过依次将函数或表达式应用于每个项目,将一个列表转换为另一个列表:
[1, 2, 3, 4, 5, 6] -> [1, 4, 9, 16, 25, 36]
他们还可以过滤列表:
[1, 2, 3, 4, 5, 6] -> [4, 5, 6]
他们的工作方式与此相同,每个关键语法组件都在&lt;&lt;&gt;&gt;中:
[ <<thing_to_do_for_each_item(item)>> <<for item in ['list', 'of', 'items']>> ]
您可以选择在末尾添加谓词来过滤
[ <<thing(item)>> <<for item in ['l', 'o', 'i']>> <<if boolean_test(item)>> ]
其中boolean_test
是任何类型的表达式或函数,可以解释为导致布尔值。
你可以看到,尽管它们会移动这些位,但从语法上讲,它们对于语句非常相似:
newlist = []
<<for item in ['l', 'o', 'i']>>:
<<if boolean_test(item)>>:
newlist.append( <<thing(item)>> )
请注意,关键字的顺序完全相同 - for
,然后是if
。唯一的区别是thing(item)
首先而不是 last 。 (但请注意,如果<<bolean_test(item)>>
返回true,它仍然只是已执行。)此规则相当干净地概括为更复杂的列表推导(但我不会在此处讨论)。
所有这些意味着以下代码:
old_list = [1, 2, 3, 4, 5, 6]
new_list = []
for i in old_list:
if i > 3:
new_list.append(i ** 2)
等同于:
new_list = [i ** 2 for i in old_list if i > 3]
在这两种情况下,结果都是原始列表中项目的正方形的过滤列表:
>>> print old_list, new_list
[1, 2, 3, 4, 5, 6] [16, 25, 36]
如果您需要进一步说明,请与我们联系。我认为列表推导是该语言非常有用和重要的部分。你应该尽快学习它们。
答案 2 :(得分:0)
您正在修改循环中的列表。你曾经(有点)通过迭代索引来欺骗自己。除了RichieHindle的解决方案(也不错),您可以将修改部分与实际逻辑分开。
missleList[] = [missile for missile in missileList if missile[1] >= missile[4]]
for i, missile in enumerate(missileList):
self.update(i)
self.surface.set_at((int(self.missile[0]), int(self.missile[1])), self.color)