import sys
import math
import random
import pyglet
# ask for a plain 640x480 window
w = pyglet.window.Window()
def center_anchor(img):
'''Center the anchor of the given image.'''
img.anchor_x = img.width // 2
img.anchor_y = img.height // 2
# load the ship image, center the anchor
ship_image = pyglet.image.load('data/ship.png')
center_anchor(ship_image)
# create a sprite with that image, intial position in the center of the
# screen and not moving or rotating
ship = pyglet.sprite.Sprite(ship_image)
ship.position = (w.width/2, w.height/2)
ship.dx = ship.dy = ship.dr = 0
ship.gun_cooldown = 0
# load the bullet image for later sprites, center anchored
bullet_image = pyglet.image.load('data/bullet.png')
center_anchor(bullet_image)
# keep track of the currently-active bullet sprites
bullets = []
# load the three sizes of asteroid image and center their anchors
small_asteroid_image = pyglet.image.load('data/small_asteroid.png')
center_anchor(small_asteroid_image)
medium_asteroid_image = pyglet.image.load('data/medium_asteroid.png')
center_anchor(medium_asteroid_image)
big_asteroid_image = pyglet.image.load('data/big_asteroid.png')
center_anchor(big_asteroid_image)
# keep track of the currently-active asteroid sprites
asteroids = []
# create 4 starting big asteroids with random positioning, speed and
# rotation
for i in range(3):
x = random.randint(0, w.width)
y = random.randint(0, w.height)
s = pyglet.sprite.Sprite(big_asteroid_image, x, y)
s.dx = random.randint(-100, 100)
s.dy = random.randint(-100, 100)
s.dr = random.randint(-100, 100)
asteroids.append(s)
# load up our sound effects
explosion_sound = pyglet.media.load('data/explosion.wav', streaming=False)
bullet_sound = pyglet.media.load('data/bullet.wav', streaming=False)
# set up the game's drawing event handler
@w.event
def on_draw():
'''Draw the game to the screen.'''
w.clear()
for s in asteroids + bullets:
s.draw()
ship.draw()
# use the standard pyglet keyboard state event handler
keys = pyglet.window.key.KeyStateHandler()
w.push_handlers(keys)
def distance(a, b):
'''Determine the distance between two points.'''
return math.sqrt((a.x-b.x)**2 + (a.y-b.y)**2)
def collide(a, b):
'''Determine whether two objects with a center point and width
(diameter) are colliding.'''
return distance(a, b) < (a.width/2 + b.width/2)
def update(dt):
'''Update the game for the "dt" seconds that have passed.'''
# update the ship's speed of rotation based on the combination of
# left/right arrow being held down
ship.dr = (keys[pyglet.window.key.RIGHT] - keys[pyglet.window.key.LEFT]) * 360
# determine the radian angle of rotation and thereby the X and Y
# components of the ship's heading
rotation = math.pi * ship.rotation / 180.0
rotation_x = math.cos(-rotation)
rotation_y = math.sin(-rotation)
# if the thrust (up) key is pressed then accelerate in the current
# heading by increasing the ship's dx/dy
if keys[pyglet.window.key.UP]:
ship.dx += 200 * rotation_x * dt
ship.dy += 200 * rotation_y * dt
# if the ship's gun has recently been fired then cool it down,
# otherwise fire a new bulled if the fire (space) key has been pressed
if ship.gun_cooldown:
ship.gun_cooldown = max(0, ship.gun_cooldown - dt)
elif keys[pyglet.window.key.SPACE] and len(bullets) < 2:
# the gun is firing so create a new bullet over the ship with a
# speed of roughly 500 pixels per second in the same heading as the
# ship.
b = pyglet.sprite.Sprite(bullet_image, ship.x,
ship.y)
b.dx = rotation_x * 500
b.dy = rotation_y * 500
b.dr = 0
# set the bullet to live for 1 second
b.life = 1
# retain the bullet in our bullet sprites list
bullets.append(b)
# set the cooldown to 2 shots per second
ship.gun_cooldown = .5
# play a nice sound effect
bullet_sound.play()
# kill off any old bullets
for b in list(bullets):
b.life -= dt
if b.life < 0:
bullets.remove(b)
# update the position and rotation of all our sprites based on their
# dx, dy and dr attributes
for a in asteroids + [ship] + bullets:
a.x += a.dx*dt
a.y += a.dy*dt
a.rotation += a.dr * dt
# wrap all sprites once they're off the screen
if a.x - a.width/2 > w.width:
a.x -= w.width + a.width
elif a.x + a.width/2 < 0:
a.x += w.width + a.width
if a.y - a.height/2 > w.height:
a.y -= w.height + a.height
elif a.y + a.height/2 < 0:
a.y += w.height + a.height
# check collisions with the asteroids
for a in list(asteroids):
# if the ship collides then it's GAME OVER
if collide(a, ship):
sys.exit('GAME OVER')
# if any bullet collides then the asteroid is damaged/destroyed
for b in list(bullets):
if collide(a, b):
bullets.remove(b)
asteroids.remove(a)
explosion_sound.play()
if a.image is big_asteroid_image.texture:
# if it's a big asteroid then make two medium asteroids in
# its place
for i in range(2):
s = pyglet.sprite.Sprite(medium_asteroid_image, a.x, a.y)
# modify the parent speed/rotation by some random
# amount
s.dx = a.dx + random.randint(-50, 50)
s.dy = a.dy + random.randint(-50, 50)
s.dr = a.dr + random.randint(-50, 50)
asteroids.append(s)
elif a.image is medium_asteroid_image.texture:
# if it's a medium asteroid then make two small asteroids in
# its place
for i in range(2):
s = pyglet.sprite.Sprite(small_asteroid_image, a.x, a.y)
# modify the parent speed/rotation by some random
# amount
s.dx = a.dx + random.randint(-50, 50)
s.dy = a.dy + random.randint(-50, 50)
s.dr = a.dr + random.randint(-50, 50)
asteroids.append(s)
# if there's no more asteroids then the player has won!
if not asteroids:
sys.exit('YOU WIN')
# schedule our update function to be called 30 times per second
pyglet.clock.schedule_interval(update, 1./30)
# invoke the pyglet runtime (event handling, clock ticking)
pyglet.app.run()