Перейти к содержимому

Как удалить объект в pygame

  • автор:

Как удалить объект с экрана в Pygame?

Пишу игру на PyGame, столкнулся с такой проблемой, как удаление спрайта — нигде в документации, на форумах нет информации о том, как удалить спрайт
Есть метод .kill(), но лично у меня он не работает — перестает работать перемещение объекта и т.д, но с экрана сам объект не удаляется. Как это можно сделать?
Код:

import random se_tostring = str import time import pygame, sys from pygame.locals import * pygame.init() pygame.font.init() pygame_screen = pygame.display.set_mode((0,0),pygame.FULLSCREEN) pygame_all_sprites = pygame.sprite.Group() #SE FUNCS def readFile(path): return open(path, 'r').read() def split(str,sep): return str.split(sep) import datetime now = datetime.datetime.now pygameObject_bg = pygame.sprite.Sprite(pygame_all_sprites) try: pygameObject_bg.image = pygame.image.load("bg.png") pygameObject_bg.rect = pygameObject_bg.image.get_rect() except Exception: print("Файл не найден") pygameObject_bg_pos = (-1, 0) pygameObject_bg.rect.x = pygameObject_bg_pos[0] pygameObject_bg.rect.y = pygameObject_bg_pos[1] pygameObject_zombie = pygame.sprite.Sprite(pygame_all_sprites) try: pygameObject_zombie.image = pygame.image.load("zomb.png") pygameObject_zombie.rect = pygameObject_zombie.image.get_rect() except Exception: print("Файл не найден") pygameObject_zombie_pos = (50, -10) pygameObject_zombie.rect.x = pygameObject_zombie_pos[0] pygameObject_zombie.rect.y = pygameObject_zombie_pos[1] pygameObjectText_text1 = pygame.font.SysFont( 'serif', 48).render("text", True, (0,0,0) ) pygame_screen.blit( pygameObjectText_text1 , (0,0) ) while True: pygame.display.flip() pygame.display.update() pygame_all_sprites.draw(pygame_screen) for pygame_event in pygame.event.get(): if pygame_event.type == pygame.KEYDOWN and pygame_event.key == pygame.K_o: pygameObject_zombie_pos = (pygameObject_zombie_pos[0]+5, 0) pygameObject_zombie.rect.x = pygameObject_zombie_pos[0] pygameObject_zombie.rect.y = pygameObject_zombie_pos[1] if pygame_event.type == pygame.KEYDOWN and pygame_event.key == pygame.K_o: pygameObject_zombie.kill()
  • Вопрос задан более двух лет назад
  • 3544 просмотра

Классы Sprite и Group

В программировании игр спрайтом называют объект, который предстает перед пользователем в виде анимированного изображения и в большинстве случаев предполагает взаимодействие с ним. Другими словами, все что в игре не является фоном, а интерактивным объектом-картинкой – это спрайт.

Хотя каждый спрайт может быть уникальным, у всех есть нечто общее, что в pygame вынесено в отдельный класс Sprite , находящийся в модуле pygame.sprite .

На базе этого класса следует создавать собственные классы спрайтов и уже от них объекты. Таким образом, класс pygame.sprite.Sprite играет роль своего рода абстрактного класса. Хотя таковым не является, можно создавать объекты непосредственно от Sprite .

В модуле pygame.sprite кроме класса Sprite есть класс Group и родственные ему, которые предназначены для объединения спрайтов в группы. Это позволяет вызывать один метод группы, который, например, обновит состояние всех спрайтов, входящих в эту группу.

Почти все предопределенные методы класса pygame.sprite.Sprite касаются добавления экземпляра в группу, удаления из нее, проверки вхождения. Только метод update() затрагивает поведение самого спрайта, этот метод следует переопределить в производном от Sprite классе.

Рассмотрим, как это работает. В примерах кода ниже сначала одна, а потом и множество машинок перемещаются сверху вниз. Каждая такая машинка – объект-спрайт, созданный от класса Car , который является дочерним от Sprite .

В конструкторе производного от Sprite класса необходимо вызвать конструктор родительского класса, а также обзавестись экземплярами Surface и Rect , имена которых должны быть соответственно self.image и self.rect . Так надо, чтобы с экземплярами класса могли работать методы группы. В остальном вы можете добавлять любые атрибуты.

Как создается поверхность (а также прямоугольная область), неважно. В примере ниже это делается с помощью функции load() . Однако в конструктор может передаваться уже подготовленный экземпляр Surface .

from random import randint import pygame as pg import sys W = 400 H = 400 WHITE = (255, 255, 255) class Car(pg.sprite.Sprite): def __init__(self, x, filename): pg.sprite.Sprite.__init__(self) self.image = pg.image.load(filename).convert_alpha() self.rect = self.image.get_rect(center=(x, 0)) sc = pg.display.set_mode((W, H)) # координата x будет случайна car1 = Car(randint(1, W), 'car1.png') while 1: for i in pg.event.get(): if i.type == pg.QUIT: sys.exit() sc.fill(WHITE) sc.blit(car1.image, car1.rect) pg.display.update() pg.time.delay(20) # машинка ездит сверху вниз if car1.rect.y  H: car1.rect.y += 2 else: car1.rect.y = 0

В данном случае мы изменяем свойства экземпляра за пределами класса. Правильней будет делать это в методе update() :

. class Car(pg.sprite.Sprite): def __init__(self, x, filename): pg.sprite.Sprite.__init__(self) self.image = pg.image.load(filename).convert_alpha() self.rect = self.image.get_rect(center=(x, 0)) def update(self): if self.rect.y  H: self.rect.y += 2 else: self.rect.y = 0 sc = pg.display.set_mode((W, H)) # координата x будет случайна car1 = Car(randint(1, W), 'car1.png') while 1: for i in pg.event.get(): if i.type == pg.QUIT: sys.exit() sc.fill(WHITE) sc.blit(car1.image, car1.rect) pg.display.update() pg.time.delay(20) car1.update()

Теперь представим, что у нас не одна машинка, а три:

. car1 = Car(randint(1, W), 'car1.png') car2 = Car(randint(1, W), 'car2.png') car3 = Car(randint(1, W), 'car3.png') while 1: for i in pg.event.get(): if i.type == pg.QUIT: sys.exit() sc.fill(WHITE) sc.blit(car1.image, car1.rect) sc.blit(car2.image, car2.rect) sc.blit(car3.image, car3.rect) pg.display.update() pg.time.delay(20) car1.update() car2.update() car3.update()

Если будет 100 машинок, придется 100 раз вызвать blit() и update() . Класс Group решает эту проблему. Добавлять спрайты в группу можно методом add() группы (по одной или все вместе).

У групп есть методы update() и draw() . Метод update() группы вызывает методы update() всех входящих в нее объектов. А метод draw() выполняет метод blit() . При этом в draw() надо передать поверхность, на которой будет происходить отрисовка:

. cars = pg.sprite.Group() cars.add(Car(randint(1, W), 'car1.png'), Car(randint(1, W), 'car2.png')) cars.add(Car(randint(1, W), 'car3.png')) while 1: for i in pg.event.get(): if i.type == pg.QUIT: sys.exit() sc.fill(WHITE) cars.draw(sc) pg.display.update() pg.time.delay(20) cars.update()

Допустим, мы хотим, чтобы новые машинки появлялись постоянно и в разные моменты времени, двигались с разной скоростью, а выезд объекта за пределы экрана обозначал бы, что он исчезает.

Потребуется таймер, который устанавливается вызовом функции pygame.time.set_timer() . В примере ниже через каждые 3 секунды будет генерироваться событие, значение поля type которого совпадает с константой pygame.USEREVENT . И как только это событие будет происходить, будет создаваться новый объект.

from random import randint import pygame as pg import sys pg.init() pg.time.set_timer(pg.USEREVENT, 3000) W = 400 H = 400 WHITE = (255, 255, 255) CARS = ('car1.png', 'car2.png', 'car3.png') # для хранения готовых машин-поверхностей CARS_SURF = [] # надо установить видео режим до вызова image.load() sc = pg.display.set_mode((W, H)) for i in range(len(CARS)): CARS_SURF.append(pg.image.load(CARS[i]).convert_alpha()) class Car(pg.sprite.Sprite): def __init__(self, x, surf, group): pg.sprite.Sprite.__init__(self) self.image = surf self.rect = self.image.get_rect(center=(x, 0)) # добавляем в группу self.add(group) # у машин будет разная скорость self.speed = randint(1, 3) def update(self): if self.rect.y  H: self.rect.y += self.speed else: # теперь не перебрасываем вверх, # а удаляем из всех групп self.kill() cars = pg.sprite.Group() # добавляем первую машину, которая появляется сразу Car(randint(1, W), CARS_SURF[randint(0, 2)], cars) while 1: for i in pg.event.get(): if i.type == pg.QUIT: sys.exit() elif i.type == pg.USEREVENT: Car(randint(1, W), CARS_SURF[randint(0, 2)], cars) sc.fill(WHITE) cars.draw(sc) pg.display.update() pg.time.delay(20) cars.update()

Метод kill() спрайта удаляет его из всех групп, в которых он содержится. Есть метод remove() , который удаляет только из указанных в качестве аргумента групп. У спрайтов также как у групп есть метод add() . Только в данном случае ему передается не объект, а группа.

Практическая работа

В модуле pygame.sprite есть ряд функций для проверки коллизий спрайтов. Одна из них spritecollideany() проверяет, столкнулся ли конкретный спрайт с любым из спрайтов из группы. Функция принимает первым аргументом спрайт, чья коллизия проверяется, вторым – группу.

Измените программу выше так, чтобы машинки появлялись чаще. Добавьте спрайт, который «едет» навстречу всем другим и управляется стрелками влево и вправо на клавиатуре. Цель игры – не допустить столкновения. Если оно происходит, то программа завершается.

Курс с примерами решений практических работ:
pdf-версия

X Скрыть Наверх

Pygame. Введение в разработку игр на Python

Деструктор в Python: уничтожение объектов

Деструктор в Python – это специальный метод, который вызывается при уничтожении объекта. Конструктор же, наоборот, используется для создания и инициализации объекта класса.

В этой статье мы разберем:

  • как создать деструктор в Python
  • использование метода __del__()
  • как работает деструктор.

Что такое деструктор в Python?

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

В Python есть сборщик мусора, который автоматически обрабатывает управление памятью. Например, он очищает память, когда объект выходит за пределы области видимости.

Однако при уничтожении объекта необходимо освобождать не только память. Мы должны освободить или закрыть другие ресурсы, которые использовались объектом, такие как открытые файлы, соединения с базой данных, очистка буфера или кеша. Для выполнения всех этих задач очистки мы используем деструктор в Python.

Деструктор противоположен конструктору. Конструктор используется для инициализации объектов, а деструктор — для удаления или уничтожения объектов, в результате чего освобождается ресурс, занятый этими объектами.

В Python деструктор вызывается не вручную, а полностью автоматически. Это происходит в следующих двух случаях:

  • когда объект выходит за пределы области видимости
  • когда счетчик ссылок на объект достигает 0.

Для определения деструктора используется специальный метод __del__() . Например, когда мы выполняем del имя_объекта , деструктор вызывается автоматически, и объект собирается в мусор.

Создание деструктора с помощью метода __del__()

Магический метод __del__() используется как деструктор в Python. Метод __del__() будет неявно вызываться, когда все ссылки на объект будут удалены, то есть когда объект подходит для сборщика мусора.

Этот метод автоматически вызывается в Python, когда экземпляр собираются уничтожить. Его также называют финализатором или (неправильно) деструктором.

Синтаксис объявления деструктора будет следующим:

def __del__(self): # тело деструктора
  • def – ключевое слово, которое используется для определения метода.
  • __del__() – зарезервированный метод. Он вызывается, как только все ссылки на объект будут удалены.
  • self : первый аргумент self относится к текущему объекту.

