Это моя надежда, что щедрость привлечет человек, который знает вещь или два о внутренней работе PyGame (я попробовал, глядя на исходном коде .. есть куча его) и может сказать мне, если это это действительно , связанное с наличием threading.py или если есть практика Я могу избежать вообще, чтобы это не происходило в других проектах PyGame.python и pygame - что произошло во время выполнения этой оболочки?
Я создал обертку для объектов, которые взрываются при вызове obj.kill()
.
def make_explosion(obj, func):
def inner(*args, **kwargs):
newX, newY = obj.x, obj.y
newBoom = Explosion(newX, newY)
allqueue.add(newBoom) #an instance of pygame.sprite.Group
return func(*args, **kwargs)
return inner
Он получает й объект и у координаты, а затем создает новый экземпляр Explosion
в этих координатах, добавляет его к allqueue
, которые я использую, чтобы убедиться, что все update
d во время игры, и, наконец, возвращается функция его обертывания - в этом случае, obj.kill
. obj.kill()
- это метод pygame.sprite.Sprite
, который удаляет объект из всех экземпляров sprite.Group
, к которому принадлежит.
Я бы просто обернул метод следующим образом, во время создания экземпляра Enemy
.
newEnemy = Enemy()
##other code to add new AI, point values…eventually, the following happens:
newEnemy.kill = make_explosion(newEnemy, newEnemy.kill)
Когда я запустил игру, взрывы появились в случайных местах, где-то рядом с фактическими координатами x и y объекта. Анекдотически это выглядело не так, как будто это происходило на объекте (x, y) происхождения или даже на дорогах, которые они путешествовали во время их короткого существования на экране (я довольно хорош в своей игре, чтобы не хвастаться) , поэтому я чувствовал, что должен был исключить, что x и y были назначены в момент, когда метод был завернут.
Немного, бьюсь futzed вокруг и изменил код так:
def make_explosion(obj, func):
def inner(*args, **kwargs):
allqueue.add(Explosion(obj.x, obj.y))
return func(*args, **kwargs)
return inner
Это изменение сделало работу «как задумано» - когда метод объекта self.kill()
называется, взрыв появляется на правильный координаты.
Что я не понимаю, почему это работает! Особенно учитывая документацию PyGame, в которой говорится, что kill()
не обязательно удаляет объект; он просто удаляет его из всех групп, к которым он принадлежит.
от https://www.pygame.org/docs/ref/sprite.html#pygame.sprite.Sprite.kill -
убить()
удалить Sprite из всех групп убивают() -> None
спрайта удаляется из всех групп, которые содержат его , Это не изменит ничего о состоянии спрайта . Можно продолжить до использовать Sprite после вызова этого метода, включая добавление его в группы.
Итак, хотя я случайно решил проблему, я вообще не понимаю ее. Почему он ведет себя таким образом?
EDIT: Исходя из некоторых ранних комментариев, я попытался воспроизвести подобное условие без использования библиотеки PyGame, и я не могу этого сделать.
Par пример:
>>> class A(object):
... def __init__(self, x, y):
... self.x = x
... self.y = y
... def go(self):
... self.x += 1
... self.y += 2
... print "-go-"
... def stop(self):
... print "-stop-"
... print "(%d, %d)" % (self.x, self.y)
... def update(self):
... self.go()
... if self.x + self.y > 200:
... self.stop()
>>> Stick = A(15, 15)
>>> Stick.go()
-go-
>>> Stick.update()
-go-
>>> Stick.x
17
>>> Stick.y
19
>>> def whereat(x, y):
... print "I find myself at (%d, %d)." % (x, y)
...
>>> def wrap(obj, func):
... def inner(*args, **kwargs):
... newX, newY = obj.x, obj.y
... whereat(newX, newY)
... return func(*args, **kwargs)
... return inner
...
>>> Stick.update = wrap(Stick, Stick.update)
>>> Stick.update()
I find myself at (17, 19).
-go-
>>> Stick.update()
I find myself at (18, 21).
-go-
>>> Stick.update()
I find myself at (19, 23).
-go-
Итак, я сразу заметил, что whereat()
вызывается перед тем изменение координат при Stick.go()
так он использует x
и y
непосредственно перед приращением, но это нормально; Я мог бы легко изменить оболочку, чтобы подождать, пока не вызывается go()
. Проблема здесь отсутствует, как это было в моем проекте PyGame; взрывы не были даже «смежными врагами», они появлялись во всех видах случайных мест, а не в предыдущих (x, y) координатах спрайта (если бы это было, я, возможно, даже не заметил проблемы!).
UPDATE: После того как пользователь комментарий получил мне интересно, я порылся в Интернете немного, побежал cProfile
по проекту в вопросе, и вот - PyGame определенно использует threading.py
. К сожалению, тот же Интернет был недостаточно хорош, чтобы сказать мне точно , как PyGame использует threading.py
, поэтому, видимо, мне нужно прочитать, как потоки даже работают. Oi. :)
После прочтения исходного кода PyGame немного (bleh) Я нашел один модуль, который, как представляется, разрешает, когда и почему PyGame порождает поток. Из того, что я знаю о потоках, это может привести к чему-то вроде состояния гонки; x не определяется «во времени» для выполнения оболочки, и поэтому (объект Explosion
) заканчивается в неправильном месте. Я видел другие ошибки, возникающие в другом проекте, который немного тяжелее в математике; но они обычно включают pulse
, которые не могут получить событие или время ожидания события или что-то подобное (у меня есть трассировка длинного стека, лежащая вокруг, в случае, если у кого-то возникли проблемы спать).
В конце дня я подозреваю, что это то, чего я могу избежать, придерживаясь определенных практик, но я не знаю, что эти практики на самом деле будет, и я не знаю, как предотвратить PyGame от арбитража, какие процессы получают свои собственные потоки, а когда это хуже, чем НЕ. Если для всего этого есть единая теория, я бы с удовольствием узнал об этом.
Вы не используете многопоточность/многопроцессорность для чего-либо в своей игре, не так ли? –
Конечно, не намеренно. Я * думаю * PyGame делает некоторую многопоточность самостоятельно, без прямого вызова пользователем любого из них, но я не уверен. –
Я не верю, потому что это затрудняет создание игр с использованием pygame на малине pi, которые, как многие знают, очень возможны. – KodyVanRy