Classes Pygame Help - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: Game Development (https://python-forum.io/forum-11.html) +--- Thread: Classes Pygame Help (/thread-11440.html) |
Classes Pygame Help - sam57719 - Jul-09-2018 Hello, I found a piece of code on the internet which creates an interactive menu. I modified it to use classes to make it more modular. It works, but the way it works isn't very Pythonic. import pygame as pg class Control(): def __init__(self, size=(480,320),caption=""): pg.init() self.size = size self.caption = caption self.screen = pg.display.set_mode(self.size) pg.display.set_caption(self.caption) self.clicked = False self.main_menu = Menu(self,["NEW GAME", "LOAD GAME", "OPTIONS"]) self.main() def main(self): while True: pg.event.pump() self.screen.fill((0,0,0)) self.clicked = False for event in pg.event.get(): if event.type == pg.MOUSEBUTTONUP: self.clicked = True elif event.type == pg.QUIT: pg.quit sys.exit() self.main_menu.update() pg.display.update() class Menu(Control): def __init__(self,window,labels,font=None): self.font = pg.font.Font(font,40) self.options = [] self.window = window self.screen = window.screen self.labels = labels self.startypos = self.window.size[1]//(len(labels)+1) self.middle = self.window.size[0]//2 count = self.startypos for label in labels: self.options.append(Option(self,label,(self.middle,count))) count += self.startypos def update(self): for option in self.options: if option.rect.collidepoint(pg.mouse.get_pos()): option.hovered = True if self.window.clicked: option.clicked() else: option.hovered = False option.draw() class Option(Menu): def __init__(self,menu,text,pos): self.hovered = False self.menu = menu self.font = self.menu.font self.screen = self.menu.screen self.text = text self.pos = pos self.set_rect() self.draw() def draw(self): self.set_rend() self.screen.blit(self.rend,self.rect) def set_rend(self): self.rend = self.font.render(self.text,True,self.get_colour()) def get_colour(self): if self.hovered: return (255,255,255) else: return (100,100,100) def set_rect(self): self.set_rend() self.rect = self.rend.get_rect() self.rect.center = self.pos def clicked(self): print(self.text) app = Control()I have searched for a way to write this where I don't have to pass the instances through as parameters, but I haven't found a way. Thanks! RE: Classes Pygame Help - Windspar - Jul-09-2018 Here another way. example import pygame pygame.init() class Scene: def blit(self, surface): pass def event(self, event): pass class Game: fps = 0 clock = None scenes = {} surface = None running = False next_scene = None current_scene = None @classmethod def setup(cls, caption, size): cls.clock = pygame.time.Clock() pygame.display.set_caption(caption) cls.surface = pygame.display.set_mode(size) @classmethod def loop(cls, intro_scene, fps=30): cls.fps = fps cls.running = True cls.next_scene = intro_scene while cls.running: if cls.next_scene: cls.current_scene = cls.scenes[cls.next_scene] cls.next_scene = None for event in pygame.event.get(): cls.current_scene.event(event) cls.current_scene.blit(cls.surface) pygame.display.flip() cls.clock.tick(cls.fps) class MenuItem: default_color = (0, 0 ,200) default_hcolor = (0, 200, 0) default_font = pygame.font.Font(None, 36) def __init__(self, name, callback, pydata=None): self.name = name self.hover = False self.pydata = pydata self.callback = callback def image(self, color=None, hcolor=None, font=None): self.hcolor = [hcolor, MenuItem.default_hcolor][hcolor is None] self.color = [color, MenuItem.default_color][color is None] self.font = [font, MenuItem.default_font][font is None] self.image = self.font.render(self.name, 1, self.color) self.hover_image = self.font.render(self.name, 1, self.hcolor) self.rect = self.image.get_rect() return self class Menu: def __init__(self, rect, spacing=4): self.rect = rect self.items = [] self.spacing = spacing self.location = 0 def add(self, item): self.items.append(item) def blit(self, surface): for item in self.items: if not item.hover: surface.blit(item.image, item.rect) else: surface.blit(item.hover_image, item.rect) def event(self, event): if event.type == pygame.MOUSEMOTION: for item in self.items: if item.rect.collidepoint(event.pos): item.hover = True else: item.hover = False elif event.type == pygame.MOUSEBUTTONDOWN: for item in self.items: if item.rect.collidepoint(event.pos): item.callback() # untested def align_left(self): x = self.rect.x + 1 down = 1 for item in self.items: item.rect.x = x item.rect.y = down down += item.font.get_linesize() def align_center(self): down = self.rect.height / 2 + self.rect.y for item in self.items: down -= (item.font.get_linesize() / 2 + self.spacing / 2) for item in self.items: item.rect.x = self.rect.x + self.rect.width / 2 - item.rect.width / 2 item.rect.y = down down += item.font.get_linesize() + self.spacing class Intro(Scene): def __init__(self): rect = Game.surface.get_rect() self.menu = Menu(rect.inflate(-100, -100)) self.menu.add(MenuItem('New Game', self.menu_newgame).image()) self.menu.add(MenuItem('Options', self.menu_options).image()) self.menu.add(MenuItem('Quit', self.menu_quit).image(color=(150,0,0), hcolor=(250,0,0))) self.menu.align_center() def blit(self, surface): surface.fill((0,0,0)) self.menu.blit(surface) def event(self, event): if event.type == pygame.QUIT: Game.running = False elif event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: Game.running = False else: self.menu.event(event) def menu_newgame(self): print('Menu New Game') def menu_options(self): print('Menu Options') def menu_quit(self): Game.running = False def main(): Game.setup('Menu', (800, 600)) Game.scenes['Intro'] = Intro() Game.loop('Intro') pygame.quit() if __name__ == '__main__': main() RE: Classes Pygame Help - metulburr - Jul-10-2018 Your way is pretty similar to my way https://python-forum.io/Thread-PyGame-Creating-a-state-machine (Jul-09-2018, 09:21 AM)sam57719 Wrote: a way to write this where I don't have to pass the instances through as parametersThe window and menu options should not be passed back and forth between states. Each state should have its own selection of menu options. For example the main menu would have things like "Play", "Options", and "exit". But the Options menu would have things like "audio", "video", etc. So i am not sure why you are passing the same menu between them? |