Skip to content

Instantly share code, notes, and snippets.

@rhysforyou
Created November 4, 2010 08:52
Show Gist options
  • Save rhysforyou/662243 to your computer and use it in GitHub Desktop.
Save rhysforyou/662243 to your computer and use it in GitHub Desktop.
#! /usr/bin/env python
# A sample game using and MVC event driven pattern
import pygame # I for one welcome our new PyGame overlords
from pygame.locals import *
DIRECTION_UP = 0
DIRECTION_DOWN = 1
DIRECTION_LEFT = 2
DIRECTION_RIGHT = 3
def Debug(msg): # Yes this is essentially an alias to print but since
print msg # this is a graphical app I think 'Debug' makes more sense
class Event:
""" This is a superclass for any Event to be used with EventHandler. """
def __init__(self):
self.name = "Generic Event"
class TickEvent(Event):
# This gets posted every tick, does that not blow your mind?
def __init__(self):
self.name = "CPU Tick Event"
class QuitEvent(Event):
def __init__(self):
self.name = "Program Quit Event"
class PlayerMoveEvent(Event):
def __init__(self, direction):
self.name = "Player Move Event"
self.direction = direction
class DrawEvent(Event):
def __init__(self, position, surface):
self.name = "Sprite Draw Event"
self.surface = surface
self.position = position
class FpsClock:
def __init__(self):
self.frame_duration = 0.000
self.this_frame_time = 0
self.last_frame_time = 0
return
def tick(self):
"Call this every frame"
self.this_frame_time = self.get_current_time()
self.frame_duration = (self.this_frame_time - self.last_frame_time) / 1000.000
self.last_frame_time = self.this_frame_time
return
def get_frame_duration(self):
"Returns the length of the previous frame, in seconds"
return self.frame_duration
def get_current_time(self):
"Used internally. Returns current time in ms."
return pygame.time.get_ticks()
def begin(self):
"Starts/restarts the timer. Call just before your main loop."
self.last_frame_time = self.get_current_time()
return
#-------------------------------------------------------------------------------
class EventManager:
""" This handles all events within the application. """
def __init__(self):
from weakref import WeakKeyDictionary
self.listeners = WeakKeyDictionary()
self.eventQueue = []
self.clock = FpsClock()
self.clock.begin()
def RegisterListener(self, listener):
self.listeners[listener] = 1
def UnregisterListener(self, listener):
if listener in self.listeners:
del self.listeners[listener]
def Post(self, event):
if not isinstance(event, TickEvent)\
and not isinstance(event, DrawEvent):
Debug("Message: " + event.name)
if isinstance(event, TickEvent):
clock.tick()
for listener in self.listeners:
# I pass every event to every listener. lazy, yes,
# but there's no huge benefit in changing it.
listener.Notify(event)
#-------------------------------------------------------------------------------
class KeyboardController:
""" This class handles you bashing about on the keyboard like some enraged
monkey. It handles sending messages to models as well as some direct events
such as Quit. """
def __init__(self, evManager):
self.evManager = evManager
self.evManager.RegisterListener( self )
self.up_key = False
self.down_key = False
self.left_key = False
self.right_key = False
def Notify(self, event):
if isinstance( event, TickEvent ):
ev = None
for event in pygame.event.get():
if event.type == QUIT:
ev = QuitEvent()
elif event.type == KEYDOWN \
and event.key == K_ESCAPE:
ev = QuitEvent()
elif event.type == KEYDOWN \
and event.key == K_UP:
self.up_key = True
elif event.type == KEYUP \
and event.key == K_UP:
self.up_key = False
elif event.type == KEYDOWN \
and event.key == K_DOWN:
self.down_key = True
elif event.type == KEYUP \
and event.key == K_DOWN:
self.down_key = False
elif event.type == KEYDOWN \
and event.key == K_LEFT:
self.left_key = True
elif event.type == KEYUP \
and event.key == K_LEFT:
self.left_key = False
elif event.type == KEYDOWN \
and event.key == K_RIGHT:
self.right_key = True
elif event.type == KEYUP \
and event.key == K_RIGHT:
self.right_key = False
if self.up_key:
ev = PlayerMoveEvent(DIRECTION_UP)
if self.down_key:
ev = PlayerMoveEvent(DIRECTION_DOWN)
if self.left_key:
ev = PlayerMoveEvent(DIRECTION_LEFT)
if self.right_key:
ev = PlayerMoveEvent(DIRECTION_RIGHT)
if ev:
self.evManager.Post(ev)
class CPUSpinnerController:
""" You spin me right 'round, and do a decent job of handling CPU ticks. """
def __init__(self, evManager):
self.evManager = evManager
self.evManager.RegisterListener(self)
self.keepGoing = True
def Run(self):
while self.keepGoing:
event = TickEvent()
self.evManager.Post(event)
def Notify(self, event):
if isinstance(event, QuitEvent):
# This is how a program dies
keepGoing = False
pygame.quit()
class PygameView:
def __init__(self, evManager):
self.evManager = evManager
self.evManager.RegisterListener(self)
pygame.init()
self.window = pygame.display.set_mode((640, 480))
pygame.display.set_caption("Cyan!")
self.background = pygame.Surface(self.window.get_size())
self.background.fill((200,200,255))
self.window.blit(self.background, (0, 0))
pygame.display.flip()
def Notify(self, event):
if isinstance(event, TickEvent):
# This is where I get to make pretty pictures
self.window.blit(self.background, (0, 0))
pygame.display.flip()
self.background.fill((0, 0, 0))
if isinstance(event, DrawEvent):
self.background.blit(event.surface, event.position)
class Player:
def __init__(self, evManager, x, y, speed = 5):
self.evManager = evManager
self.pos_x = x
self.pos_y = y
self.vel_x = 0
self.vel_y = 0
self.speed = speed
self.sprite = pygame.Surface((10, 10))
self.sprite.fill((255,255,255))
evManager.RegisterListener(self)
def Notify(self, event):
if isinstance(event, TickEvent):
self.pos_x += self.vel_x * evManager.clock.get_frame_duration()
self.pos_y += self.vel_y * evManager.clock.get_frame_duration()
self.vel_x *= 0.8
self.vel_y *= 0.8
self.evManager.Post(DrawEvent((self.pos_x, self.pos_y), self.sprite))
elif isinstance(event, PlayerMoveEvent):
if event.direction == DIRECTION_UP:
self.vel_y -= self.speed
if event.direction == DIRECTION_DOWN:
self.vel_y += self.speed
if event.direction == DIRECTION_LEFT:
self.vel_x -= self.speed
if event.direction == DIRECTION_RIGHT:
self.vel_x += self.speed
#-------------------------------------------------------------------------------
class Game:
""" This one is pretty flipping self explanatory. """
STATE_PREPARING = 'preparing'
STATE_RUNNING = 'running'
STATE_PAUSED = 'paused'
def __init__(self, evManager):
self.evManager = evManager
self.evManager.RegisterListener(self)
self.running = False
def Start(self):
self.running = True
def Notify(self, event):
if isinstance( event, TickEvent ):
if self.running == False:
self.Start()
#-------------------------------------------------------------------------------
def main():
evManager = EventManager()
keybd = KeyboardController(evManager)
spinner = CPUSpinnerController(evManager)
pygameView = PygameView(evManager)
game = Game(evManager)
player = Player(evManager, 320, 240)
spinner.Run()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment