Prg2/design patterns 1
- This is part of Programming 2 2563
เนื้อหา
Basic information
- Game Programming Patterns by Robert Nystrom
- Refactoring.Guru
- Patterns covered this week
- Observer: wikipedia, game programmming patterns
- Command: wikipedia, game programmming patterns
- State: wikipedia, game programmming patterns
- Singleton: wikipedia, game programmming patterns
Codes
Observer Pattern (OO version)
File: gamelib.py
class GameApp(ttk.Frame):
def __init__(self, parent, canvas_width=800, canvas_height=500, update_delay=33):
# ...
self.on_key_pressed_observers = []
# ...
def register_on_key_pressed_observer(self, observer):
self.on_key_pressed_observers.append(observer)
def on_key_pressed(self, event):
for observer in self.on_key_pressed_observers:
observer.notify(event)
File: monkeys.py
class MonkeyGame(GameApp):
class AppObserver:
def __init__(self, app):
self.app = app
class SpeedAdjustmentObserver(AppObserver):
def notify(self, event):
app = self.app
if event.char == '+':
if app.speed < 10:
app.speed += 1
app.update_speed_text()
if event.char == '-':
if app.speed > 1:
app.speed -= 1
app.update_speed_text()
class BananaThrowingObserver(AppObserver):
def notify(self, event):
app = self.app
if event.char == ' ':
if not app.banana.is_moving:
app.banana.set_speed(3 * app.speed, 5 * app.speed)
app.banana.reset()
app.banana.start()
# ...
def init_game(self):
# ...
self.register_on_key_pressed_observer(MonkeyGame.SpeedAdjustmentObserver(self))
self.register_on_key_pressed_observer(MonkeyGame.BananaThrowingObserver(self))
Observer Pattern (functions)
File: gamelib.py
class GameApp(ttk.Frame):
def __init__(self, parent, canvas_width=800, canvas_height=500, update_delay=33):
# ...
self.on_key_pressed_handlers = []
# ...
def register_on_key_pressed_handler(self, f):
self.on_key_pressed_handlers.append(f)
def on_key_pressed(self, event):
for f in self.on_key_pressed_handlers:
f(event)
File: monkeys.py
class MonkeyGame(GameApp):
def handle_speed_adjustment_key_pressed(self, event):
if event.char == '+':
if self.speed < 10:
self.speed += 1
self.update_speed_text()
if event.char == '-':
if self.speed > 1:
self.speed -= 1
self.update_speed_text()
def handle_banana_throwing_key_pressed(self, event):
if event.char == ' ':
if not self.banana.is_moving:
self.banana.set_speed(3 * self.speed, 5 * self.speed)
self.banana.reset()
self.banana.start()
# ...
def init_game(self):
# ...
self.register_on_key_pressed_handler(self.handle_speed_adjustment_key_pressed)
self.register_on_key_pressed_handler(self.handle_banana_throwing_key_pressed)
Command Pattern
class DotUpdateCommand:
def __init__(self, dot):
self.dot = dot
def excute(self):
self.old_state = self.dot.get_state()
self.dot.real_update()
def undo(self):
self.dot.set_state(self.old_state)
class Dot(Sprite):
def init_element(self):
self.vx = 0
self.vy = 0
def random_speed(self):
self.vx = 5 * randint(-5,5)
self.vy = -5 * randint(1,10)
def bounce(self):
if (self.x > CANVAS_WIDTH) or (self.x < 0):
self.vx = -self.vx
if self.y > CANVAS_HEIGHT:
self.vy = -0.85 * self.vy
def real_update(self):
self.x += self.vx
self.y += self.vy
self.vy += GRAVITY
self.bounce()
def get_update_command(self):
return DotUpdateCommand(self)
def get_state(self):
return (self.x, self.y, self.vx, self.vy)
def set_state(self, state):
self.x, self.y, self.vx, self.vy = state
class FlappyGame(GameApp):
def create_sprites(self):
self.dots = []
for i in range(NUM_BALLS):
dot = Dot(self, 'images/dot.png', CANVAS_WIDTH // 2, CANVAS_HEIGHT // 2)
dot.random_speed()
self.dots.append(dot)
self.elements.append(dot)
def init_game(self):
self.create_sprites()
self.commands = []
self.is_reversed = False
self.cmd_index = 0
def reverse_update(self):
current_commands = self.commands[self.cmd_index]
for c in reversed(current_commands):
c.undo()
self.cmd_index -= 1
if self.cmd_index < 0:
self.is_reversed = False
self.commands = []
def create_update_commands(self):
new_commands = []
for dot in self.dots:
new_commands.append(dot.get_update_command())
return new_commands
def pre_update(self):
if self.is_reversed:
self.reverse_update()
return
new_commands = self.create_update_commands()
for c in new_commands:
c.execute()
self.commands.append(new_commands)
self.cmd_index = len(self.commands) - 1
def on_key_pressed(self, event):
self.is_reversed = True