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.radius
3) 在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.
射中后的动效:
游戏画面: