1. Sean McKiernan
  2. sample_pygame_project_organization

Commits

Sean McKiernan  committed 2e02328

Initial Commit

  • Participants
  • Branches master

Comments (0)

Files changed (16)

File data/__init__.py

  • Ignore whitespace
Empty file added.

File data/main.py

View file
  • Ignore whitespace
+import pygame as pg
+import os,sys
+
+from . import setup,tools
+from .states import title,menu,fourdir,eightdir,drag
+
+def main():
+    RunIt = tools.Control(setup.ORIGINAL_CAPTION)
+    state_dict = {"TITLE"  : title.Title(),
+                  "MENU"   : menu.Menu(),
+                  "FOUR"   : fourdir.FourDir(),
+                  "EIGHT"  : eightdir.EightDir(),
+                  "DRAG"   : drag.Drag()}
+    RunIt.setup_states(state_dict,"TITLE")
+    RunIt.main()

File data/setup.py

View file
  • Ignore whitespace
+import pygame as pg
+import os
+from . import tools
+
+SCREEN_SIZE = 800,600
+ORIGINAL_CAPTION = "Game Title Here"
+
+#Initialization
+os.environ['SDL_VIDEO_CENTERED'] = '1'
+pg.init()
+pg.display.set_caption(ORIGINAL_CAPTION)
+
+SCREEN = pg.display.set_mode(SCREEN_SIZE)
+SCREEN_RECT = SCREEN.get_rect()
+
+#Resource loading (Fonts and music just contain path names).
+FONTS = tools.load_all_fonts(os.path.join("resources","fonts"))
+MUSIC = tools.load_all_music(os.path.join("resources","music"))
+GFX   = tools.load_all_gfx(os.path.join("resources","graphics"))
+SFX   = tools.load_all_sfx(os.path.join("resources","sound"))

File data/states/__init__.py

  • Ignore whitespace
Empty file added.

File data/states/drag.py

View file
  • Ignore whitespace
+import pygame as pg
+from .. import setup as su,tools
+
+class Player(object):
+    def __init__(self,rect):
+        self.rect = pg.Rect(rect)
+        self.click = False
+        self.image = pg.Surface(self.rect.size).convert()
+        self.image.fill((255,0,0))
+        self.image.fill((0,50,255),self.rect.inflate((-10,-10)))
+
+    def update(self,Surf):
+        if self.click:
+            self.rect.move_ip(pg.mouse.get_rel())
+        Surf.blit(self.image,self.rect)
+
+class Drag(tools._State):
+    def __init__(self):
+        tools._State.__init__(self)
+        self.Player = Player((0,0,150,150))
+        self.Player.rect.center = su.SCREEN_RECT.center
+        self.title = self.render_font("Fixedsys500c",40,"Drag With Mouse",(0,255,255))
+        self.title_rect = self.title.get_rect(center=(su.SCREEN_RECT.centerx,75))
+        self.escape_txt = (self.render_font("Fixedsys500c",20,
+                            "[Press ESCAPE to return to menu]",(255,255,0)))
+        self.escape_txt_rect = self.escape_txt.get_rect(center=(su.SCREEN_RECT.centerx,500))
+        self.blink = False
+        self.timer = 0.0
+    def render_font(self,font,size,msg,color=(255,255,255)):
+        RenderFont = pg.font.Font(su.FONTS[font],size)
+        return RenderFont.render(msg,1,color)
+    def get_event(self,event):
+        if event.type == pg.MOUSEBUTTONDOWN:
+            if self.Player.rect.collidepoint(event.pos):
+                self.Player.click = True
+                pg.mouse.get_rel()
+        elif event.type == pg.MOUSEBUTTONUP:
+            self.Player.click = False
+        elif event.type == pg.KEYDOWN:
+            if event.key == pg.K_ESCAPE:
+                self.next = "MENU"
+                self.done = True
+    def cleanup(self):
+        self.persist["location"] = self.Player.rect.center
+        return tools._State.cleanup(self)
+    def startup(self,persistant):
+        tools._State.startup(self,persistant)
+        if "location" in self.persist:
+            self.Player.rect.center = self.persist["location"]
+    def update(self,Surf,keys,mouse):
+        Surf.fill(0)
+        self.Player.update(Surf)
+        Surf.blit(self.title,self.title_rect)
+        if pg.time.get_ticks() - self.timer > 1000/5.0:
+            self.blink = not self.blink
+            self.timer = pg.time.get_ticks()
+        if self.blink:
+            Surf.blit(self.escape_txt,self.escape_txt_rect)

File data/states/eightdir.py

View file
  • Ignore whitespace
+import pygame as pg
+from .. import setup as su,tools
+
+DIRECTDICT = {pg.K_LEFT  : (-1, 0),
+              pg.K_RIGHT : ( 1, 0),
+              pg.K_UP    : ( 0,-1),
+              pg.K_DOWN  : ( 0, 1)}
+
+class Player(object):
+    def __init__(self,rect,speed):
+        self.rect = pg.Rect(rect)
+        self.speed = speed
+        self.movement = [0,0]
+        self.make_image()
+    def make_image(self):
+        self.image = pg.Surface((self.rect.size)).convert_alpha()
+        self.image.fill((0,0,0,0))
+        pg.draw.ellipse(self.image,(0,0,0),(1,1,self.rect.size[0]-2,self.rect.size[1]-2))
+        pg.draw.ellipse(self.image,(255,0,0),(6,6,self.rect.size[0]-12,self.rect.size[1]-12))
+    def update(self,Surf,keys):
+        self.movement = [0,0]
+        for key in DIRECTDICT:
+            if keys[key]:
+                for i in (0,1):
+                    self.movement[i] += DIRECTDICT[key][i]*self.speed
+        self.rect.move_ip(self.movement)
+        self.draw(Surf)
+    def draw(self,Surf):
+        Surf.blit(self.image,self.rect)
+
+class EightDir(tools._State):
+    def __init__(self):
+        tools._State.__init__(self)
+        self.Player = Player((250,250,100,100),5)
+        self.title = self.render_font("Fixedsys500c",40,"8-Direction Movement",(0,255,255))
+        self.title_rect = self.title.get_rect(center=(su.SCREEN_RECT.centerx,75))
+        self.escape_txt = (self.render_font("Fixedsys500c",20,
+                            "[Press ESCAPE to return to menu]",(255,255,0)))
+        self.escape_txt_rect = self.escape_txt.get_rect(center=(su.SCREEN_RECT.centerx,500))
+        self.blink = False
+        self.timer = 0.0
+    def render_font(self,font,size,msg,color=(255,255,255)):
+        RenderFont = pg.font.Font(su.FONTS[font],size)
+        return RenderFont.render(msg,1,color)
+    def get_event(self,event):
+        if event.type == pg.KEYDOWN:
+            if event.key == pg.K_ESCAPE:
+                self.next = "MENU"
+                self.done = True
+    def cleanup(self):
+        self.persist["location"] = self.Player.rect.center
+        return tools._State.cleanup(self)
+    def startup(self,persistant):
+        tools._State.startup(self,persistant)
+        if "location" in self.persist:
+            self.Player.rect.center = self.persist["location"]
+    def update(self,Surf,keys,mouse):
+        Surf.fill((50,100,50))
+        self.Player.update(Surf,keys)
+        Surf.blit(self.title,self.title_rect)
+        if pg.time.get_ticks() - self.timer > 1000/5.0:
+            self.blink = not self.blink
+            self.timer = pg.time.get_ticks()
+        if self.blink:
+            Surf.blit(self.escape_txt,self.escape_txt_rect)

File data/states/fourdir.py

View file
  • Ignore whitespace
+import pygame as pg
+from .. import setup as su,tools
+
+DIRECTDICT = {pg.K_LEFT  : (-1, 0),
+              pg.K_RIGHT : ( 1, 0),
+              pg.K_UP    : ( 0,-1),
+              pg.K_DOWN  : ( 0, 1)}
+
+class Player(object):
+    def __init__(self,rect,speed,direction=pg.K_RIGHT):
+        self.rect = pg.Rect(rect)
+        self.speed = speed
+        self.direction = direction
+        self.oldy = None
+        self.dir_stack = []
+        self.walk = False
+        self.redraw = False
+        self.spritesheet = su.GFX["skelly"]
+        self.image = None
+        self.frame_inds = [[0,0],[1,0],[2,0],[3,0]]
+        self.frame  = 0
+        self.frames = []
+        self.timer = 0.0
+        self.fps   = 7.0
+        self.walkframes = []
+        self.get_images()
+        self.walkframe_dict = self.make_frame_dict()
+        self.adjust_images()
+    def get_images(self):
+        """Get the desired images from the sprite sheet."""
+        for cell in self.frame_inds:
+            loc = ((self.rect.width*cell[0],self.rect.height*cell[1]),self.rect.size)
+            self.frames.append(self.spritesheet.subsurface(loc))
+    def make_frame_dict(self):
+        frames = {pg.K_LEFT : [self.frames[0],self.frames[1]],
+                  pg.K_RIGHT: [pg.transform.flip(self.frames[0],True,False),
+                               pg.transform.flip(self.frames[1],True,False)],
+                  pg.K_DOWN : [self.frames[3],
+                               pg.transform.flip(self.frames[3],True,False)],
+                  pg.K_UP   : [self.frames[2],
+                               pg.transform.flip(self.frames[2],True,False)]}
+        return frames
+    def adjust_images(self):
+        if self.direction != self.oldy:
+            self.walkframes = self.walkframe_dict[self.direction]
+            self.oldy = self.direction
+            self.redraw = True
+        self.make_image()
+    def make_image(self):
+        if self.redraw or pg.time.get_ticks()-self.timer > 1000/self.fps:
+            if self.walk:
+                self.frame = (self.frame+1) % len(self.walkframes)
+                self.image = self.walkframes[self.frame]
+            self.timer = pg.time.get_ticks()
+        if not self.image:
+            self.image = self.walkframes[self.frame]
+        self.redraw = False
+    def update(self,Surf):
+        """Updates our player appropriately every frame."""
+        self.walk = bool(self.dir_stack)
+        self.adjust_images()
+        if self.walk:
+            self.rect.x += self.speed*DIRECTDICT[self.dir_stack[-1]][0]
+            self.rect.y += self.speed*DIRECTDICT[self.dir_stack[-1]][1]
+        Surf.blit(self.image,self.rect)
+
+class FourDir(tools._State):
+    def __init__(self):
+        tools._State.__init__(self)
+        self.Player = Player((250,250,50,50),3)  #Our Player instance
+        self.title = self.render_font("Fixedsys500c",40,"4-Direction Movement",(0,255,255))
+        self.title_rect = self.title.get_rect(center=(su.SCREEN_RECT.centerx,75))
+        self.escape_txt = (self.render_font("Fixedsys500c",20,
+                            "[Press ESCAPE to return to menu]",(255,255,0)))
+        self.escape_txt_rect = self.escape_txt.get_rect(center=(su.SCREEN_RECT.centerx,500))
+        self.blink = False
+        self.timer = 0.0
+    def render_font(self,font,size,msg,color=(255,255,255)):
+        RenderFont = pg.font.Font(su.FONTS[font],size)
+        return RenderFont.render(msg,1,color)
+    def get_event(self,event):
+        if event.type == pg.KEYDOWN: #all key press events here.
+            if event.key in DIRECTDICT:
+                self.Player.dir_stack.append(event.key)
+                self.Player.direction = self.Player.dir_stack[-1]
+            elif event.key == pg.K_ESCAPE:
+                self.next = "MENU"
+                self.done = True
+        elif event.type == pg.KEYUP: #all key-up events here
+            if event.key in DIRECTDICT:
+                self.Player.dir_stack.remove(event.key)
+                if self.Player.dir_stack:
+                    self.Player.direction = self.Player.dir_stack[-1]
+    def cleanup(self):
+        self.persist["location"] = self.Player.rect.center
+        return tools._State.cleanup(self)
+    def startup(self,persistant):
+        tools._State.startup(self,persistant)
+        if "location" in self.persist:
+            self.Player.rect.center = self.persist["location"]
+    def update(self,Surf,keys,mouse):
+        Surf.fill((100,100,100)) #redraw background before player
+        self.Player.update(Surf) #update the player
+        Surf.blit(self.title,self.title_rect)
+        if pg.time.get_ticks() - self.timer > 1000/5.0:
+            self.blink = not self.blink
+            self.timer = pg.time.get_ticks()
+        if self.blink:
+            Surf.blit(self.escape_txt,self.escape_txt_rect)

File data/states/menu.py

View file
  • Ignore whitespace
