RiceRocks是Asteroids游戏的升级版,也是Spaceship完善后的结果。
相较于Mini-project#7,更改并补充了许多功能,包括对原来的飞船类、Sprite类进行修改,创建星石、导弹组,碰撞函数,开始与结束界面,也增加了星石被击中的音效、动效。这样阶段性地完成项目,是很值得采用的方法!
Mini-project #8详细描述:https://www.coursera.org/learn/interactive-python-2/supplement/GjfKF/mini-project-description
1. 对星石块的处理:
初始化时,rock、missile、explosion因为有多个,所以改为组。
# initialize ship and two sprites my_ship = Ship([WIDTH / 2, HEIGHT / 2], [0, 0], 0, ship_image, ship_info, ) rock_group = set([]) missile_group = set([]) explosion_group = set([])星石生成函数做很多更改:
def rock_spawner(): #随机生成星石 global rock_group, started if len(rock_group)<12 and started: #限定数量最多12个,且在游戏开始后产生 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 while dist(rock_pos, my_ship.pos) < 60 : #当星石与飞船离得太近,重新生成 rock_pos = [random.randrange(0, WIDTH), random.randrange(0, HEIGHT)] a_rock = Sprite(rock_pos, rock_vel, 0, rock_avel, asteroid_image, asteroid_info) rock_group.add(a_rock) #将星石归入石组中Sprite class中:
def update(self): #Sprite类的update部分,添加了随着时间消失效果 # 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: if self.age > self.lifespan: return True #如果年龄大于寿命,消失 else: self.age += 1 return False添加石组处理函数:
def process_sprite_group(canvas, group): for sprite in set(group): sprite.draw(canvas) if sprite.update(): #update部分返回的参数传到此,决定星石是否消失 group.remove(sprite)
2. 碰撞部分:
首先在Sprite类中添加碰撞函数:
def collide(self, other_object): if dist( self.pos, other_object.pos ) <= self.radius+my_ship.radius: return True else: return False当飞船撞上星石,就需要增加组与物体的碰撞函数:
def group_collide(group, other_object): global explosion_group #爆炸组,在星石被子弹击中或被飞船撞,都会使用这个,从而产生动效,下文会讲 for sprite in list(group): #在复制的组中循环,可以更改原组 if sprite.collide(other_object): explosion_group.add(Sprite(sprite.get_position(), (0, 0), 0, 0, explosion_image, explosion_info, explosion_sound)) group.remove(sprite) return True else: return False当导弹击中星石,就是组与组间碰撞了:
def group_group_collide(group1, group2): global score for sprite in list(group2): if group_collide(group1, sprite): #调用物体与组的碰撞函数 group2.remove(sprite) score += 1 #每击中1次,子弹与星石消失,分数加1
最后,在draw函数中,调用各种类型的碰撞函数,检测碰撞:
# draw collision if group_collide(rock_group, my_ship): lives -= 1 group_group_collide(rock_group, missile_group)
3. 添加星石爆炸效果:
在Sprite类中更新draw函数:
def draw(self, canvas): if self.animated: image_tile = (self.age%24) // 1 #动画效果 由24个图像组成 self.image_center = [self.image_center[0]+image_tile*self.image_size[0],self.image_center[1]] canvas.draw_image(self.image, self.image_center, self.image_size, self.pos, self.image_size, self.angle)在碰撞中,在爆炸组中添加被撞到的星石
def group_collide(group, other_object): global explosion_group for sprite in list(group): #在复制的组中循环,可以更改原组 if sprite.collide(other_object): explosion_group.add(Sprite(sprite.get_position(), (0, 0), 0, 0, explosion_image, explosion_info, explosion_sound)) group.remove(sprite) return True else: return False
4. 与Mini-project #7相比做的其它改变:
1) 由于导弹成组,所以对Ship类中的shoot函数做了更新:
def shoot(self): global missile_group 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]] missile_group.add(Sprite(missile_pos, missile_vel, self.angle, 0, missile_image, missile_info, missile_sound))
对飞船的运动做了更改:
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
2) 对Sprite类添加了返回位置、半径函数
def get_position(self): return self.pos def get_radius(self): return self.radius3) 在draw(canvas)函数中,引入全局变量:
started = False #最开始started初始化为False
global time, started, lives, rock_group
sprite的更新与绘制都集中在process_sprite_group函数中
# draw ship and update sprites my_ship.draw(canvas) process_sprite_group(canvas, rock_group) process_sprite_group(canvas, missile_group) process_sprite_group(canvas, explosion_group) # update ship my_ship.update()添加游戏开始与结束画面:
# reset game if lives <1: started = False rock_group = set([]) soundtrack.pause() timer.stop() # 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())4) 添加鼠标点击开始游戏功能
# mouseclick handlers that reset UI and conditions whether splash image is drawn def click(pos): global started, score, lives, my_ship, rock_group, missile_group 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 score = 0 lives = 3 timer.start() soundtrack.rewind() soundtrack.play my_ship = Ship([WIDTH / 2, HEIGHT / 2], [0, 0], 0, ship_image, ship_info)引入鼠标控制
frame.set_mouseclick_handler(click)
5.
射中后的动效:
游戏画面: