import os import random import pyglet from pyglet.window import key class Sprite(object): """Base Sprite Class Sprites will get their basic position (x, y), movement (dx, dy), and size (w, h) attributes set in this base class. The sprite's image will be set to the image file that corresponds to its subclass name from the images directory. Very basic draw and update methods are implemented but are expected to be overridden in many cases. """ def __init__(self, x, y): """ Init position, movement, size, and image values. Also set image. """ # This class is meant to be subclassed only if self.__class__.__name__ == 'Sprite': raise NotImplementedError('Don\'t instantiate sprite base class') self.x, self.y = x, y self.dx, self.dy = 0, 0 self.image = images[self.__class__.__name__] # Width and Height are taken from this sprite's image object self.w, self.h = self.image.width, self.image.height def draw(self): """ A basic, single-frame blit to the screen """ self.image.blit(self.x, self.y) def update(self): """ Move the sprite according to its dx/dy and draw it """ self.x, self.y = self.x + self.dx, self.y + self.dy self.draw() class Background(Sprite): """ Image of a black background and net. Sprite may be a bit overkill """ pass class Paddle(Sprite): """ Paddle that will be controlled by the player and enemy AI """ def start_moving(self, dy): self.dy = dy def stop_moving(self): self.dy = 0 def ai(self, ball): """ Simple AI to move the paddle along with the ball """ if ball.y > self.y + self.h: self.start_moving(speed) elif ball.y < self.y + self.h: self.start_moving(-speed) def update(self): """ Check for out of bounds and draw """ self.y = self.y + self.dy if self.y + self.h >= window.height: self.y = window.height - self.h if self.y <= 0: self.y = 0 self.draw() class Ball(Sprite): """ Pong ball that will bounce of walls and paddles """ def __init__(self, x, y): """ Init position and a random speed. Also ball is not out yet """ Sprite.__init__(self, x, y) self.dx = random.randint(2, 6) self.dy = random.randint(2, 6) def update(self): """ Check for collisions and bounds then draw. """ # Update position self.x, self.y = self.x + self.dx, self.y + self.dy # Check for paddle collisions, reflect ball if needed if collide(self, player) or collide(self, enemy): self.dx = -self.dx + random.randint(0, 4) self.dy = -self.dy + random.randint(0, 4) # Make sure the ball isn't going too fast if self.dx > 6: self.dx = 6 if self.dx < -6: self.dx = -6 if self.dy > 6: self.dx > 6 # Check for a wall hit, reflect and play sound if needed if self.y + self.h >= window.height or self.y <= 0: self.dy = -self.dy self.draw() def load_images(): """ Creates a dict mapping Sprite classnames to image objects """ images = {} for filename in os.listdir('images'): key = filename.split('.')[0] value = pyglet.image.load(os.sep.join(['images', filename])) images[key] = value return images def collide(a, b): """ Basic rectangle collision """ if a.y + a.h < b.y: return False if a.y > b.y + b.h: return False if a.x + a.w < b.x: return False if a.x > b.x + b.w: return False return True def update(dt): """ Scheduled function that will update sprites and call ai """ background.update() ball.update() player.update() enemy.ai(ball) enemy.update() # Load images so that Sprite.__init__ will work images = load_images() # Load game objects background = Background(0, 0) player = Paddle(100, 200) enemy = Paddle(530, 200) ball = Ball(120, 214) # Set paddle speed and create our move shortcut dict speed = 4 move_dirs = {key.UP: speed, key.DOWN: -speed} window = pyglet.window.Window() # Register keypress events @window.event def on_key_press(symbol, modifiers): if symbol in move_dirs.keys(): player.start_moving(move_dirs[symbol]) @window.event def on_key_release(symbol, modifiers): if symbol in move_dirs.keys() and player.dy == move_dirs[symbol]: player.stop_moving() # Schedule our update function and start the game loop pyglet.clock.schedule(update) pyglet.app.run()