"""The Pong Game.""" import sys import sdl2 import sdl2.ext BLACK = sdl2.ext.Color(0, 0, 0) WHITE = sdl2.ext.Color(255, 255, 255) PADDLE_SPEED = 3 BALL_SPEED = 3 class CollisionSystem(sdl2.ext.Applicator): def __init__(self, minx, miny, maxx, maxy): super(CollisionSystem, self).__init__() self.componenttypes = Velocity, sdl2.ext.Sprite self.ball = None self.minx = minx self.miny = miny self.maxx = maxx self.maxy = maxy def _overlap(self, item): sprite = item[1] if sprite == self.ball.sprite: return False left, top, right, bottom = sprite.area bleft, btop, bright, bbottom = self.ball.sprite.area return (bleft < right and bright > left and btop < bottom and bbottom > top) def process(self, world, componentsets): collitems = [comp for comp in componentsets if self._overlap(comp)] if len(collitems) != 0: self.ball.velocity.vx = -self.ball.velocity.vx sprite = collitems[0][1] ballcentery = self.ball.sprite.y + self.ball.sprite.size[1] // 2 halfheight = sprite.size[1] // 2 stepsize = halfheight // 10 degrees = 0.7 paddlecentery = sprite.y + halfheight if ballcentery < paddlecentery: factor = (paddlecentery - ballcentery) // stepsize self.ball.velocity.vy = -int(round(factor * degrees)) elif ballcentery > paddlecentery: factor = (ballcentery - paddlecentery) // stepsize self.ball.velocity.vy = int(round(factor * degrees)) else: self.ball.velocity.vy = -self.ball.velocity.vy if (self.ball.sprite.y <= self.miny or self.ball.sprite.y + self.ball.sprite.size[1] >= self.maxy): self.ball.velocity.vy = -self.ball.velocity.vy if (self.ball.sprite.x <= self.minx or self.ball.sprite.x + self.ball.sprite.size[0] >= self.maxx): self.ball.velocity.vx = -self.ball.velocity.vx class MovementSystem(sdl2.ext.Applicator): def __init__(self, minx, miny, maxx, maxy): super(MovementSystem, self).__init__() self.componenttypes = Velocity, sdl2.ext.Sprite self.minx = minx self.miny = miny self.maxx = maxx self.maxy = maxy def process(self, world, componentsets): for velocity, sprite in componentsets: swidth, sheight = sprite.size sprite.x += velocity.vx sprite.y += velocity.vy sprite.x = max(self.minx, sprite.x) sprite.y = max(self.miny, sprite.y) pmaxx = sprite.x + swidth pmaxy = sprite.y + sheight if pmaxx > self.maxx: sprite.x = self.maxx - swidth if pmaxy > self.maxy: sprite.y = self.maxy - sheight class TrackingAIController(sdl2.ext.Applicator): def __init__(self, miny, maxy): super(TrackingAIController, self).__init__() self.componenttypes = PlayerData, Velocity, sdl2.ext.Sprite self.miny = miny self.maxy = maxy self.ball = None def process(self, world, componentsets): for pdata, vel, sprite in componentsets: if not pdata.ai: continue sheight = sprite.size[1] centery = sprite.y + sheight // 2 if self.ball.velocity.vx < 0: # ball is moving away from the AI if centery < self.maxy // 2 - PADDLE_SPEED: vel.vy = PADDLE_SPEED elif centery > self.maxy // 2 + PADDLE_SPEED: vel.vy = -PADDLE_SPEED else: vel.vy = 0 else: bcentery = self.ball.sprite.y + self.ball.sprite.size[1] // 2 if bcentery < centery: vel.vy = -PADDLE_SPEED elif bcentery > centery: vel.vy = PADDLE_SPEED else: vel.vy = 0 class SoftwareRenderSystem(sdl2.ext.SoftwareSpriteRenderSystem): def __init__(self, window): super(SoftwareRenderSystem, self).__init__(window) def render(self, components): sdl2.ext.fill(self.surface, BLACK) super(SoftwareRenderSystem, self).render(components) class TextureRenderSystem(sdl2.ext.TextureSpriteRenderSystem): def __init__(self, renderer): super(TextureRenderSystem, self).__init__(renderer) self.renderer = renderer def render(self, components): tmp = self.renderer.color self.renderer.color = BLACK self.renderer.clear() self.renderer.color = tmp super(TextureRenderSystem, self).render(components) class Velocity(object): def __init__(self): super(Velocity, self).__init__() self.vx = 0 self.vy = 0 class PlayerData(object): def __init__(self): super(PlayerData, self).__init__() self.ai = False self.points = 0 class Player(sdl2.ext.Entity): def __init__(self, world, sprite, posx=0, posy=0, ai=False): self.sprite = sprite self.sprite.position = posx, posy self.velocity = Velocity() self.playerdata = PlayerData() self.playerdata.ai = ai class Ball(sdl2.ext.Entity): def __init__(self, world, sprite, posx=0, posy=0): self.sprite = sprite self.sprite.position = posx, posy self.velocity = Velocity() def run(): sdl2.ext.init() window = sdl2.ext.Window("The Pong Game", size=(800, 600)) window.show() if "-hardware" in sys.argv: print("Using hardware acceleration") renderer = sdl2.ext.Renderer(window) factory = sdl2.ext.SpriteFactory(sdl2.ext.TEXTURE, renderer=renderer) else: print("Using software rendering") factory = sdl2.ext.SpriteFactory(sdl2.ext.SOFTWARE) # Create the paddles - we want white ones. To keep it easy enough for us, # we create a set of surfaces that can be used for Texture- and # Software-based sprites. sp_paddle1 = factory.from_color(WHITE, size=(20, 100)) sp_paddle2 = factory.from_color(WHITE, size=(20, 100)) sp_ball = factory.from_color(WHITE, size=(20, 20)) world = sdl2.ext.World() movement = MovementSystem(0, 0, 800, 600) collision = CollisionSystem(0, 0, 800, 600) aicontroller = TrackingAIController(0, 600) if factory.sprite_type == sdl2.ext.SOFTWARE: spriterenderer = SoftwareRenderSystem(window) else: spriterenderer = TextureRenderSystem(renderer) world.add_system(aicontroller) world.add_system(movement) world.add_system(collision) world.add_system(spriterenderer) player1 = Player(world, sp_paddle1, 0, 250) player2 = Player(world, sp_paddle2, 780, 250, True) ball = Ball(world, sp_ball, 390, 290) ball.velocity.vx = -BALL_SPEED collision.ball = ball aicontroller.ball = ball running = True while running: for event in sdl2.ext.get_events(): if event.type == sdl2.SDL_QUIT: running = False break if event.type == sdl2.SDL_KEYDOWN: if event.key.keysym.sym == sdl2.SDLK_UP: player1.velocity.vy = -PADDLE_SPEED elif event.key.keysym.sym == sdl2.SDLK_DOWN: player1.velocity.vy = PADDLE_SPEED elif event.type == sdl2.SDL_KEYUP: if event.key.keysym.sym in (sdl2.SDLK_UP, sdl2.SDLK_DOWN): player1.velocity.vy = 0 sdl2.SDL_Delay(10) world.process() if __name__ == "__main__": sys.exit(run())