Примечание. Аргументы метода __del__() необязательны. Мы можем определить деструктор с любым количеством аргументов.

Пример

Давайте рассмотрим создание деструктора в Python на простом примере. Мы создадим класс Student с деструктором.

class Student: # конструктор def __init__(self, name): print('Inside Constructor') self.name = name print('Object initialized') def show(self): print('Hello, my name is', self.name) # деструктор def __del__(self): print('Inside destructor') print('Object destroyed') # создать объект s1 = Student('Emma') s1.show() # удалить объект del s1

Запустим наш код и получим следующий результат:

Inside Constructor Object initialized Hello, my name is Emma Inside destructor Object destroyed

Примечание. Как видно из вывода, при удалении ссылки на объект с помощью del s1 метод __del__() вызывается автоматически.

В приведенном выше коде мы создали один объект. s1 – это ссылочная переменная, указывающая на вновь созданный объект.

Деструктор вызывается, когда ссылка на объект удалена или счетчик ссылок на объект доходит до нуля.

Важные моменты, которые следует помнить о деструкторе

  • Метод __del__() вызывается для любого объекта, когда счетчик ссылок для этого объекта становится равным нулю.
  • Счетчик ссылок для данного объекта становится нулевым, когда работа программы завершается, или мы удаляем все ссылки вручную с помощью ключевого слова del .
  • Деструктор не будет запускаться при удалении какой-то одной ссылки на объект. Он будет вызываться только тогда, когда все ссылки на объект будут удалены.

Пример

Давайте разберемся в приведенных выше пунктах на примере.

Сначала создадим объект класса Student , используя s1 = student (‘Emma’) .

Затем давайте создадим новую ссылку на объект, присвоив переменной s2 значение s1 (т.е. s2 = s1 ).

Теперь обе ссылочные переменные s1 и s2 указывают на один и тот же объект.

От редакции Pythonist. Ссылки, имена и значения подробно рассмотрены в статье «Факты и мифы об именах и значениях в Python».

Далее мы удалим ссылку s1 .

Затем добавим 5 секунд задержки (sleep) к основному потоку, чтобы было ясно, что деструкторы вызываются только при удалении всех ссылок на объекты.

import time class Student: # конструктор def __init__(self, name): print('Inside Constructor') self.name = name def show(self): print('Hello, my name is', self.name) # деструктор def __del__(self): print('Object destroyed') # создание объекта s1 = Student('Emma') # создание новой ссылки # обе ссылки указывают на один объект s2 = s1 s1.show() # удаление ссылки s1 del s1 # добавление задержки и наблюдение за результатом time.sleep(5) print('After sleep') s2.show()
Inside Constructor Hello, my name is Emma
After sleep Hello, my name is Emma Object destroyed
  • Как вы можете видеть из полученного результата, деструкторы вызываются только тогда, когда удаляются все ссылки на объекты.
  • Кроме того, деструктор выполняется, когда код (программа) заканчивается и объект становится доступным для сборщика мусора. Например, мы не удаляли ссылку на объект s2 вручную с помощью del s2 . Это произошло автоматически, т.к. программа закончилась.

Случаи, когда деструктор работает не корректно

__del__() не является идеальным решением для очистки ненужных объектов. В Python деструктор ведет себя странно и не выполняется в следующих двух случаях:

  • ссылка является круговой: два объекта ссылаются друг на друга
  • исключение в методе __init__()

Круговая ссылка

__del__() некорректно работает в случае циклической ссылки.Суть этой ссылки в том, что два объекта ссылаются друг на друга. И когда оба объекта выходят за пределы области видимости, Python не знает, какой объект уничтожить первым. Поэтому, чтобы избежать ошибок, он не уничтожает ни один из них.

Короче говоря, это означает, что сборщик мусора не знает порядок, в котором объекты должны быть уничтожены, поэтому он не удаляет их из памяти.

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

Пример

В приведенном ниже примере в идеале оба объекта — Vehicle и Car — должны быть уничтожены сборщиком мусора после выхода за пределы области видимости. Тем не менее, из-за циклической ссылки они остаются в памяти.

Для управления ресурсами, которые необходимо очистить, можно посоветовать использовать оператор with .

import time class Vehicle(): def __init__(self, id, car): self.id = id; # сохранение ссылки объекта Car self.dealer = car; print('Vehicle', self.id, 'created'); def __del__(self): print('Vehicle', self.id, 'destroyed'); class Car(): def __init__(self, id): self.id = id; # сохранение объекта класса Vehicle в переменной 'dealer' # пересылка ссылки объекта Car ('self') для объекта Vehicle self.dealer = Vehicle(id, self); print('Car', self.id, 'created') def __del__(self): print('Car', self.id, 'destroyed') # создание объекта car c = Car(12) # удаление объекта car del c # в идеале теперь должен выполниться деструктор # задержка для наблюдения за поведением time.sleep(8)

Запустим наш код и получим следующее:

Vehicle 12 created Car 12 created

Исключение в методе __init__()

В объектно-ориентированном программировании конструктор – это специальный метод, используемый для создания и инициализации объекта класса. Используя метод __init__() , мы можем реализовать конструктор для инициализации объекта.

В ООП, если в конструкторе возникает какое-либо исключение при инициализации объекта, конструктор уничтожает объект.

Аналогично, в Python, если в методе инициализации возникает какое-либо исключение при инициализации объекта, вызывается метод del . Но на самом деле объект не создается, и ему не выделяются ресурсы.

Несмотря на то, что объект так и не был инициализирован правильно, метод del все равно попытается очистить все ресурсы. А это, в свою очередь, может привести к другому исключению.

Пример
class Vehicle: def __init__(self, speed): if speed > 240: raise Exception('Not Allowed'); self.speed = speed; def __del__(self): print('Release resources') # создание объекта car = Vehicle(350); # для явного удаления объекта: del car

Запустив этот код, мы получим следующий результат:

Traceback (most recent call last): Release resources Exception: Not Allowed

Заключение

В объектно-ориентированном программировании деструктор вызывается при удалении или уничтожении объекта.

Деструктор используется для выполнения действий по очистке перед разрушением объекта, таких как закрытие соединений с базой данных.

Для выполнения задачи очистки перед удалением объекта в Python мы используем метод __del__() .

При удалении ссылки на объект деструктор не запускается. Для этого нужно удалить все ссылки на этот объект.

Как удалить объект спрайта Pygame?

Мне нужно удалять спрайт из массива, когда он выходит за границу.
Вот код:

import pygame pygame.init() win = pygame.display.set_mode((700, 400)) pygame.display.set_caption("Защита") pygame.display.set_icon(pygame.image.load("images/sheald.png")) fon = pygame.image.load("images/fon.png") class Bullet(pygame.sprite.Sprite): def __init__(self, y, speed, image): pygame.sprite.Sprite.__init__(self) self.image = pygame.image.load(image).convert_alpha() self.rect = self.image.get_rect(center=(70, y + 20)) self.speed = speed def update(self): self.rect.x += self.speed win.blit(self.image, self.rect) playerY = 70 playerX = 20 bull = [] bulletsNumber = 10 FPS = 60 def shoot(): global bulletsNumber if bulletsNumber > 0: bulletsNumber -= 1 bullet = Bullet(playerY, 2, 'images/bullet.png') bull.append(bullet) def window(): win.blit(fon, (0, 0)) pygame.draw.rect(win, (236, 218, 103), (playerX, playerY, 40, 40)) for bul in bull: bul.update() if bul.rect.x >= 700: **тут нужно удалить объект** 

window — это обновление экрана shoot — создание объекта Как это сделать?

Отслеживать

12.5k 7 7 золотых знаков 18 18 серебряных знаков 48 48 бронзовых знаков

задан 26 мар 2021 в 16:25

Tikhon_ Yaremchuk Tikhon_ Yaremchuk

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *