Created
November 4, 2010 08:52
-
-
Save rhysforyou/662243 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /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