added py-pong

This commit is contained in:
schneider 2011-12-15 21:08:02 +01:00
parent 919f5eb3f0
commit 6429ebde91
21 changed files with 385 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,67 @@
import pygame, pypong
from pypong.player import BasicAIPlayer, KeyboardPlayer, MousePlayer
def run():
configuration = {
'screen_size': (686,488),
'paddle_image': 'assets/paddle.png',
'paddle_left_position': 84.,
'paddle_right_position': 594.,
'paddle_velocity': 6.,
'paddle_bounds': (0, 488), # This sets the upper and lower paddle boundary.The original game didn't allow the paddle to touch the edge,
'line_image': 'assets/dividing-line.png',
'ball_image': 'assets/ball.png',
'ball_velocity': 4.,
'ball_velocity_bounce_multiplier': 1.105,
'ball_velocity_max': 32.,
'score_left_position': (141, 30),
'score_right_position': (473, 30),
'digit_image': 'assets/digit_%i.png',
'sound_missed': 'assets/missed-ball.wav',
'sound_paddle': 'assets/bounce-paddle.wav',
'sound_wall': 'assets/bounce-wall.wav',
'sound': True,
}
pygame.mixer.pre_init(22050, -16, 2, 1024)
pygame.init()
display_surface = pygame.display.set_mode(configuration['screen_size'])
output_surface = display_surface.copy().convert_alpha()
output_surface.fill((0,0,0))
#~ debug_surface = output_surface.copy()
#~ debug_surface.fill((0,0,0,0))
debug_surface = None
clock = pygame.time.Clock()
input_state = {'key': None, 'mouse': None}
# Prepare game
player_left = KeyboardPlayer(input_state, pygame.K_w, pygame.K_s)
#~ player_right = MousePlayer(input_state)
#player_left = BasicAIPlayer()
player_right = BasicAIPlayer()
game = pypong.Game(player_left, player_right, configuration)
# Main game loop
timestamp = 1
while game.running:
clock.tick(60)
now = pygame.time.get_ticks()
if timestamp > 0 and timestamp < now:
timestamp = now + 5000
print clock.get_fps()
input_state['key'] = pygame.key.get_pressed()
input_state['mouse'] = pygame.mouse.get_pos()
game.update()
game.draw(output_surface)
#~ pygame.surfarray.pixels_alpha(output_surface)[:,::2] = 12
display_surface.blit(output_surface, (0,0))
if debug_surface:
display_surface.blit(debug_surface, (0,0))
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.QUIT:
game.running = False
elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
game.running = False
if __name__ == '__main__': run()

View File

@ -0,0 +1,150 @@
import pygame, math, random, entity
def load_image(path):
surface = pygame.image.load(path)
surface.convert()
pygame.surfarray.pixels3d(surface)[:,:,0:1:] = 0
return surface
def line_line_intersect(x1, y1, x2, y2, x3, y3, x4, y4):
# Taken from http://paulbourke.net/geometry/lineline2d/
# Denominator for ua and ub are the same, so store this calculation
d = float((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1))
# n_a and n_b are calculated as seperate values for readability
n_a = float((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3))
n_b = float((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3))
# Make sure there is not a division by zero - this also indicates that
# the lines are parallel.
# If n_a and n_b were both equal to zero the lines would be on top of each
# other (coincidental). This check is not done because it is not
# necessary for this implementation (the parallel check accounts for this).
if d == 0:
return False
# Calculate the intermediate fractional point that the lines potentially intersect.
ua = n_a / d
ub = n_b / d
# The fractional point will be between 0 and 1 inclusive if the lines
# intersect. If the fractional calculation is larger than 1 or smaller
# than 0 the lines would need to be longer to intersect.
if ua >= 0. and ua <= 1. and ub >= 0. and ub <= 1.:
return [x1 + (ua * (x2 - x1)), y1 + (ua * (y2 - y1))]
return False
class Game(object):
def __init__(self, player_left, player_right, configuration):
self.player_left = player_left
self.player_right = player_right
self.configuration = configuration
self.background = pygame.Surface(configuration['screen_size'])
self.sprites = pygame.sprite.OrderedUpdates()
line = entity.Line(load_image(configuration['line_image']), self.sprites)
line.rect.topleft = ((configuration['screen_size'][0]-line.rect.width)/2, 0)
paddle_image = load_image(configuration['paddle_image'])
self.paddle_left = entity.Paddle(configuration['paddle_velocity'], paddle_image, configuration['paddle_bounds'], self.sprites)
self.paddle_right = entity.Paddle(configuration['paddle_velocity'], paddle_image, configuration['paddle_bounds'], self.sprites)
self.paddle_left.rect.topleft = (self.configuration['paddle_left_position'], (self.configuration['screen_size'][1]-self.paddle_left.rect.height)/2)
self.paddle_right.rect.topleft = (self.configuration['paddle_right_position'], (self.configuration['screen_size'][1]-self.paddle_left.rect.height)/2)
digit_images = [load_image(configuration['digit_image'] % n) for n in xrange(10)]
self.score_left = entity.Score(digit_images, self.sprites)
self.score_left.rect.topleft = configuration['score_left_position']
self.score_right = entity.Score(digit_images, self.sprites)
self.score_right.rect.topleft = configuration['score_right_position']
ball_image = load_image(configuration['ball_image'])
self.ball = entity.Ball(self.configuration['ball_velocity'], ball_image, self.sprites)
self.bounds = pygame.Rect(20, 0, configuration['screen_size'][0]-ball_image.get_width()-20, configuration['screen_size'][1]-ball_image.get_height())
self.sound_missed = pygame.mixer.Sound(configuration['sound_missed'])
self.sound_paddle = pygame.mixer.Sound(configuration['sound_paddle'])
self.sound_wall = pygame.mixer.Sound(configuration['sound_wall'])
self.reset_game(random.random()<0.5)
self.running = True
def play_sound(self, sound):
if self.configuration['sound']:
sound.play()
def reset_game(self, serveLeft=True):
y = self.configuration['screen_size'][1] - self.ball.rect.height
self.ball.position_x = (self.configuration['screen_size'][0]-self.ball.rect.width)/2.0
self.ball.position_y = y * random.random()
self.ball.velocity = self.configuration['ball_velocity']
a = random.random() * math.pi / 2. - math.pi / 4.
self.ball.velocity_vec[0] = self.ball.velocity * math.cos(a)
self.ball.velocity_vec[1] = self.ball.velocity * math.sin(a)
if random.random() < 0.5:
self.ball.velocity_vec[1] = -self.ball.velocity_vec[1]
if serveLeft:
self.ball.velocity_vec[0] *= -1
def update(self):
# Store previous ball position for line-line intersect test later
ball_position_x = self.ball.position_x
ball_position_y = self.ball.position_y
# Update sprites and players
self.sprites.update()
self.player_left.update(self.paddle_left, self)
self.player_right.update(self.paddle_right, self)
# Paddle collision check. Could probably just do a line-line intersect but I think I prefer having the pixel-pefect result of a rect-rect intersect test as well.
if self.ball.rect.x < self.bounds.centerx:
# Left side bullet-through-paper check on ball and paddle
if self.ball.velocity_vec[0] < 0:
intersect_point = line_line_intersect(
self.paddle_left.rect.right, self.paddle_left.rect.top,
self.paddle_left.rect.right, self.paddle_left.rect.bottom,
ball_position_x-self.ball.rect.width/2, ball_position_y+self.ball.rect.height/2,
self.ball.position_x-self.ball.rect.width/2, self.ball.position_y+self.ball.rect.height/2
)
if intersect_point:
self.ball.position_y = intersect_point[1]-self.ball.rect.height/2
if intersect_point or (self.paddle_left.rect.colliderect(self.ball.rect) and self.ball.rect.right > self.paddle_left.rect.right):
self.ball.position_x = self.paddle_left.rect.right
velocity = self.paddle_left.calculate_bounce(min(1,max(0,(self.ball.rect.centery - self.paddle_left.rect.y)/float(self.paddle_left.rect.height))))
self.ball.velocity = min(self.configuration['ball_velocity_max'], self.ball.velocity * self.configuration['ball_velocity_bounce_multiplier'])
self.ball.velocity_vec[0] = velocity[0] * self.ball.velocity
self.ball.velocity_vec[1] = velocity[1] * self.ball.velocity
self.player_left.hit()
self.play_sound(self.sound_paddle)
else:
# Right side bullet-through-paper check on ball and paddle.
if self.ball.velocity_vec[0] > 0:
intersect_point = line_line_intersect(
self.paddle_right.rect.left, self.paddle_right.rect.top,
self.paddle_right.rect.left, self.paddle_right.rect.bottom,
ball_position_x-self.ball.rect.width/2, ball_position_y+self.ball.rect.height/2,
self.ball.position_x-self.ball.rect.width/2, self.ball.position_y+self.ball.rect.height/2
)
if intersect_point:
self.ball.position_y = intersect_point[1]-self.ball.rect.height/2
if intersect_point or (self.paddle_right.rect.colliderect(self.ball.rect) and self.ball.rect.x < self.paddle_right.rect.x):
self.ball.position_x = self.paddle_right.rect.x - self.ball.rect.width
velocity = self.paddle_right.calculate_bounce(min(1,max(0,(self.ball.rect.centery - self.paddle_right.rect.y)/float(self.paddle_right.rect.height))))
self.ball.velocity = min(self.configuration['ball_velocity_max'], self.ball.velocity * self.configuration['ball_velocity_bounce_multiplier'])
self.ball.velocity_vec[0] = -velocity[0] * self.ball.velocity
self.ball.velocity_vec[1] = velocity[1] * self.ball.velocity
self.player_right.hit()
self.play_sound(self.sound_paddle)
# Bounds collision check
if self.ball.rect.y < self.bounds.top:
self.ball.position_y = float(self.bounds.top)
self.ball.velocity_vec[1] = -self.ball.velocity_vec[1]
self.play_sound(self.sound_wall)
elif self.ball.rect.y > self.bounds.bottom:
self.ball.position_y = float(self.bounds.bottom)
self.ball.velocity_vec[1] = -self.ball.velocity_vec[1]
self.play_sound(self.sound_wall)
# Check the ball is still in play
if self.ball.rect.x < self.bounds.x:
self.player_left.lost()
self.player_right.won()
self.score_right.score += 1
self.reset_game(False)
self.play_sound(self.sound_missed)
if self.ball.rect.x > self.bounds.right:
self.player_left.won()
self.player_right.lost()
self.score_left.score += 1
self.reset_game(True)
self.play_sound(self.sound_missed)
def draw(self, display_surface):
self.sprites.clear(display_surface, self.background)
return self.sprites.draw(display_surface)

View File

@ -0,0 +1,87 @@
import pygame, math
from pygame.sprite import Sprite
class Paddle(Sprite):
def __init__(self, velocity, image, bounds_y, *groups):
Sprite.__init__(self, *groups)
self.image = image
self.rect = self.image.get_rect()
self.direction = 0
self.velocity = velocity
self.bounds_y = bounds_y
# Like original pong, we break this up into 8 segments from the edge angle (acute_angle) to pi/2 at the center
# Changing acute_angle lets us change the extreme edge angle of the paddle.
acute_angle = .125
# Build the angles from acute_angle to the first 0.5 center value then append the values going from the
# second center 0.5 value by using the values we just calculated reversed.
angles = [acute_angle + (0.5-acute_angle)/3.0 * n for n in xrange(4)]
angles += map(lambda x: 1 + x * -1, reversed(angles))
# Final table is the output vector (x,y) of each angle
self.bounce_table = [(math.cos(n*math.pi-math.pi/2.0), math.sin(n*math.pi-math.pi/2.0)) for n in angles]
def update(self):
self.rect.y = max(self.bounds_y[0], min(self.bounds_y[1]-self.rect.height, self.rect.y + self.direction * self.velocity))
def calculate_bounce(self, delta):
return self.bounce_table[int(round(delta * (len(self.bounce_table)-1)))]
class Line(Sprite):
def __init__(self, image, *groups):
Sprite.__init__(self, *groups)
self.image = image
self.rect = self.image.get_rect()
class Ball(Sprite):
def __init__(self, velocity, image, *groups):
Sprite.__init__(self, *groups)
self.velocity = velocity
self.image = image
self.rect = self.image.get_rect()
self.position_vec = [0., 0.]
self.velocity_vec = [0., 0.]
def update(self):
self.position_vec[0] += self.velocity_vec[0]
self.position_vec[1] += self.velocity_vec[1]
self.rect.x = self.position_vec[0]
self.rect.y = self.position_vec[1]
def set_position_x(self, value):
self.position_vec[0] = value
self.rect.left = value
position_x = property(lambda self: self.position_vec[0], set_position_x)
def set_position_y(self, value):
self.position_vec[1] = value
self.rect.top = value
position_y = property(lambda self: self.position_vec[1], set_position_y)
class Score(Sprite):
def __init__(self, image_list, *groups):
Sprite.__init__(self, *groups)
self.image_list = image_list
self.image = None
self.rect = pygame.Rect(0,0,0,0)
self.score = 0
def get_score(self):
return self.score_value
def set_score(self, value):
self.score_value = value
digit_spacing = 8
digit_width = self.image_list[0].get_width()
digit_height = self.image_list[0].get_height()
values = map(int, reversed(str(self.score_value)))
surface_width = len(values) * digit_width + (len(values)-1) * digit_spacing
if not self.image or self.image.get_width() < surface_width:
self.image = pygame.Surface((surface_width, digit_height))
self.image.fill((0,0,0))
self.rect.width = self.image.get_width()
self.rect.height = self.image.get_height()
offset = self.image.get_width()-digit_width
for i in values:
self.image.blit(self.image_list[i], (offset, 0))
offset = offset - (digit_width + digit_spacing)
score = property(get_score, set_score)

