У меня возникли две проблемы, связанные с моим непониманием того, как Pygame обрабатывает спрайты (не помогает тот факт, что некоторые учебники кажутся устаревшими, но найти обходные пути забавно :)).
Сначала соответствующий код, затем мои проблемы:
class Controller():
if getattr(sys, 'frozen', False):
path = os.path.dirname(sys.executable)
elif __file__:
path = os.path.dirname(__file__)
else:
print('No path has been defined')
windowWidth = 800
windowHeight = 600
def __init__(self):
self._running = True
self.sprites = pygame.sprite.Group()
self.callback = None
def on_init(self):
pygame.init()
pygame.display.set_caption('Ourstory')
self.clock = pygame.time.Clock()
self.screen = pygame.display.set_mode((self.windowWidth, self.windowHeight))
self.menu('MainMenu')
self._running = True
def on_event(self, event):
if event.type == QUIT:
self._running = False
elif event.type == TURN:
self.turn += 1
def on_loop(self):
pass
def on_cleanup(self):
pygame.quit()
def on_execute(self):
if self.on_init() == False:
self._running = False
while (self._running):
self.clock.tick(60)
pygame.event.pump()
#self.update_button()
#self.sprites._update_image(events)
self.screen.fill(pygame.Color('white'))
self.sprites.draw(self.screen)
pygame.display.update()
keys = pygame.key.get_pressed()
if (keys[K_ESCAPE]):
self._running = False
self.on_loop()
self.on_cleanup()
def menu(self, name:str):
if name == 'MainMenu':
self.sprites.add(Controller.load_image(self, 'TOC_EarthDawn800600.png', 0, 0))
#Controller.load_button(self, 'SelectCivilisation', 'Culture3232.png', 'Espionage3232.png', 32, 32, 32, 32, -1, 'Select Civilisation', 'freesansbold.ttf', 8, (0,0,0))
self.sprites.add(Button('TestButton', (128,128,128), (160,160,160), (0,0,0), pygame.Rect(150, 200, 90, 100), QUIT))#, 'text', 'freesansbold.ttf', 8, (0,0,0)))
elif name == 'NewGame':
Controller.load_image(self, 'TOC_EarthDusk800600.png', 0, 0)
else:
print('Cannot find menu: ', name)
def load_image(self, name:str, x:int, y:int, colorkey=None):
fullname = os.path.join('Images', name)
try:
image = pygame.image.load(fullname)
except:
pygame.error
print('Cannot find image: ', name)
image = image.convert()
if colorkey is not None:
if colorkey is -1:
colorkey = image.get_at((0, 0)) #i.e. top-left pixel
image.set_colorkey(colorkey, RLEACCEL)
self.screen.blit(image, (x, y))
pygame.display.flip()
class Button(pygame.sprite.Sprite):
def __init__(self, name:str, colourinactive:str, colouractive:str, colouroutline:str, rect, callback, text=None, font='freesansbold.ttf', fontsize=8, textcolour=(0,0,0), imageinactive=None, imageactive=None, colorkey=None):
super().__init__()
self.text = text
temprect = pygame.Rect(0, 0, *rect.size)
if not imageinactive:
self.inactive = self._create_image(colourinactive, colouroutline, temprect, text)
self.active = self._create_image(colouractive, colouroutline, temprect, text)
self.image = self.inactive
self.rect = rect
self.callback = callback
def _create_image(self, colourinactive:str, colouroutline:str, rect, text=None, font='freesansbold.ttf', fontsize=8, textcolour=(0,0,0)):
imagesurface = pygame.Surface(rect.size)
if colouroutline:
imagesurface.fill(colouroutline)
imagesurface.fill(colourinactive, rect.inflate(-4, -4))
else:
imagesurface.fill(colourinactive)
if text:
font = pygame.font.Font(font, fontsize)
print(type(textcolour))
textsurface = font.render(text, True, pygame.Color(textcolour))
textrect = textsurface.get_rect(center=rect.center)
img.blit(textsurface, textrect)
return imagesurface
def _update_image(self, events):
mouse = pygame.mouse.get_pos()
active = self.rect.collidepoint(mouse)
self.image = self.active if active else self.inactive
for event in events:
if event.type == pygame.MOUSEBUTTONDOWN and hit:
self.callback(self)
1) В def menu(self, name:str):
я создаю свои меню и даю им фоновое изображение с функцией load_image, которую я объявляю ниже.В настоящее время у меня self.sprites.add
перед фоном 'MainMenu'
, что заставляет его работать на мгновение (я вижу изображение), а затем сразу вылетает:
AttributeError: 'NoneType' object has no attribute 'add_internal'
Если я не поставлю self.sprites.add
там, как в случае с меню 'NewGame'
, код работает, но этот фрагмент кода создает полностью белый фон, так что я не вижу свое фоновое изображение:
self.screen.fill(pygame.Color('white'))
self.sprites.draw(self.screen)
pygame.display.update()
Поэтому мне нужно найти способ добавить фоновое изображение, которое я обрабатываю с помощью функции load_image, в группу спрайтов.Я не уверен, как это сделать, что является проблемой 1).
2) Помимо универсальной функции для быстрого создания изображений, я хочу универсальную функцию для быстрого создания кнопок.Это я делаю с class Button(pygame.sprite.Sprite):
.Однако, хотя текущий код показывает кнопку, _update_image
, похоже, не работает.У меня проблема в том, что я обрабатываю свои события в while (self._running):
из class Controller():
, и что моя группа спрайтов является частью def __init__(self):
, также из class Controller():
.Я следовал примеру ленивца, здесь , но для своих целей я не могу (не думаю, что это разумно?) Определять все как часть class Button
или просто вне какого-либо класса,Как бы вы решили это?
Я надеюсь, что это понятно.Спасибо большое за помощь!:)
РЕДАКТИРОВАТЬ в ответ на Rabbid76:
Спасибо за подробный ответ!К сожалению, ваш код не работал (кроме дополнительного) в img = self.load_image(self, 'TOC_EarthDawn800600.png', 0, 0))
), он выдает мне ошибку:
Traceback (most recent call last):
line 243, in <module>
Game.on_execute()
line 59, in on_execute
if self.on_init() == False:
line 47, in on_init
self.menu('MainMenu')
line 76, in menu
background = self.load_image(self, 'TOC_EarthDawn800600.png', 0, 0)
line 88, in load_image
fullname = os.path.join('Images', name)
line 114, in join
genericpath._check_arg_types('join', path, *paths)
line 149, in _check_arg_types
(funcname, s.__class__.__name__)) from None
TypeError: join() argument must be str or bytes, not 'Controller'
Я немного расширил свой код, такой как ниже,но это возвращает меня к этой ошибке: AttributeError: 'pygame.Surface' object has no attribute 'add_internal'
class Controller():
if getattr(sys, 'frozen', False):
path = os.path.dirname(sys.executable)
elif __file__:
path = os.path.dirname(__file__)
else:
print('No path has been defined')
windowWidth = 800
windowHeight = 600
def __init__(self):
self._running = True
self.sprites = pygame.sprite.Group()
self.callback = None
def on_init(self):
pygame.init()
pygame.display.set_caption('Ourstory')
self.clock = pygame.time.Clock()
self.screen = pygame.display.set_mode((self.windowWidth, self.windowHeight))
self.menu('MainMenu')
self._running = True
def on_event(self, event):
if event.type == QUIT:
self._running = False
elif event.type == TURN:
self.turn += 1
def on_loop(self):
pass
def on_cleanup(self):
pygame.quit()
def on_execute(self):
if self.on_init() == False:
self._running = False
while (self._running):
self.clock.tick(60)
pygame.event.pump()
#self.update_button()
#self.sprites._update_image(events)
self.screen.fill(pygame.Color('white'))
self.sprites.draw(self.screen)
pygame.display.update()
keys = pygame.key.get_pressed()
if (keys[K_ESCAPE]):
self._running = False
self.on_loop()
self.on_cleanup()
def menu(self, name:str):
if name == 'MainMenu':
background = Image._create_image(self, 'TOC_EarthDawn800600.png', 0, 0)
self.sprites.add(background)
#self.sprites.add(Controller.load_image(self, 'TOC_EarthDawn800600.png', 0, 0))
##Controller.load_button(self, 'SelectCivilisation', 'Culture3232.png', 'Espionage3232.png', 32, 32, 32, 32, -1, 'Select Civilisation', 'freesansbold.ttf', 8, (0,0,0))
#self.sprites.add(Button('TestButton', (128,128,128), (160,160,160), (0,0,0), pygame.Rect(150, 200, 90, 100), QUIT))#, 'text', 'freesansbold.ttf', 8, (0,0,0)))
elif name == 'NewGame':
Controller.load_image(self, 'TOC_EarthDusk800600.png', 0, 0)
else:
print('Cannot find menu: ', name)
class Image(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
def _create_image(self, name, x, y, colorkey=None):
self.name = name
fullname = os.path.join('Images', name)
try:
image = pygame.image.load(fullname)
except:
pygame.error
print('Cannot find image: ', name)
image = image.convert()
if colorkey is not None:
if colorkey is -1:
colorkey = image.get_at((0, 0)) # i.e. top-left pixel
image.set_colorkey(colorkey, RLEACCEL)
return image
class Button(pygame.sprite.Sprite):
def __init__(self, name:str, colourinactive:str, colouractive:str, colouroutline:str, rect, callback, text=None, font='freesansbold.ttf', fontsize=8, textcolour=(0,0,0), imageinactive=None, imageactive=None, colorkey=None):
super().__init__()
self.text = text
temprect = pygame.Rect(0, 0, *rect.size)
if not imageinactive:
self.inactive = self._create_image(colourinactive, colouroutline, temprect, text)
self.active = self._create_image(colouractive, colouroutline, temprect, text)
self.image = self.inactive
self.rect = rect
self.callback = callback
def _create_image(self, colourinactive:str, colouroutline:str, rect, text=None, font='freesansbold.ttf', fontsize=8, textcolour=(0,0,0)):
imagesurface = pygame.Surface(rect.size)
if colouroutline:
imagesurface.fill(colouroutline)
imagesurface.fill(colourinactive, rect.inflate(-4, -4))
else:
imagesurface.fill(colourinactive)
if text:
font = pygame.font.Font(font, fontsize)
print(type(textcolour))
textsurface = font.render(text, True, pygame.Color(textcolour))
textrect = textsurface.get_rect(center=rect.center)
img.blit(textsurface, textrect)
return imagesurface
def _update_image(self, events):
mouse = pygame.mouse.get_pos()
active = self.rect.collidepoint(mouse)
self.image = self.active if active else self.inactive
for event in events:
if event.type == pygame.MOUSEBUTTONDOWN and hit:
self.callback(self)