Отрисовка объекта из Класса (и его последующего движения) Python 3 модуля - Pygame
Изучаю python c недавнего времени и модуль Pygame. Есть объект (в данном примере Линия). Нужно создать из определенное количество, после чего двигать каждый объект по отдельности куда бы то ни было. В данном случае "от центра". Хочу сделать чтобы Класс создавал объект который двигался от центра, а следовательно изменял свои координаты. Создать некое количество объектов не проблема, но с их движением все иначе.
Как я это вижу. Идет закрашивание экрана (для обновления и удаления старых рисунков), далее отрисовка объектов с их позициями, но при следующем обновлении (кадре) идет прорисовка заново (Черный экран, и каждый объект создается заново не зная свою позицию, а беря ее из списка). Это то как, получилось пока.
import pygame
from random import*
top, side = (0, 0)
FPS = 144
run = True
clock = pygame.time.Clock()
width = 640
height = 360
amount = 100
num = []
conditions = [(-10, -10), (width + 10, height + 10),
(width + 10, -10), (-10, height + 10)]
c = conditions
for i in range(amount):
num.append((randint(0, width), randint(0, height)))
def check(num):
global top, side
x, y = num
minus, plus = (-1, 1)
if x <= width / 2 and y <= height / 2:
side, top = (minus, minus)
elif x >= width / 2 and y <= height / 2:
side, top = (plus, minus)
elif x <= width / 2 and y >= height / 2:
side, top = (minus, plus)
elif x >= width / 2 and y >= height / 2:
side, top = (plus, plus)
elif x == width / 2 and y == height / 2:
side, top = (plus, plus)
class star:
def __init__(self, num):
self.x, self.y = num
self.check()
pygame.draw.aaline(win, (255, 0, 0),
[self.x, self.y], [self.x + self.sx, self.y + self.sy])
def check(self):
self.minus, self.plus = (-1, 1)
if self.x <= width / 2:
self.sx = -10
self.side = self.minus
else:
self.sx = 10
self.side = self.plus
if self.y <= height / 2:
self.sy = -10
self.top = self.minus
else:
self.sy = 10
self.top = self.plus
#pygame.draw.aaline(win, (255, 0, 0), [10, 70], [290, 55])
pygame.init()
win = pygame.display.set_mode((width, height))
while run:
clock.tick(FPS)
win.fill((0, 0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT: run = False
#------Draw----------------
for i in range(amount):
check(num[i])
num[i] = (num[i][0] + (side), num[i][1] + (top))
for i in range(amount):
n = num[i]
star(n)
if n <= c[0] or n >= c[1] or n >= c[2] or n <= c[3] or n == (width / 2, height / 2):
num[i] = (randint(0, width), randint(0, height))
#------------------------
pygame.display.flip()
pygame.quit()
Хотелось бы чтобы объект(Класс) сам помнил свою позицию и двигался от центра. Есть ли возможность это написать, как то компактнее и понятнее?
Как то непонятно описал. Если добавлю несколько объектов в область --Draw--
#------Draw----------------
for i in range(amount):
check(num[i])
num[i] = (num[i][0] + (side), num[i][1] + (top))
for i in range(amount):
n = num[i]
star(n)
if n <= c[0] or n >= c[1] or n >= c[2] or n <= c[3] or n == (width / 2, height / 2):
num[i] = (randint(0, width), randint(0, height))
#------------------------
pygame.display.flip()
pygame.quit()
То они нарисуются в разных местах и как надо, а если захочу до черного экрана, чтобы присвоить их к чему-то и как хранить?
@virvaldium, спасибо за помощь. На основе ваших примеров сделал такой код:
import pygame
from random import*
FPS = 60
run = True
clock = pygame.time.Clock()
width = 640
height = 360
amount = 100
class GameManager:
def __init__(self, game_window, array):
self.game_window = game_window
self.array = array
def redraw(self):
self.game_window.fill((0, 0, 0))
for star in self.array:
star.set_new_position()
class Star:
def __init__(self, game_window, start_x, start_y):
self.x = start_x
self.y = start_y
self.game_window = game_window
self.draw()
def draw(self):
pygame.draw.circle(self.game_window, (255, 0, 0),
[self.x, self.y], 3)
def set_new_position(self):
self.x += 1
self.y += 1
self.draw()
pygame.init()
win = pygame.display.set_mode((width, height))
array = [Star(win, randint(0, width),
randint(0, height)) for i in range(amount)]
manager = GameManager(win, array)
while run:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
manager.redraw()
pygame.display.flip()
pygame.quit()
Разбирался долго, зато понял). Создать количество теперь можно и задать им действия тоже не проблема, дело за малым)
Ответы (1 шт):
Ничего не понятно, но очень интересно... Если я правильно понял суть написанного, то необходимо сделать как-то так, что бы объекты порожденные от класса Star самостоятельно двигались от центра к краям экрана? То есть звездочки у Вас будут летать по экрану.
На мой взгляд в Вашем коде проблема в большей степени архитектурная. Я бы на вашем месте придумал бы какой-нибудь менеджер, который бы управлял перерисовкой игрового поля. Предупреждаю сразу - я с pygame работал очень давно, так что на истинность кода не претендую, тут я показываю только концепцию. Выглядело бы это примерно так:
class GameManager:
def __init__(self, game_window, star_array):
self.game_window = game_window # содержит ссылку на игровое поле
self.star_array = star_array # список всех звезд, которыми нужно управлять
def redraw(self):
self.game_window.fill((0, 0, 0)) # закрашиваем поле в черный цвет
for star in self.star_array:
star.set_new_position() # перерисовываем позиции звезд
класс Star выглядел бы при этом как-то так:
class Star:
def __init__(self, game_window, start_x, start_y):
self.x = start_x
self.y = start_y
self.game_window = game_window
self.draw()
def draw(self):
# тут логика отрисовки объекта на self.game_window по координатам self.x и self.y
pass
def set_new_position(self):
# тут будут задаваться новые координаты и после вызываться метод self.draw()
pass
Старт программы бы выглядел как-то так:
if __name__ == "__main__":
pygame.init()
win = pygame.display.set_mode((width, height))
start_array = [star for star in Star(win, randint(0, 10), randint(0, 10))]
manager = GameManager(win, star_array)
while True:
manager.redraw()
Я надеюсь, что Вы понимаете - это просто концепция, того как бы сделал это я. В случае с Вашим подходом... Наверное можно было бы в начале каждого цикла перекрашивать поле в черный цвет:
for i in range(amount):
win = pygame.display.set_mode((width, height)) # <<<<< добавить какую-то такую строчку, или как там в pygame задается цвет игрового поля...
n = num[i]
star(n)
if n <= c[0] or n >= c[1] or n >= c[2] or n <= c[3] or n == (width / 2, height / 2):
num[i] = (randint(0, width), randint(0, height))
но такой подход, когда пересоздаются объекты класса Star, выглядит, на мой взгляд, несколько не оптимальным и запутанным. Инициализация объекта достаточно долгая операция, плюс каждый раз будет заниматься дополнительная область в памяти (когда там ее сборщик мусора обработает...). А если потребуется изменить механизм полета звезд? А если потребуется, что бы одни звезды летали по кругу, а не от центра к краю? А когда дело дойдет до взаимодействия с игроком? - тут перерисовку звезд придется учитывать с тем объектом, которым управляет игрок.
Повторюсь еще раз, с библиотекой pygame я работал мало и давно. В своем ответе показываю только концептуальный подход к поставленной задаче
P.S. Ну и плюсом... Я бы настоятельно рекомендовал:
- перестать использовать глобальные переменные для обмена данными - это зло во плоти.
- почитать PEP8
- почитать как работает типизация в питоне и чем отличаются изменяемы и не изменяемы типы данных друг от друга:
conditions = [(-10, -10), (width + 10, height + 10), (width + 10, -10), (-10, height + 10)]
c = conditions
Зачем Вы делаете две ссылки на один и тот же массив? с и conditions будут оперировать одним и тем же списком и изменять его мешая работе друг друга. Такие баги очень сложно отыскивать. Если хотите, скопировать один list в другой, то почитайте про функции copy и deepcopy из стандартного модуля copy