# # scriptedfun.com # # Screencast #4 # Arinoid - The Ball # import math, os, pygame from pygame.locals import * SCREENRECT = Rect(0, 0, 640, 480) def paddleimage(spritesheet): paddle = pygame.Surface((55, 11)).convert() # left half paddle.blit(spritesheet.imgat((261, 143, 27, 11)), (0, 0)) # right half paddle.blit(spritesheet.imgat((289, 143, 28, 11)), (27, 0)) paddle.set_colorkey(paddle.get_at((0, 0)), RLEACCEL) return paddle class Spritesheet: def __init__(self, filename): self.sheet = pygame.image.load(os.path.join('data', filename)).convert() def imgat(self, rect, colorkey = None): rect = Rect(rect) image = pygame.Surface(rect.size).convert() image.blit(self.sheet, (0, 0), rect) if colorkey is not None: if colorkey is -1: colorkey = image.get_at((0, 0)) image.set_colorkey(colorkey, RLEACCEL) return image def imgsat(self, rects, colorkey = None): imgs = [] for rect in rects: imgs.append(self.imgat(rect, colorkey)) return imgs class Arena: tileside = 31 numxtiles = 12 numytiles = 14 topx = (SCREENRECT.width - SCREENRECT.width/tileside*tileside)/2 topy = (SCREENRECT.height - SCREENRECT.height/tileside*tileside)/2 rect = Rect(topx + tileside, topy + tileside, tileside*numxtiles, tileside*numytiles) def __init__(self): self.background = pygame.Surface(SCREENRECT.size).convert() def drawtile(self, tile, x, y): self.background.blit(tile, (self.topx + self.tileside*x, \ self.topy + self.tileside*y)) def makebg(self, tilenum): for x in range(self.numxtiles): for y in range(self.numytiles): self.drawtile(self.tiles[tilenum], x + 1, y + 1) class Paddle(pygame.sprite.Sprite): def __init__(self): pygame.sprite.Sprite.__init__(self, self.containers) self.rect = self.image.get_rect() self.rect.bottom = self.arena.rect.bottom - self.arena.tileside def update(self): self.rect.centerx = pygame.mouse.get_pos()[0] self.rect.clamp_ip(self.arena.rect) class Ball(pygame.sprite.Sprite): speed = 5 angleleft = 135 angleright = 45 def __init__(self): pygame.sprite.Sprite.__init__(self, self.containers) self.rect = self.image.get_rect() self.update = self.start def start(self): self.rect.centerx = self.paddle.rect.centerx self.rect.bottom = self.paddle.rect.top if pygame.mouse.get_pressed()[0] == 1: self.setfp() self.dx = 0 self.dy = 1 self.update = self.move def setfp(self): self.fpx = float(self.rect.centerx) self.fpy = float(self.rect.centery) def setint(self): self.rect.centerx = int(self.fpx) self.rect.centery = int(self.fpy) def move(self): # # Ball-Paddle Physics # - there are 2 extreme ball-paddle # collision scenarios: # # # Scenario 1 - right edge # # bbb # pppppppppp # # - in this scenario, the left edge # of the ball is at paddle.rect.right # # # Scenario 2 - left edge # # bbb # pppppppppp # # - here, the left edge of the ball is # (ball.rect.width - 1) units away from # the paddle.rect.left, which means that # the left edge of the ball is at # (paddle.rect.left - (ball.rect.width - 1)) # # # Hence, we want the linear function that # will determine the ball angle to contain # (paddle.rect.right, angler) and # (paddle.rect.left - (ball.rect.width - 1), anglel). # if self.rect.colliderect(self.paddle.rect) and self.dy > 0: x1 = self.paddle.rect.right y1 = self.angleright x2 = self.paddle.rect.left - (self.rect.width - 1) y2 = self.angleleft x = self.rect.left m = float(y2 - y1)/(x2 - x1) y = m*(x - x1) + y1 angle = math.radians(y) self.dx = self.speed*math.cos(angle) self.dy = -self.speed*math.sin(angle) self.fpx = self.fpx + self.dx self.fpy = self.fpy + self.dy self.setint() if not self.arena.rect.contains(self.rect): if self.rect.bottom > self.arena.rect.bottom: self.kill() else: if self.rect.left < self.arena.rect.left or \ self.rect.right > self.arena.rect.right: self.dx = -self.dx if self.rect.top < self.arena.rect.top: self.dy = -self.dy self.rect.clamp_ip(self.arena.rect) self.setfp() def main(): pygame.init() screen = pygame.display.set_mode(SCREENRECT.size) spritesheet = Spritesheet('arinoid_master.bmp') Arena.tiles = spritesheet.imgsat([(129, 321, 31, 31), # purple - 0 (161, 321, 31, 31), # dark blue - 1 (129, 353, 31, 31), # red - 2 (161, 353, 31, 31), # green - 3 (129, 385, 31, 31)]) # blue - 4 Paddle.image = paddleimage(spritesheet) Ball.image = spritesheet.imgat((428, 300, 11, 11), -1) # make background arena = Arena() arena.makebg(0) # you may change the background color here screen.blit(arena.background, (0, 0)) pygame.display.update() Paddle.arena = arena Ball.arena = arena # keep track of sprites balls = pygame.sprite.Group() all = pygame.sprite.RenderUpdates() Paddle.containers = all Ball.containers = all, balls # keep track of time clock = pygame.time.Clock() paddle = Paddle() Ball.paddle = paddle # game loop while 1: # get input for event in pygame.event.get(): if event.type == QUIT \ or (event.type == KEYDOWN and \ event.key == K_ESCAPE): return # clear sprites all.clear(screen, arena.background) # update sprites all.update() if not balls: Ball() # redraw sprites dirty = all.draw(screen) pygame.display.update(dirty) # maintain frame rate clock.tick(60) if __name__ == '__main__': main()