Saturday, August 3, 2013

Asteroids with Python (for codeskulptor)


1:  import simplegui  
2:  import math  
3:  import random  
4:    
5:  # globals for user interface  
6:  WIDTH = 800  
7:  HEIGHT = 600  
8:  score = 0  
9:  lives = 3  
10:  time = 0.5  
11:  started = False  
12:  rock_group = set()  
13:  missile_group = set()  
14:  explosion_group = set()  
15:  collisions = 0  
16:  n_collisions = 0  
17:  r_collisions = 0  
18:    
19:  class ImageInfo:  
20:    def __init__(self, center, size, radius = 0, lifespan = None, animated = False):  
21:      self.center = center  
22:      self.size = size  
23:      self.radius = radius  
24:      if lifespan:  
25:        self.lifespan = lifespan  
26:      else:  
27:        self.lifespan = float('inf')  
28:      self.animated = animated  
29:    
30:    def get_center(self):  
31:      return self.center  
32:    
33:    def get_size(self):  
34:      return self.size  
35:    
36:    def get_radius(self):  
37:      return self.radius  
38:    
39:    def get_lifespan(self):  
40:      return self.lifespan  
41:    
42:    def get_animated(self):  
43:      return self.animated  
44:    
45:      
46:  # art assets created by Kim Lathrop, may be freely re-used in non-commercial projects, please credit Kim  
47:      
48:  # debris images - debris1_brown.png, debris2_brown.png, debris3_brown.png, debris4_brown.png  
49:  #         debris1_blue.png, debris2_blue.png, debris3_blue.png, debris4_blue.png, debris_blend.png  
50:  debris_info = ImageInfo([320, 240], [640, 480])  
51:  debris_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/debris2_blue.png")  
52:    
53:  # nebula images - nebula_brown.png, nebula_blue.png  
54:  nebula_info = ImageInfo([400, 300], [800, 600])  
55:  nebula_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/nebula_blue.png")  
56:    
57:  # splash image  
58:  splash_info = ImageInfo([200, 150], [400, 300])  
59:  splash_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/splash.png")  
60:    
61:  # ship image  
62:  ship_info = ImageInfo([45, 45], [90, 90], 35)  
63:  ship_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/double_ship.png")  
64:    
65:  # missile image - shot1.png, shot2.png, shot3.png  
66:  missile_info = ImageInfo([5,5], [10, 10], 3, 50)  
67:  missile_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/shot2.png")  
68:    
69:  # asteroid images - asteroid_blue.png, asteroid_brown.png, asteroid_blend.png  
70:  asteroid_info = ImageInfo([45, 45], [90, 90], 40)  
71:  asteroid_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/asteroid_blue.png")  
72:    
73:  # animated explosion - explosion_orange.png, explosion_blue.png, explosion_blue2.png, explosion_alpha.png  
74:  explosion_info = ImageInfo([64, 64], [128, 128], 17, 24, True)  
75:  explosion_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/explosion_alpha.png")  
76:    
77:  # sound assets purchased from sounddogs.com, please do not redistribute  
78:  # .ogg versions of sounds are also available, just replace .mp3 by .ogg  
79:  soundtrack = simplegui.load_sound("http://commondatastorage.googleapis.com/codeskulptor-assets/sounddogs/soundtrack.ogg")  
80:  missile_sound = simplegui.load_sound("http://commondatastorage.googleapis.com/codeskulptor-assets/sounddogs/missile.ogg")  
81:  missile_sound.set_volume(.5)  
82:  ship_thrust_sound = simplegui.load_sound("http://commondatastorage.googleapis.com/codeskulptor-assets/sounddogs/thrust.ogg")  
83:  explosion_sound = simplegui.load_sound("http://commondatastorage.googleapis.com/codeskulptor-assets/sounddogs/explosion.ogg")  
84:    
85:  # helper functions to handle transformations  
86:  def angle_to_vector(ang):  
87:    return [math.cos(ang), math.sin(ang)]  
88:    
89:  def dist(p, q):  
90:    return math.sqrt((p[0] - q[0]) ** 2 + (p[1] - q[1]) ** 2)  
91:    
92:    
93:  # Ship class  
94:  class Ship:  
95:    
96:    def __init__(self, pos, vel, angle, image, info):  
97:      self.pos = [pos[0], pos[1]]  
98:      self.vel = [vel[0], vel[1]]  
99:      self.thrust = False  
100:      self.angle = angle  
101:      self.angle_vel = 0  
102:      self.image = image  
103:      self.image_center = info.get_center()  
104:      self.image_size = info.get_size()  
105:      self.radius = info.get_radius()  
106:        
107:    def draw(self,canvas):  
108:      if self.thrust:  
109:        canvas.draw_image(self.image, [self.image_center[0] + self.image_size[0], self.image_center[1]] , self.image_size,  
110:                 self.pos, self.image_size, self.angle)  
111:      else:  
112:        canvas.draw_image(self.image, self.image_center, self.image_size,  
113:                 self.pos, self.image_size, self.angle)  
114:      # canvas.draw_circle(self.pos, self.radius, 1, "White", "White")  
115:    
116:    def update(self):  
117:      # update angle  
118:      self.angle += self.angle_vel  
119:        
120:      # update position  
121:      self.pos[0] = (self.pos[0] + self.vel[0]) % WIDTH  
122:      self.pos[1] = (self.pos[1] + self.vel[1]) % HEIGHT  
123:    
124:      # update velocity  
125:      if self.thrust:  
126:        acc = angle_to_vector(self.angle)  
127:        self.vel[0] += acc[0] * .5  
128:        self.vel[1] += acc[1] * .5  
129:          
130:      self.vel[0] *= .95  
131:      self.vel[1] *= .95  
132:    
133:    def set_thrust(self, on):  
134:      self.thrust = on  
135:      if on:  
136:        ship_thrust_sound.rewind()  
137:        ship_thrust_sound.play()  
138:      else:  
139:        ship_thrust_sound.pause()  
140:        
141:    def increment_angle_vel(self):  
142:      self.angle_vel += .05  
143:        
144:    def decrement_angle_vel(self):  
145:      self.angle_vel -= .05  
146:        
147:    def shoot(self):  
148:      global a_missile  
149:      forward = angle_to_vector(self.angle)  
150:      missile_pos = [self.pos[0] + self.radius * forward[0], self.pos[1] + self.radius * forward[1]]  
151:      missile_vel = [self.vel[0] + 6 * forward[0], self.vel[1] + 6 * forward[1]]  
152:      a_missile = Sprite(missile_pos, missile_vel, self.angle, 0, missile_image, missile_info, missile_sound)  
153:      missile_group.add(a_missile)  
154:      
155:      
156:      
157:  # Sprite class  
158:  class Sprite:  
159:    def __init__(self, pos, vel, ang, ang_vel, image, info, sound = None):  
160:      self.pos = [pos[0],pos[1]]  
161:      self.vel = [vel[0],vel[1]]  
162:      self.angle = ang  
163:      self.angle_vel = ang_vel  
164:      self.image = image  
165:      self.image_center = info.get_center()  
166:      self.image_size = info.get_size()  
167:      self.radius = info.get_radius()  
168:      self.lifespan = info.get_lifespan()  
169:      self.animated = info.get_animated()  
170:      self.age = 0  
171:      if sound:  
172:        sound.rewind()  
173:        sound.play()  
174:      
175:    def draw(self, canvas):  
176:      if self.animated == True:  
177:        center = [self.image_center[0] + 2*self.image_center[0]*self.age, self.image_center[1]]  
178:        canvas.draw_image(self.image, center, self.image_size,  
179:               self.pos, self.image_size, self.angle)  
180:      else:  
181:        canvas.draw_image(self.image, self.image_center, self.image_size,  
182:               self.pos, self.image_size, self.angle)  
183:    
184:    def update(self):  
185:      # update angle  
186:      self.angle += self.angle_vel  
187:        
188:      # update position  
189:      self.pos[0] = (self.pos[0] + self.vel[0]) % WIDTH  
190:      self.pos[1] = (self.pos[1] + self.vel[1]) % HEIGHT  
191:      self.age += 1  
192:      if self.age >= self.lifespan:  
193:        return True  
194:      else:  
195:        return False  
196:        
197:    def collide(self, other_object):  
198:      if dist(self.pos, other_object.pos) > self.radius + other_object.radius:  
199:        return False  
200:      else:  
201:        return True  
202:          
203:     
204:        
205:  # key handlers to control ship    
206:  def keydown(key):  
207:    if key == simplegui.KEY_MAP['left']:  
208:      my_ship.decrement_angle_vel()  
209:    elif key == simplegui.KEY_MAP['right']:  
210:      my_ship.increment_angle_vel()  
211:    elif key == simplegui.KEY_MAP['up']:  
212:      my_ship.set_thrust(True)  
213:    elif key == simplegui.KEY_MAP['space']:  
214:      my_ship.shoot()  
215:        
216:  def keyup(key):  
217:    if key == simplegui.KEY_MAP['left']:  
218:      my_ship.increment_angle_vel()  
219:    elif key == simplegui.KEY_MAP['right']:  
220:      my_ship.decrement_angle_vel()  
221:    elif key == simplegui.KEY_MAP['up']:  
222:      my_ship.set_thrust(False)  
223:        
224:  # mouseclick handlers that reset UI and conditions whether splash image is drawn  
225:  def click(pos):  
226:    global started, lives, score  
227:    center = [WIDTH / 2, HEIGHT / 2]  
228:    size = splash_info.get_size()  
229:    inwidth = (center[0] - size[0] / 2) < pos[0] < (center[0] + size[0] / 2)  
230:    inheight = (center[1] - size[1] / 2) < pos[1] < (center[1] + size[1] / 2)  
231:    if (not started) and inwidth and inheight:  
232:      started = True  
233:      lives = 3  
234:      score = 0  
235:      soundtrack.play()  
236:    
237:  def draw(canvas):  
238:    global time, started, lives, score, rock_group  
239:      
240:    # animiate background  
241:    time += 1  
242:    center = debris_info.get_center()  
243:    size = debris_info.get_size()  
244:    wtime = (time / 8) % center[0]  
245:    canvas.draw_image(nebula_image, nebula_info.get_center(), nebula_info.get_size(), [WIDTH / 2, HEIGHT / 2], [WIDTH, HEIGHT])  
246:    canvas.draw_image(debris_image, [center[0] - wtime, center[1]], [size[0] - 2 * wtime, size[1]],   
247:                  [WIDTH / 2 + 1.25 * wtime, HEIGHT / 2], [WIDTH - 2.5 * wtime, HEIGHT])  
248:    canvas.draw_image(debris_image, [size[0] - wtime, center[1]], [2 * wtime, size[1]],   
249:                  [1.25 * wtime, HEIGHT / 2], [2.5 * wtime, HEIGHT])  
250:    
251:    if group_collide(rock_group, my_ship) > 0:  
252:      lives -= 1  
253:        
254:    if group_group_collide(rock_group, missile_group) > 0:  
255:      score += 1  
256:        
257:    if lives == 0:  
258:      started = False  
259:      rock_group = set()  
260:      soundtrack.rewind()  
261:      lives = 3  
262:        
263:    # draw UI  
264:    canvas.draw_text("Lives", [50, 50], 22, "White")  
265:    canvas.draw_text("Score", [680, 50], 22, "White")  
266:    canvas.draw_text(str(lives), [50, 80], 22, "White")  
267:    canvas.draw_text(str(score), [680, 80], 22, "White")  
268:    
269:    # draw ship and sprites  
270:    my_ship.draw(canvas)  
271:      
272:    process_sprite_group(rock_group, canvas)  
273:    process_sprite_group(missile_group, canvas)  
274:    process_sprite_group(explosion_group, canvas)  
275:      
276:        
277:    for a_missile in set(missile_group):  
278:      if a_missile.age >= a_missile.lifespan:  
279:        missile_group.remove(a_missile)  
280:      else:  
281:        pass  
282:      a_missile.draw(canvas)  
283:      a_missile.update()  
284:      
285:    # update ship and sprites  
286:    my_ship.update()  
287:    
288:    # draw splash screen if not started  
289:    if not started:  
290:      canvas.draw_image(splash_image, splash_info.get_center(),   
291:               splash_info.get_size(), [WIDTH / 2, HEIGHT / 2],   
292:               splash_info.get_size())  
293:    
294:  # timer handler that spawns a rock    
295:  def rock_spawner():  
296:    global a_rock, rock_group, score  
297:    if started:  
298:      if len(rock_group) < 12:  
299:        rock_pos = [random.randrange(0, WIDTH), random.randrange(0, HEIGHT)]  
300:        rock_vel = [random.random() * .6 - .3, random.random() * .6 - .3]  
301:        rock_avel = random.random() * .2 - .1  
302:        if score > 10:  
303:          rock_vel[0] *= 4  
304:          rock_vel[1] *= 4  
305:        elif score > 20:  
306:          rock_vel[0] *= 6  
307:          rock_vel[1] *= 6  
308:        elif score > 30:  
309:          rock_vel[0] *= 10  
310:          rock_vel[1] *= 10  
311:        elif score > 40:  
312:          rock_vel[0] *= 20  
313:          rock_vel[1] *= 20  
314:            
315:        a_rock = Sprite(rock_pos, rock_vel, 0, rock_avel, asteroid_image, asteroid_info)  
316:        if dist(my_ship.pos, rock_pos) > 3*my_ship.radius:  
317:          rock_group.add(a_rock)  
318:        else:  
319:          pass  
320:      else:  
321:        pass  
322:      
323:  def process_sprite_group(group, canvas):  
324:    global collisions, lives  
325:      
326:    for sprite in set(group):  
327:      sprite.draw(canvas)  
328:      if sprite.update() == True:  
329:        group.remove(sprite)  
330:      else:  
331:        pass  
332:        
333:      sprite.update()  
334:    
335:  def group_collide(group, sprite):  
336:    global collisions, explosion_group  
337:    for item in set(group):  
338:      if item.collide(sprite) == True:  
339:        group.remove(item)  
340:        exp = Sprite(item.pos, [0, 0], 0, 0, explosion_image, explosion_info, explosion_sound)  
341:        explosion_group.add(exp)  
342:        collisions += 1  
343:        return collisions  
344:      else:  
345:        pass  
346:       
347:        
348:  def group_group_collide(group1, group2):  
349:    global n_collisions, r_collisions  
350:    for item1 in set(group1):  
351:      if group_collide(group2, item1) > 0:  
352:        r_collisions += 1  
353:        group1.remove(item1)  
354:        return r_collisions  
355:      else:  
356:        pass  
357:          
358:    
359:          
360:  # initialize stuff  
361:  frame = simplegui.create_frame("Asteroids", WIDTH, HEIGHT)  
362:    
363:  # initialize ship and two sprites  
364:  my_ship = Ship([WIDTH / 2, HEIGHT / 2], [0, 0], 0, ship_image, ship_info)  
365:    
366:  #a_rock = Sprite([WIDTH / 3, HEIGHT / 3], [1, 1], 0, .1, asteroid_image, asteroid_info)  
367:  #a_missile = Sprite([2 * WIDTH / 3, 2 * HEIGHT / 3], [-1,1], 0, 0, missile_image, missile_info, missile_sound)  
368:    
369:    
370:  # register handlers  
371:  frame.set_keyup_handler(keyup)  
372:  frame.set_keydown_handler(keydown)  
373:  frame.set_mouseclick_handler(click)  
374:  frame.set_draw_handler(draw)  
375:    
376:  timer = simplegui.create_timer(1000.0, rock_spawner)  
377:    
378:  # get things rolling  
379:  timer.start()  
380:  frame.start()  
381:    
382:    

No comments:

Post a Comment

Linguistics and Information Theory