+import pygame as pg
+from .. import setup as su,tools
+
+class Menu(tools._State):
+    def __init__(self):
+        tools._State.__init__(self)
+        self.from_bottom = 200
+        self.spacer = 70
+        self.opts = ['4 Direction Movement','8 Direction Movement',
+                     'Drag w/ Mouse','Quit']
+        self.next_list = ["FOUR","EIGHT","DRAG"]
+        self.title = self.render_font("Fixedsys500c",40,"Menu Screen",(0,255,255))
+        self.title_rect = self.title.get_rect(center=(su.SCREEN_RECT.centerx,75))
+        self.pre_render_options()
+
+    def render_font(self,font,size,msg,color=(255,255,255)):
+        RenderFont = pg.font.Font(su.FONTS[font],size)
+        return RenderFont.render(msg,1,color)
+
+    def pre_render_options(self):
+        font_deselect = pg.font.Font(su.FONTS["impact"],25)
+        font_selected = pg.font.Font(su.FONTS["impact"],40)
+
+        rendered_msg = {"des":[],"sel":[]}
+        for option in self.opts:
+            d_rend = font_deselect.render(option, 1, (255,255,255))
+            d_rect = d_rend.get_rect()
+            s_rend = font_selected.render(option, 1, (255,0,0))
+            s_rect = s_rend.get_rect()
+            rendered_msg["des"].append((d_rend,d_rect))
+            rendered_msg["sel"].append((s_rend,s_rect))
+        self.rendered = rendered_msg
+
+    def get_event(self,event):
+        if event.type == pg.MOUSEBUTTONDOWN and event.button == 1:
+            for i,opt in enumerate(self.rendered["des"]):
+                if opt[1].collidepoint(pg.mouse.get_pos()):
+                    if i == len(self.next_list):
+                        self.quit = True
+                    else:
+                        self.next = self.next_list[i]
+                        self.done = True
+                    break
+
+    def update(self,Surf,keys,mouse):
+        Surf.fill((50,50,150))
+        Surf.blit(self.title,self.title_rect)
+        for i,opt in enumerate(self.rendered["des"]):
+            opt[1].center = (su.SCREEN_RECT.centerx,self.from_bottom+i*self.spacer)
+            if opt[1].collidepoint(pg.mouse.get_pos()):
+                rend_img,rend_rect = self.rendered["sel"][i]
+                rend_rect.center = opt[1].center
+                Surf.blit(rend_img,rend_rect)
+            else:
+                Surf.blit(opt[0],opt[1])
+        self.timeout(15)
+
+    def timeout(self,seconds):
+        """Timeout and return to title screen after 15 seconds."""
+        if pg.time.get_ticks()-self.start_time > seconds*1000:
+            self.next = "TITLE"
+            self.done = True

File data/states/title.py

View file
  • Ignore whitespace
+import pygame as pg
+from .. import setup as su,tools
+
+class Title(tools._State):
+    def __init__(self):
+        tools._State.__init__(self)
+        self.logo = su.GFX["logo"]
+        self.logo_rect = self.logo.get_rect(center=su.SCREEN_RECT.center)
+        self.logo_angle = 0
+        self.title = self.render_font("Fixedsys500c",40,"Title Screen")
+        self.title_rect = self.title.get_rect(center=(su.SCREEN_RECT.centerx,75))
+        self.ne_key = self.render_font("Fixedsys500c",20,"[Press Any Key]",(255,255,0))
+        self.ne_key_rect = self.ne_key.get_rect(center=(su.SCREEN_RECT.centerx,500))
+        self.blink = False
+        self.timer = 0.0
+
+    def render_font(self,font,size,msg,color=(255,255,255)):
+        RenderFont = pg.font.Font(su.FONTS[font],size)
+        return RenderFont.render(msg,1,color)
+
+    def update(self,Surf,keys,mouse):
+        self.logo_angle = (self.logo_angle+1)%360
+        Surf.fill((50,50,50))
+        rot_logo = pg.transform.rotozoom(self.logo,self.logo_angle,1)
+        rot_logo_rect = rot_logo.get_rect(center=self.logo_rect.center)
+        Surf.blit(rot_logo,rot_logo_rect)
+        Surf.blit(self.title,self.title_rect)
+        if pg.time.get_ticks() - self.timer > 1000/5.0:
+            self.blink = not self.blink
+            self.timer = pg.time.get_ticks()
+        if self.blink:
+            Surf.blit(self.ne_key,self.ne_key_rect)
+
+    def get_event(self,event):
+        if event.type == pg.KEYDOWN:
+            self.next = "MENU"
+            self.done = True

File data/tools.py

View file
  • Ignore whitespace
+import pygame as pg
+import os
+
+class Control(object):
+    def __init__(self,caption):
+        self.screen = pg.display.get_surface()
+        self.caption = caption
+        self.done = False
+        self.Clock = pg.time.Clock()
+        self.fps = 60
+        self.show_fps = True
+        self.keys = pg.key.get_pressed()
+        self.mouse = pg.mouse.get_pressed()
+        self.state_dict = {}
+        self.state_name = None
+        self.State = None
+    def setup_states(self,state_dict,start_state):
+        self.state_dict = state_dict
+        self.state_name = start_state
+        self.State = self.state_dict[self.state_name]
+    def update(self):
+        if self.State.quit:
+            self.done = True
+        elif self.State.done:
+            self.flip_state()
+        self.State.update(self.screen,self.keys,self.mouse)
+    def flip_state(self):
+        previous,self.state_name = self.state_name,self.State.next
+        persist = self.State.cleanup()
+        self.State = self.state_dict[self.state_name]
+        self.State.startup(persist)
+        self.State.previous = previous
+
+    def event_loop(self):
+        for event in pg.event.get():
+            if event.type == pg.QUIT:
+                self.done = True
+            elif event.type in (pg.KEYDOWN,pg.KEYUP):
+                self.keys = pg.key.get_pressed()
+                self.toggle_show_fps()
+            elif event.type in (pg.MOUSEBUTTONDOWN,pg.MOUSEBUTTONUP):
+                self.mouse = pg.mouse.get_pressed()
+            self.State.get_event(event)
+
+    def toggle_show_fps(self):
+        if self.keys[pg.K_F5]:
+            self.show_fps = not self.show_fps
+        if not self.show_fps:
+            pg.display.set_caption(self.caption)
+
+    def main(self):
+        while not self.done:
+            self.event_loop()
+            self.update()
+            pg.display.update()
+            self.Clock.tick(self.fps)
+            if self.show_fps:
+                with_fps = "{} - {:.2f} FPS".format(self.caption,self.Clock.get_fps())
+                pg.display.set_caption(with_fps)
+
+class _State(object):
+    def __init__(self):
+        self.start_time = pg.time.get_ticks()
+        self.done = False
+        self.quit = False
+        self.next = None
+        self.previous = None
+        self.persist = {}
+    def get_event(self,event,keys,mouse):
+        pass
+    def startup(self,persistant):
+        self.persist = persistant
+        self.start_time = pg.time.get_ticks()
+    def cleanup(self):
+        self.done = False
+        return self.persist
+    def update(self,Surf):
+        pass
+
+### Resource loading functions.
+def load_all_gfx(directory,colorkey=(255,0,255),accept=(".png",".jpg",".bmp")):
+    graphics = {}
+    for pic in os.listdir(directory):
+        name,ext = os.path.splitext(pic)
+        if ext.lower() in accept:
+            img = pg.image.load(os.path.join(directory,pic))
+            if img.get_alpha():
+                img = img.convert_alpha()
+            else:
+                img = img.convert()
+                img.set_colorkey(colorkey)
+            graphics[name]=img
+    return graphics
+
+def load_all_music(directory,accept=(".wav",".mp3",".ogg",".mdi")):
+    songs = {}
+    for song in os.listdir(directory):
+        name,ext = os.path.splitext(song)
+        if ext.lower() in accept:
+            songs[name] = os.path.join(directory,song)
+    return songs
+
+def load_all_fonts(directory,accept=(".ttf",)):
+    return load_all_music(directory,accept)
+
+def load_all_sfx(directory,accept=(".wav",".mp3",".ogg",".mdi")):
+    effects = {}
+    for fx in os.listdir(directory):
+        name,ext = os.path.splitext(fx)
+        if ext.lower() in accept:
+            effects[name] = pg.mixer.Sound(pg.os.path.join(directory,fx))
+    return effects

File game_title_here.py

View file
  • Ignore whitespace
+import pygame as pg
+import sys
+from data.main import main
+
+if __name__ == '__main__':
+    main()
+    pg.quit();sys.exit()

File readme.md

View file
  • Ignore whitespace
+This is just a simple demo of my current style of organizing projects. The top level directory contains a file that does nothing more than call the actual main function. This main function is within a package. This allows me to keep the top level directory nice and tidy so there is no ambiguity about which file launches the game. 
+
+-Mek

File resources/fonts/Fixedsys500c.ttf

  • Ignore whitespace
Binary file added.

File resources/fonts/impact.ttf

  • Ignore whitespace
Binary file added.

File resources/graphics/logo.png

  • Ignore whitespace
Added
New image

File resources/graphics/skelly.png

  • Ignore whitespace
Added
New image