View File

@ -0,0 +1,81 @@
import pygame, random
class BasicAIPlayer(object):
def __init__(self):
self.bias = random.random() - 0.5
self.hit_count = 0
def update(self, paddle, game):
# Dead simple AI, waits until the ball is on its side of the screen then moves the paddle to intercept.
# A bias is used to decide which edge of the paddle is going to be favored.
if (paddle.rect.x < game.bounds.centerx and game.ball.rect.x < game.bounds.centerx) or (paddle.rect.x > game.bounds.centerx and game.ball.rect.x > game.bounds.centerx):
delta = (paddle.rect.centery + self.bias * paddle.rect.height) - game.ball.rect.centery
if abs(delta) > paddle.velocity:
if delta > 0:
paddle.direction = -1
else:
paddle.direction = 1
else:
paddle.direction = 0
else:
paddle.direction = 0
def hit(self):
self.hit_count += 1
if self.hit_count > 6:
self.bias = random.random() - 0.5 # Recalculate our bias, this game is going on forever
self.hit_count = 0
def lost(self):
# If we lose, randomise the bias again
self.bias = random.random() - 0.5
def won(self):
pass
class KeyboardPlayer(object):
def __init__(self, input_state, up_key=None, down_key=None):
self.input_state = input_state
self.up_key = up_key
self.down_key = down_key
def update(self, paddle, game):
if self.input_state['key'][self.up_key]:
paddle.direction = -1
elif self.input_state['key'][self.down_key]:
paddle.direction = 1
else:
paddle.direction = 0
def hit(self):
pass
def lost(self):
pass
def won(self):
pass
class MousePlayer(object):
def __init__(self, input_state):
self.input_state = input_state
pygame.mouse.set_visible(False)
def update(self, paddle, game):
centery = paddle.rect.centery/int(paddle.velocity)
mousey = self.input_state['mouse'][1]/int(paddle.velocity)
if centery > mousey:
paddle.direction = -1
elif centery < mousey:
paddle.direction = 1
else:
paddle.direction = 0
def hit(self):
pass
def lost(self):
pass
def won(self):
pass