Juego de asteroids en python.
Con las teclas de dirección para moverse y el espacio para disparar es todo lo que necesitas para jugar.
No es gran cosa, pero aún así ayuda en mi viaje ya que lo mejor de este código es haber aprendido cómo manejar las animaciones, su dibujado y su relación con el tiempo, aunque no nos hallamos metido con los conceptos de fps (frames per second/frames por segundo)
Una vez más, toca ejecutarlo en CodeSkulptor por la librería gráfica propia de la plataforma.
RiceRocks: Pulsa para ver/ocultar el código
import simplegui import math import random # globals for user interface WIDTH = 800 HEIGHT = 600 score = 0 lives = 3 time = 0 started = False limit_rocks=12 class ImageInfo: def __init__(self, center, size, radius = 0, lifespan = None, animated = False): self.center = center self.size = size self.radius = radius if lifespan: self.lifespan = lifespan else: self.lifespan = float('inf') self.animated = animated def get_center(self): return self.center def get_size(self): return self.size def get_radius(self): return self.radius def get_lifespan(self): return self.lifespan def get_animated(self): return self.animated # art assets created by Kim Lathrop, may be freely re-used in non-commercial projects, please credit Kim # debris images - debris1_brown.png, debris2_brown.png, debris3_brown.png, debris4_brown.png # debris1_blue.png, debris2_blue.png, debris3_blue.png, debris4_blue.png, debris_blend.png debris_info = ImageInfo([320, 240], [640, 480]) debris_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/debris2_blue.png") # nebula images - nebula_brown.png, nebula_blue.png nebula_info = ImageInfo([400, 300], [800, 600]) nebula_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/nebula_blue.f2013.png") # splash image splash_info = ImageInfo([200, 150], [400, 300]) splash_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/splash.png") # ship image ship_info = ImageInfo([45, 45], [90, 90], 35) ship_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/double_ship.png") # missile image - shot1.png, shot2.png, shot3.png missile_info = ImageInfo([5,5], [10, 10], 3, 50) missile_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/shot2.png") # asteroid images - asteroid_blue.png, asteroid_brown.png, asteroid_blend.png asteroid_info = ImageInfo([45, 45], [90, 90], 40) asteroid_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/asteroid_blue.png") # animated explosion - explosion_orange.png, explosion_blue.png, explosion_blue2.png, explosion_alpha.png explosion_info = ImageInfo([64, 64], [128, 128], 17, 24, True) explosion_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/explosion_alpha.png") # sound assets purchased from sounddogs.com, please do not redistribute # .ogg versions of sounds are also available, just replace .mp3 by .ogg soundtrack = simplegui.load_sound("http://commondatastorage.googleapis.com/codeskulptor-assets/sounddogs/soundtrack.mp3") missile_sound = simplegui.load_sound("http://commondatastorage.googleapis.com/codeskulptor-assets/sounddogs/missile.mp3") missile_sound.set_volume(.5) ship_thrust_sound = simplegui.load_sound("http://commondatastorage.googleapis.com/codeskulptor-assets/sounddogs/thrust.mp3") explosion_sound = simplegui.load_sound("http://commondatastorage.googleapis.com/codeskulptor-assets/sounddogs/explosion.mp3") # helper functions to handle transformations def angle_to_vector(ang): return [math.cos(ang), math.sin(ang)] def dist(p, q): return math.sqrt((p[0] - q[0]) ** 2 + (p[1] - q[1]) ** 2) # Ship class class Ship: def __init__(self, pos, vel, angle, image, info): self.pos = [pos[0], pos[1]] self.vel = [vel[0], vel[1]] self.thrust = False self.angle = angle self.angle_vel = 0 self.image = image self.image_center = info.get_center() self.image_size = info.get_size() self.radius = info.get_radius() def get_position(self): return self.pos def get_radius(self): return self.radius def draw(self,canvas): if self.thrust: canvas.draw_image(self.image, [self.image_center[0] + self.image_size[0], self.image_center[1]] , self.image_size, self.pos, self.image_size, self.angle) else: canvas.draw_image(self.image, self.image_center, self.image_size, self.pos, self.image_size, self.angle) # canvas.draw_circle(self.pos, self.radius, 1, "White", "White") def update(self): # update angle self.angle += self.angle_vel # update position self.pos[0] = (self.pos[0] + self.vel[0]) % WIDTH self.pos[1] = (self.pos[1] + self.vel[1]) % HEIGHT # update velocity if self.thrust: acc = angle_to_vector(self.angle) self.vel[0] += acc[0] * .1 self.vel[1] += acc[1] * .1 self.vel[0] *= .99 self.vel[1] *= .99 def set_thrust(self, on): self.thrust = on if on: ship_thrust_sound.rewind() ship_thrust_sound.play() else: ship_thrust_sound.pause() def increment_angle_vel(self): self.angle_vel += .05 def decrement_angle_vel(self): self.angle_vel -= .05 def shoot(self): forward = angle_to_vector(self.angle) missile_pos = [self.pos[0] + self.radius * forward[0], self.pos[1] + self.radius * forward[1]] missile_vel = [self.vel[0] + 6 * forward[0], self.vel[1] + 6 * forward[1]] a_missile = Sprite(missile_pos, missile_vel, self.angle, 0, missile_image, missile_info, missile_sound) missiles.add(a_missile) # Sprite class class Sprite: def __init__(self, pos, vel, ang, ang_vel, image, info, sound = None): self.pos = [pos[0],pos[1]] self.vel = [vel[0],vel[1]] self.angle = ang self.angle_vel = ang_vel self.image = image self.image_center = info.get_center() self.image_size = info.get_size() self.radius = info.get_radius() self.lifespan = info.get_lifespan() self.animated = info.get_animated() self.age = 0 if sound: sound.rewind() sound.play() def draw(self, canvas): canvas.draw_image(self.image, self.image_center, self.image_size, self.pos, self.image_size, self.angle) def get_position(self): return self.pos def get_radius(self): return self.radius def update(self): # update angle self.angle += self.angle_vel # update position self.pos[0] = (self.pos[0] + self.vel[0]) % WIDTH self.pos[1] = (self.pos[1] + self.vel[1]) % HEIGHT if self.lifespan!=None: self.age=self.age+1 if self.age>self.lifespan: return True else: return False def collide(self, other_sprite): other_pos = other_sprite.get_position() other_rad = other_sprite.get_radius() if dist(self.pos, other_pos) < (other_rad + self.radius): return True else: return False # key handlers to control ship def keydown(key): if key == simplegui.KEY_MAP['left']: my_ship.decrement_angle_vel() elif key == simplegui.KEY_MAP['right']: my_ship.increment_angle_vel() elif key == simplegui.KEY_MAP['up']: my_ship.set_thrust(True) elif key == simplegui.KEY_MAP['space']: my_ship.shoot() def keyup(key): if key == simplegui.KEY_MAP['left']: my_ship.increment_angle_vel() elif key == simplegui.KEY_MAP['right']: my_ship.decrement_angle_vel() elif key == simplegui.KEY_MAP['up']: my_ship.set_thrust(False) # mouseclick handlers that reset UI and conditions whether splash image is drawn def click(pos): global started, timer center = [WIDTH / 2, HEIGHT / 2] size = splash_info.get_size() inwidth = (center[0] - size[0] / 2) < pos[0] < (center[0] + size[0] / 2) inheight = (center[1] - size[1] / 2) < pos[1] < (center[1] + size[1] / 2) if (not started) and inwidth and inheight: started = True timer.start() soundtrack.rewind() soundtrack.play() def draw(canvas): global time, started, lives, score # animiate background time += 1 wtime = (time / 4) % WIDTH center = debris_info.get_center() size = debris_info.get_size() canvas.draw_image(nebula_image, nebula_info.get_center(), nebula_info.get_size(), [WIDTH / 2, HEIGHT / 2], [WIDTH, HEIGHT]) canvas.draw_image(debris_image, center, size, (wtime - WIDTH / 2, HEIGHT / 2), (WIDTH, HEIGHT)) canvas.draw_image(debris_image, center, size, (wtime + WIDTH / 2, HEIGHT / 2), (WIDTH, HEIGHT)) if started: #if touched if collide_group(rocks, my_ship): lives=lives-1 if lives==0: reset_game() number_hits=collide_two_group(rocks, missiles) score=score+number_hits*10 # draw ship and sprites my_ship.draw(canvas) draw_set(rocks, canvas) draw_set(missiles, canvas) # update ship and sprites my_ship.update() # draw UI canvas.draw_text("Lives", [50, 50], 22, "White") canvas.draw_text("Score", [680, 50], 22, "White") canvas.draw_text(str(lives), [50, 80], 22, "White") canvas.draw_text(str(score), [680, 80], 22, "White") # draw splash screen if not started if not started: canvas.draw_image(splash_image, splash_info.get_center(), splash_info.get_size(), [WIDTH / 2, HEIGHT / 2], splash_info.get_size()) def draw_set(the_set, canvas): for element in list(the_set): element.draw(canvas) if element.update(): the_set.remove(element) # timer handler that spawns a rock def rock_spawner(): global limit_rocks if limit_rocks>len(rocks) : rock_pos = [random.randrange(0, WIDTH), random.randrange(0, HEIGHT)] rock_vel = [random.random() * .6 - .3, random.random() * .6 - .3] rock_avel = random.random() * .2 - .1 new_rock = Sprite(rock_pos, rock_vel, 0, rock_avel, asteroid_image, asteroid_info) if new_rock.collide(my_ship): rock_spawner() else: rocks.add(new_rock) def collide_group(the_set, element): for one_of_set in list(the_set): if one_of_set.collide(element): the_set.remove(one_of_set) return True return False def collide_two_group(rock, missil): count_collide=0 for one_rock in list(rock): for a_missil in list(missil): if one_rock.collide(a_missil): count_collide=count_collide+1 missil.remove(a_missil) rock.remove(one_rock) return count_collide def reset_game(): global lives, score,started, timer, number_rocks, rocks started=False number_rocks=0 lives=3 score=0 rocks=set([]) timer.stop() soundtrack.pause() # initialize stuff frame = simplegui.create_frame("Asteroids", WIDTH, HEIGHT) # initialize ship and two sprites my_ship = Ship([WIDTH / 2, HEIGHT / 2], [0, 0], 0, ship_image, ship_info) rocks=set([]) missiles=set([]) # register handlers frame.set_keyup_handler(keyup) frame.set_keydown_handler(keydown) frame.set_mouseclick_handler(click) frame.set_draw_handler(draw) timer = simplegui.create_timer(1000.0, rock_spawner) # get things rolling frame.start()