Pygame, проблема индекса списка Python - PullRequest
2 голосов
/ 18 июля 2011

Я пытаюсь создать клон Missile Command, чтобы проверить / расширить мои навыки и знания 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)

Таким образом, каждый раз, когда я нажимаю кнопку мыши, в файл missile.missileList добавляется вся информация, необходимая для получения «ракеты» от начала до конца. Когда ракета достигает своей конечной точки, эти записи в списке удаляются. Проблема в том, что после этого списочные индексы сбрасываются и выдается ошибка (индекс списка выходит за пределы диапазона), если отслеживается другая ракета. Я думал, что сортировка списка в начале каждого вызова draw () поможет, но это не помогло. Есть идеи?

Ответы [ 3 ]

4 голосов
/ 18 июля 2011

Вы изменяете список, повторяя его, что обычно является ошибкой.В этом случае самый простой способ заставить код работать - это итерация в обратном направлении:

for i in range(len(self.missileList)-1, -1, -1)

Это работает, потому что вы удаляете только текущий элемент в цикле - когда вы выполняете итерацию в обратном направлении, удаляя только текущий элементвлияет на показатели тех предметов, которые вы уже видели.

3 голосов
/ 18 июля 2011

Лучшее решение, вероятно, - сначала отфильтровать 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

Как хорошо задокументировано , удаление элементов из списков во время их перебора не работает, даже (особенно) с диапазоном, поскольку у вас больше индексов, чем элементов списка. Если вам нужно изменить список на месте, см. здесь .


Понимание списков действительно довольно простое, и я настоятельно рекомендую поиграть с ними еще немного. Вот быстрый учебник. Постижения списков просто превращают один список в другой, применяя функцию или выражение к каждому элементу по очереди:

[1, 2, 3, 4, 5, 6] -> [1, 4, 9, 16, 25, 36]

Они также могут фильтровать списки:

[1, 2, 3, 4, 5, 6] -> [4, 5, 6]

Они работают так, с каждым из ключевых синтаксических компонентов в << >>:

[ <<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) идет сначала вместо последний . (Но учтите, что он все еще выполняется только , если <<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]

Пожалуйста, дайте мне знать, если вам нужны дальнейшие объяснения. Я думаю, что понимание списка - действительно полезная и важная часть языка. Вы должны изучить их как можно скорее.

0 голосов
/ 18 июля 2011

вы изменяете список в том же цикле, что итерируете по нему.Вы (вроде бы) обманули себя, перебирая индексы.Помимо решения 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...