Rice Rock
先翻译评分要点,然后一点点翻译程序实现过程
如何产生一堆岩石?
rock_group = set([])#空集合,全局变量
rock_group.add(a_rock)
要画出来draw handler?
用rock_group代替a_rock,因为要产生一组岩石,有很多a_rock,代替哪一个呢?
在draw handler里面我用:
for n in rock_group:
n.draw(canvea)
可以产生一堆岩石了,但是岩石后来就不转了,而且岩石数目一直增加-------用timer.stop()解决了
process_sprite_group
看论坛,找到了答案
现在做撞击,看论坛,找到了答案。
如何产生一组导弹,岩石碰撞后不能再生
已经可以产生一组导弹了,但是导弹寿命要调整
碰撞后岩石消失,但是分数没有增加,岩石消失不再出现
分数已经增加啦,这个我自己做出来的
如何生成的导弹不碰到飞船
完成啦啦啦
Mini-project description - RiceRocks (Asteroids)
开发过程
要有多重火箭和多重导弹。飞船撞到岩石会丢一条命, 导弹打到岩石得一分, 你要更新得分和生命情况,在合适的时候结束游戏。 .当发生碰撞的时候你可以选择添加爆炸动画
Tip #1 - Spawning rocks多重岩石
为了控制多重岩石,用rock_group代替 a_rock
.在rock_spawner里面,你应该产生一个本地的a_rock的副本并添加到全局集合rock_group里面。 为了避免超过12个岩石,你可以使用一个基于rock_group长度的测试。 为了测试你的代码,你可以添加一个for循环到draw handler里面以这种形式: 对每个在rock_group里面的岩石, 画出那个岩石。这个draw以后将被放到 process_sprite_group
里面在第一部分的最后一步
Phase one - 多重岩石
- 用rock_group代替a_rock.复位岩石组成空的集合set. 你的rock spawner产生一个新岩石(Sprite的一个对象实例) 并添加到rock_group里面
- 修改你的rock spawner使得屏幕里面的岩石数量有上限。我们建议最多12个太多岩石的话游戏会不那么好玩而且动画会明显变慢。
- 建立一个process_sprite_group函数. 这个函数应该有一个集合(set)和画布, 并调用update和draw函数对每个群组里面的sprite.
- 在draw handler里面调用process_sprite_group功能在rock_group里面
这部分,你要检测飞船和岩石的撞击。 发生撞击,岩石要毁坏并且玩家要丢失生命. 为了完成飞船岩石撞击:你要这么做:
- 在Sprite类里面添加一个collide功能。 这个应该有个other_object并返回True,如果有撞击返回
False
. 目前,这个其他对象将一直是你的飞船, 但我们也想要能用这个colide方法来侦测跟导弹的撞击。 可以使用这两个对象的半径侦测撞击。这需要你完成get_position
和get_radius在Sprite和get_radius. - 完成group_collide函数。这个函数要有个set
group
和一个spritother_object
并经检查在other_object和group里的元素的撞击。如果有撞击, 撞击对象应该从group里面移除。为了避免移除你正在迭代的set(可能产生一个严重的bug), 通过set(group)
创建一个副本迭代. 这个函数应该返回True
或者False取决于是否有撞击。确定使用第一部分的collide方法 在sprites在group里面来完成这个任务。 - 在draw handler里面使用group_collide判定是否飞船撞击到岩石.要是这样,减少一条生命值。 现在你可以有负的生命值
Phase three - 导弹
在这部分你要产生一个导弹组,在按空格的时候产生新的导弹加入导弹组里面。需要如下步骤:
- 删掉a_missile并用missile_group代替。 复位导弹组为空组。修改my_ship里面的shoot函数来创造新导弹。 (一个Sprite类里面的实例) 并添加到missile_group里面. , 如果你用模板的代码,每次生产导弹的时候开火的声音应该自动播放
- 在draw handler里面,使用你的process_sprite_group函数来process
missile_group
. 当你发射导弹的时候,你会注意到他们永远在飞。 为了修正这个,我们需要修改Sprite类和process_sprite_group. - 在Sprite类里面的update函数。每次调用
update
,sprite要增加。如果寿命大于或者等于sprite的生命,我们要删掉它。. 所以return返回False
(意味着我们要保留它) 如果age少于寿命并且True
(意味着我们要删掉它) 否则. - 修改process_sprite_group来检查返回的值对sprites. 如果返回
True
, 从group里面删掉sprite。再一次 你将希望迭代sprite群的副本在process_sprite_group.来避免删掉从相同的set在你迭代的。
group_collide
, 因为我要检查两个group的碰撞。我们要做的就是加一个新的函数 :- 完成最后一个函数
group_group_collide
,啥呢,将两个grops的对象作为输入.group_group_collide
应该使用一个for循环在第一组副本元素迭代,然后第二组的所有元素调用group_collide.group_group_collide应该返回第一组元素跟第二在元素撞击并删除第一组中的这些元素。 你可能会发现set的discard
功能在这里很有用。 - 在draw handler里面调用
group_group_collide
来检测导弹/岩石的碰撞。 根据导弹撞击的数目增加分数。
基本完了,再添加一点
- 添加代码到draw handler里面 , 要是生命数变成0了,游戏重置,并且出现启动画面.尤其, set the 设置started为False, 毁掉所有岩石并阻止任何岩石生成,直到游戏重新开始。
- 当游戏开始/重新开始. 确保生命数和分数也重置。开始生成岩石 .播放/重新开始加载在程序模板的soundtrack变量里面背景音乐
- 当你生成岩石的时候,你想让他们跟你的飞船有一定距离,否则岩石在你头上生成的时候你就挂了。.那就不好玩了.一个简单的方法来实现这个效果是这样的:如果生的岩石跟飞船太近,忽视岩石生产事件。
- 试着根据分数变化岩石的速度来将游戏难度提高
- 调整一些参数,以使游戏按我们的想法运行
额外的
- ,在Sprite类的draw功能里面,检查
self.animated
是否是True .如果是的话根据age选择正确的碎片.图片是平铺的.如果self.animated是False
,应该像以前一样画sprite. - 创建一个
explosion_group
,全局变量并复位它成为一个空集合。 - 在group_collide里面,如果有碰撞,创建一个新的爆炸(一个Sprite类的实例)并添加到explosion_group里面,确认每个爆炸都播放爆炸声音。
- 在draw handler里面使用process_sprite_group表达explosion_group.
Grading rubric - 13 pts (scaled to 100 pts)
- 1 pt - 程序产生很多岩石。
- 1 pt - 程序正确的判定飞船是否跟岩石相撞
- 1 pt - 飞船与岩石相撞,该岩石会消失
- 1 pt - 飞船撞上岩石生命丢失一条
- 1 pt - 程序产生多重导弹
- 1 pt - 发射导弹的时候播放导弹发射声音
- 1 pt - 如果导弹没有击中岩石,导弹在固定的时间后会消失
- 1 pt - 程序可以正确的判定导弹和岩石是否碰撞
- 1 pt - 相互碰撞的导弹和岩石会消失
- 1 pt - 导弹岩石相撞分数要更新
- 1 pt - 当生命数变成0,显示启动画面并且所有的岩石要消失
- 1 pt - 当点击启动画面,生命重置为3,分数重置为0,并启动背景音乐。
- 1 pt - 游戏只有在启动画面不存在还有游戏正在进行的时候产生岩石。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 | # implementation of Spaceship - program template for RiceRocks import simplegui import math import random # globals for user interface WIDTH = 800 HEIGHT = 600 score = 0 lives = 3 time = 0 started = False zjcs = 0 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.f2014.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 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 ): global a_missile, 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 ]] a_missile = Sprite(missile_pos, missile_vel, self .angle, 0 , missile_image, missile_info, missile_sound) missile_group.add(a_missile) def get_position( self ): return self .pos def get_radius( self ): return self .radius # 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 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 self .age + = 1 if self .age < self .lifespan: return False else : return True def get_position( self ): return self .pos def get_radius( self ): return self .radius def collide( self , other_object): if dist( self .pos, other_object.get_position()) < = self .radius + other_object.get_radius(): return True else : return False #参考https://class.coursera.org/interactivepython-005/forum/thread?thread_id=5328 def group_collide(group, other_object): global collided collided = False group_copy = list (group) for item in group_copy: if item.collide(other_object) = = True : collided = True group.discard(item) return collided def group_group_collide(group1, group2): global zjcs group1_copy = list (group1) for i in group1_copy: if group_collide(group2, i) = = True : group1.discard(i) zjcs + = 1 return True # 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): timer.start() global started 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 soundtrack.rewind() soundtrack.play() def draw(canvas): global time, started, lives, score, zjcs, rock_group # 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)) # draw UI canvas.draw_text( "Lives" , [ 50 , 50 ], 22 , "White" ) canvas.draw_text( "Score" , [ 680 , 50 ], 22 , "White" ) if group_collide(rock_group, my_ship) = = True : lives - = 1 if lives < 1 : started = False lives = 3 score = 0 zjcs = 0 rock_group = set ([]) timer.stop() #number -= 1 #canvas.draw_text(str(lives), [50, 80], 22, "White") #canvas.draw_text(str(score), [680, 80], 22, "White") if group_group_collide(missile_group, rock_group) = = True : score = zjcs * 10 # draw ship and sprites my_ship.draw(canvas) #a_rock.draw(canvas) process_sprite_group(canvas,rock_group) process_sprite_group(canvas,missile_group) #for n in rock_group: # n.draw(canvas) #a_missile.draw(canvas)注释掉这一行,导弹就不是一直存在了 # update ship and sprites my_ship.update() #a_rock.update()????????????? #a_missile.update() # 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()) 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" ) # timer handler that spawns a rock def rock_spawner(): global a_rock, rock_group, zjcs rock_pos = [random.randrange( 0 , WIDTH), random.randrange( 0 , HEIGHT)] rock_vel = [random.random() * . 6 - . 3 , random.random() * . 6 - . 3 ] if zjcs > 5 : #得分超过5,速度加快 rock_vel = [random.random() * 6 - . 3 , random.random() * 6 - . 3 ] rock_avel = random.random() * . 2 - . 1 a_rock = Sprite(rock_pos, rock_vel, 0 , rock_avel, asteroid_image, asteroid_info) #rock_group.add(a_rock) if len (rock_group) < 13 and dist(rock_pos, my_ship.pos) > 150 : rock_group.add(a_rock) def process_sprite_group(canvas, group): #if a_missile.update == True: #missile_group.remove(a_missile) for m in list (group): #m.update() if m.update() = = True : missile_group.remove(m) m.draw(canvas) # 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) a_rock = Sprite([WIDTH / 3 , HEIGHT / 3 ], [ 1 , 1 ], 0 , . 1 , asteroid_image, asteroid_info) rock_group = set ([]) a_missile = Sprite([ 2 * WIDTH / 3 , 2 * HEIGHT / 3 ], [ - 1 , 1 ], 0 , 0 , missile_image, missile_info, missile_sound) missile_group = 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 timer.stop() frame.start() |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?