本系列博客介紹以python+pygame庫進行小遊戲的開發。有寫的不對之處還望各位海涵。html
前幾期博客咱們一塊兒學習了,pygame中的衝突檢測技術以及一些經常使用的數據結構。python
此次咱們來一塊兒作一個簡單的酷跑類遊戲綜合運用之前學到的知識。git
程序下載地址:https://pan.baidu.com/s/1Ji2Ubsds6z2brBx8Gz1OOw 提取碼:dff4 github
源代碼網盤地址:https://pan.baidu.com/s/1T7tlYbTNUPRhtJ45B6PAPw 提取碼:mhip 數據結構
github地址:https://github.com/XINCGer/catRunFastdom
效果圖:ide
如今咱們來分析一下製做流程:函數
遊戲中一共有嗷大喵,惡龍,火焰,爆炸動畫和果實(就是上方藍色的矩形塊)這幾種精靈。這裏咱們使用到了前幾期博客中的MyLibrary.py。上述這幾個精靈都是 MySprite類實例化的對象。學習
爲了方便管理。咱們創建了幾個精靈組,而且將一些精靈塞到了裏面:動畫
#建立精靈組 group = pygame.sprite.Group() group_exp = pygame.sprite.Group() group_fruit = pygame.sprite.Group() #建立怪物精靈 dragon = MySprite() dragon.load("dragon.png", 260, 150, 3) dragon.position = 100, 230 group.add(dragon) #建立爆炸動畫 explosion = MySprite() explosion.load("explosion.png",128,128,6) #建立玩家精靈 player = MySprite() player.load("sprite.png", 100, 100, 4) player.position = 400, 270 group.add(player) #建立子彈精靈 arrow = MySprite() arrow.load("flame.png", 40, 16, 1) arrow.position = 800,320 group.add(arrow)
在程序開始的時候咱們能夠看到有一個歡迎界面,爲了簡單我這裏是直接在ps裏面作好了圖片,而後加載到程序中的:
interface = pygame.image.load("interface.png")
界面上面還有一個按鈕,當鼠標通過的時候,會變成灰底的,所以咱們設計一個button類:
簡單來講就是預先加載一張正常狀態下在的button圖片和一個按下狀態的button圖片,而後判斷鼠標的pos是否和button的位置有重合,若是有則顯示button被按下時的圖片。
關於button的設計我參考了這位博友的教程:http://www.cnblogs.com/SRL-Southern/p/4949624.html,他的教程寫的很是不錯。
#定義一個按鈕類 class Button(object): def __init__(self, upimage, downimage,position): self.imageUp = pygame.image.load(upimage).convert_alpha() self.imageDown = pygame.image.load(downimage).convert_alpha() self.position = position self.game_start = False def isOver(self): point_x,point_y = pygame.mouse.get_pos() x, y = self. position w, h = self.imageUp.get_size() in_x = x - w/2 < point_x < x + w/2 in_y = y - h/2 < point_y < y + h/2 return in_x and in_y def render(self): w, h = self.imageUp.get_size() x, y = self.position if self.isOver(): screen.blit(self.imageDown, (x-w/2,y-h/2)) else: screen.blit(self.imageUp, (x-w/2, y-h/2)) def is_start(self): if self.isOver(): b1,b2,b3 = pygame.mouse.get_pressed() if b1 == 1: self.game_start = True bg_sound.play_pause() btn_sound.play_sound() bg_sound.play_sound()
能夠看到這個button類裏面我還添加了一個isStart的方法,他是用來判斷是否開始遊戲的。當鼠標的位置與button重合,且按下鼠標左鍵的時候,遊戲就開始。
(將game_start變量置爲True)而後經過btn_sound.play_sound(),bg_sound.play_sound() 這兩句來播放按鈕被按下的聲音和遊戲的背景音樂。
關於pygame中聲音的操做,我稍後介紹一下。
能夠看到程序中還有一個不停滾動的地圖,讓咱們來實現這個滾動地圖類:
#定義一個滾動地圖類 class MyMap(pygame.sprite.Sprite): def __init__(self,x,y): self.x = x self.y = y self.bg = pygame.image.load("background.png").convert_alpha() def map_rolling(self): if self.x < -600: self.x = 600 else: self.x -=5 def map_update(self): screen.blit(self.bg, (self.x,self.y)) def set_pos(x,y): self.x =x self.y =y
建立兩個地圖對象:
#建立地圖對象 bg1 = MyMap(0,0) bg2 = MyMap(600,0)
在程序中直接調用update和rolling方法就可讓地圖無限的滾動起來了。
bg1.map_update()
bg2.map_update()
bg1.map_rolling()
bg2.map_rolling()
你看明白這個無限滾動地圖是如何工做的了嗎。首先渲染兩張地圖背景,一張展現在屏幕上面,一張在屏幕以外預備着(咱們暫時看不到),以下圖所示:
而後兩張地圖一塊兒以相同的速度向左移動:
當地圖1徹底離開屏幕範圍的時候,再次將它的座標置爲600,0(這樣就又回到了狀態1):
這樣經過兩張圖片的不斷顛倒位置,而後平移,在咱們的視覺中就造成了一張不斷滾動的地圖了。
下面介紹一下如何在pygame中加載而且使用聲音:
1.初始化音頻模塊:
咱們要使用的音頻系統包含在了pygame的pygame.mixer模塊裏面。所以在使用音頻以前要初始化這個模塊:
pygame.mixer.init()
這個初始化模塊語句在程序中執行一次就好。
2.加載音頻文件:
使用的是pygame.mixer.Sound類來加載和管理音頻文件,pygame支持兩種音頻文件:未壓縮的WAV和OGG音頻文件,若是要播放長時間的音樂,我推薦你使用OGG格式音頻文件,由於它的體積比較小,適合長時間的加載和播放。當你要播放比較短的音頻的時候能夠選擇WAV。
hit_au = pygame.mixer.Sound("exlposion.wav")
3.播放音樂:
上面的pygame.mixer.Sound函數返回了一個sound對象,咱們可使用play和stop方法來播放和中止播放音樂。
可是這裏咱們介紹一種更爲高級的用法,使用pygame.mixer.Channel,這個類提供了比sound對象更爲豐富的功能。
首先咱們先申請一個可用的音頻頻道:
channel = pygame.mixer.find_channel(True)
一旦有了頻道以後咱們就可使用Channel.play()方法來播放一個sound對象了。
channel.play(sound)
好了如今讓咱們來實現一下和音頻有關的模塊:
首先定義一個初始化的函數,它初始化了音頻模塊,而且加載了一些音頻文件以方便咱們在程序中使用:
def audio_init(): global hit_au,btn_au,bg_au,bullent_au pygame.mixer.init() hit_au = pygame.mixer.Sound("exlposion.wav") btn_au = pygame.mixer.Sound("button.wav") bg_au = pygame.mixer.Sound("background.ogg") bullent_au = pygame.mixer.Sound("bullet.wav")
而後咱們實現了一個Music類,這個類能夠控制聲音的播放和暫停(set_volume函數是用來設置音樂聲音大小的):
class Music(): def __init__(self,sound): self.channel = None self.sound = sound def play_sound(self): self.channel = pygame.mixer.find_channel(True) self.channel.set_volume(0.5) self.channel.play(self.sound) def play_pause(self): self.channel.set_volume(0.0) self.channel.play(self.sound)
跳躍函數:
當按下空格鍵的時候,嗷大喵會跳起,這個是如何實現的呢?
for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() keys = pygame.key.get_pressed() if keys[K_ESCAPE]: pygame.quit() sys.exit() elif keys[K_SPACE]: if not player_jumping: player_jumping = True jump_vel = -12.0
當按下空格鍵的時候,會將player_jumping變量置爲True 而且給jump_vel一個初速度-12.0
而後在每次循環的時候,將jump_vel 加0.6,當嗷大喵回到起跳位置的時候,將速度置爲0,令人物再也不在y方向上有移動。
#檢測玩家是否處於跳躍狀態 if player_jumping: if jump_vel <0: jump_vel += 0.6 elif jump_vel >= 0: jump_vel += 0.8 player.Y += jump_vel if player.Y > player_start_y: player_jumping = False player.Y = player_start_y jump_vel = 0.0
而後咱們還須要一個不斷髮出的子彈:
#更新子彈 if not game_over: arrow.X -= arrow_vel if arrow.X < -40: reset_arrow()
#重置火箭函數 def reset_arrow(): y = random.randint(270,350) arrow.position = 800,y bullent_sound.play_sound()
關於嗷大喵和子彈衝突檢測咱們使用了以前學過的矩形衝突檢測技術,當玩家和子彈產生衝突的時候,重置子彈,播放爆炸動畫,而後將人物的x座標值向左移動10,以表示人物受到傷害。惡龍和子彈的衝突和這個是同樣的,這裏就再也不贅述了。
#碰撞檢測,子彈是否擊中玩家 if pygame.sprite.collide_rect(arrow, player): reset_arrow() explosion.position =player.X,player.Y player_hit = True hit_sound.play_sound() if p_first: group_exp.add(explosion) p_first = False player.X -= 10
而後咱們還須要考慮一下玩家被惡龍追上的時候的情形,仍是應用矩形檢測技術:
if pygame.sprite.collide_rect(player, dragon): game_over = True
爲了使果實移動,咱們須要遍歷group_fruit裏面的果實,而後依次將他們左移5個單位,而後咱們還須要判斷玩家吃到果實的場景,果實會消失,而後玩家的積分增長。
這裏使用了以前學過的pygame.sprite.spritecollide(sprite,sprite_group,bool)。
調用這個函數的時候,一個組中的全部精靈都會逐個地對另一個單個精靈進行衝突檢測,發生衝突的精靈會做爲一個列表返回。
這個函數的第一個參數就是單個精靈,第二個參數是精靈組,第三個參數是一個bool值,最後這個參數起了很大的做用。當爲True的時候,會刪除組中全部衝突的精靈,False的時候不會刪除衝突的精靈。所以咱們這裏將第三個參數設置爲True,這樣就會刪除掉和精靈衝突的對象了,看起來就好像是玩家吃掉了這些果實同樣。
#遍歷果實,使果實移動 for e in group_fruit: e.X -=5 collide_list = pygame.sprite.spritecollide(player,group_fruit,False) score +=len(collide_list)
最後仍是看一下所有的代碼:
1 # -*- coding: utf-8 -*- 2 import sys, time, random, math, pygame,locale 3 from pygame.locals import * 4 from MyLibrary import * 5 6 #重置火箭函數 7 def reset_arrow(): 8 y = random.randint(270,350) 9 arrow.position = 800,y 10 bullent_sound.play_sound() 11 12 #定義一個滾動地圖類 13 class MyMap(pygame.sprite.Sprite): 14 15 def __init__(self,x,y): 16 self.x = x 17 self.y = y 18 self.bg = pygame.image.load("background.png").convert_alpha() 19 def map_rolling(self): 20 if self.x < -600: 21 self.x = 600 22 else: 23 self.x -=5 24 def map_update(self): 25 screen.blit(self.bg, (self.x,self.y)) 26 def set_pos(x,y): 27 self.x =x 28 self.y =y 29 #定義一個按鈕類 30 class Button(object): 31 def __init__(self, upimage, downimage,position): 32 self.imageUp = pygame.image.load(upimage).convert_alpha() 33 self.imageDown = pygame.image.load(downimage).convert_alpha() 34 self.position = position 35 self.game_start = False 36 37 def isOver(self): 38 point_x,point_y = pygame.mouse.get_pos() 39 x, y = self. position 40 w, h = self.imageUp.get_size() 41 42 in_x = x - w/2 < point_x < x + w/2 43 in_y = y - h/2 < point_y < y + h/2 44 return in_x and in_y 45 46 def render(self): 47 w, h = self.imageUp.get_size() 48 x, y = self.position 49 50 if self.isOver(): 51 screen.blit(self.imageDown, (x-w/2,y-h/2)) 52 else: 53 screen.blit(self.imageUp, (x-w/2, y-h/2)) 54 def is_start(self): 55 if self.isOver(): 56 b1,b2,b3 = pygame.mouse.get_pressed() 57 if b1 == 1: 58 self.game_start = True 59 bg_sound.play_pause() 60 btn_sound.play_sound() 61 bg_sound.play_sound() 62 63 def replay_music(): 64 bg_sound.play_pause() 65 bg_sound.play_sound() 66 67 #定義一個數據IO的方法 68 def data_read(): 69 fd_1 = open("data.txt","r") 70 best_score = fd_1.read() 71 fd_1.close() 72 return best_score 73 74 75 #定義一個控制聲音的類和初始音頻的方法 76 def audio_init(): 77 global hit_au,btn_au,bg_au,bullent_au 78 pygame.mixer.init() 79 hit_au = pygame.mixer.Sound("exlposion.wav") 80 btn_au = pygame.mixer.Sound("button.wav") 81 bg_au = pygame.mixer.Sound("background.ogg") 82 bullent_au = pygame.mixer.Sound("bullet.wav") 83 class Music(): 84 def __init__(self,sound): 85 self.channel = None 86 self.sound = sound 87 def play_sound(self): 88 self.channel = pygame.mixer.find_channel(True) 89 self.channel.set_volume(0.5) 90 self.channel.play(self.sound) 91 def play_pause(self): 92 self.channel.set_volume(0.0) 93 self.channel.play(self.sound) 94 95 #主程序部分 96 pygame.init() 97 audio_init() 98 screen = pygame.display.set_mode((800,600),0,32) 99 pygame.display.set_caption("嗷大喵快跑!") 100 font = pygame.font.Font(None, 22) 101 font1 = pygame.font.Font(None, 40) 102 framerate = pygame.time.Clock() 103 upImageFilename = 'game_start_up.png' 104 downImageFilename = 'game_start_down.png' 105 #建立按鈕對象 106 button = Button(upImageFilename,downImageFilename, (400,500)) 107 interface = pygame.image.load("interface.png") 108 109 #建立地圖對象 110 bg1 = MyMap(0,0) 111 bg2 = MyMap(600,0) 112 #建立一個精靈組 113 group = pygame.sprite.Group() 114 group_exp = pygame.sprite.Group() 115 group_fruit = pygame.sprite.Group() 116 #建立怪物精靈 117 dragon = MySprite() 118 dragon.load("dragon.png", 260, 150, 3) 119 dragon.position = 100, 230 120 group.add(dragon) 121 122 #建立爆炸動畫 123 explosion = MySprite() 124 explosion.load("explosion.png",128,128,6) 125 #建立玩家精靈 126 player = MySprite() 127 player.load("sprite.png", 100, 100, 4) 128 player.position = 400, 270 129 group.add(player) 130 131 #建立子彈精靈 132 arrow = MySprite() 133 arrow.load("flame.png", 40, 16, 1) 134 arrow.position = 800,320 135 group.add(arrow) 136 137 138 139 #定義一些變量 140 arrow_vel = 10.0 141 game_over = False 142 you_win = False 143 player_jumping = False 144 jump_vel = 0.0 145 player_start_y = player.Y 146 player_hit = False 147 monster_hit = False 148 p_first = True 149 m_first = True 150 best_score = 0 151 global bg_sound,hit_sound,btn_sound,bullent_sound 152 bg_sound=Music(bg_au) 153 hit_sound=Music(hit_au) 154 btn_sound=Music(btn_au) 155 bullent_sound =Music(bullent_au) 156 game_round = {1:'ROUND ONE',2:'ROUND TWO',3:'ROUND THREE',4:'ROUND FOUR',5:'ROUND FIVE'} 157 game_pause = True 158 index =0 159 current_time = 0 160 start_time = 0 161 music_time = 0 162 score =0 163 replay_flag = True 164 #循環 165 bg_sound.play_sound() 166 best_score = data_read() 167 while True: 168 framerate.tick(60) 169 ticks = pygame.time.get_ticks() 170 for event in pygame.event.get(): 171 if event.type == pygame.QUIT: 172 pygame.quit() 173 sys.exit() 174 keys = pygame.key.get_pressed() 175 if keys[K_ESCAPE]: 176 pygame.quit() 177 sys.exit() 178 179 elif keys[K_SPACE]: 180 if not player_jumping: 181 player_jumping = True 182 jump_vel = -12.0 183 184 screen.blit(interface,(0,0)) 185 button.render() 186 button.is_start() 187 if button.game_start == True: 188 if game_pause : 189 index +=1 190 tmp_x =0 191 if score >int (best_score): 192 best_score = score 193 fd_2 = open("data.txt","w+") 194 fd_2.write(str(best_score)) 195 fd_2.close() 196 #判斷遊戲是否通關 197 if index == 6: 198 you_win = True 199 if you_win: 200 start_time = time.clock() 201 current_time =time.clock()-start_time 202 while current_time<5: 203 screen.fill((200, 200, 200)) 204 print_text(font1, 270, 150,"YOU WIN THE GAME!",(240,20,20)) 205 current_time =time.clock()-start_time 206 print_text(font1, 320, 250, "Best Score:",(120,224,22)) 207 print_text(font1, 370, 290, str(best_score),(255,0,0)) 208 print_text(font1, 270, 330, "This Game Score:",(120,224,22)) 209 print_text(font1, 385, 380, str(score),(255,0,0)) 210 pygame.display.update() 211 pygame.quit() 212 sys.exit() 213 214 for i in range(0,100): 215 element = MySprite() 216 element.load("fruit.bmp", 75, 20, 1) 217 tmp_x +=random.randint(50,120) 218 element.X = tmp_x+300 219 element.Y = random.randint(80,200) 220 group_fruit.add(element) 221 start_time = time.clock() 222 current_time =time.clock()-start_time 223 while current_time<3: 224 screen.fill((200, 200, 200)) 225 print_text(font1, 320, 250,game_round[index],(240,20,20)) 226 pygame.display.update() 227 game_pause = False 228 current_time =time.clock()-start_time 229 230 else: 231 #更新子彈 232 if not game_over: 233 arrow.X -= arrow_vel 234 if arrow.X < -40: reset_arrow() 235 #碰撞檢測,子彈是否擊中玩家 236 if pygame.sprite.collide_rect(arrow, player): 237 reset_arrow() 238 explosion.position =player.X,player.Y 239 player_hit = True 240 hit_sound.play_sound() 241 if p_first: 242 group_exp.add(explosion) 243 p_first = False 244 player.X -= 10 245 246 #碰撞檢測,子彈是否擊中怪物 247 if pygame.sprite.collide_rect(arrow, dragon): 248 reset_arrow() 249 explosion.position =dragon.X+50,dragon.Y+50 250 monster_hit = True 251 hit_sound.play_sound() 252 if m_first: 253 group_exp.add(explosion) 254 m_first = False 255 dragon.X -= 10 256 257 #碰撞檢測,玩家是否被怪物追上 258 if pygame.sprite.collide_rect(player, dragon): 259 game_over = True 260 #遍歷果實,使果實移動 261 for e in group_fruit: 262 e.X -=5 263 collide_list = pygame.sprite.spritecollide(player,group_fruit,False) 264 score +=len(collide_list) 265 #是否經過關卡 266 if dragon.X < -100: 267 game_pause = True 268 reset_arrow() 269 player.X = 400 270 dragon.X = 100 271 272 273 274 #檢測玩家是否處於跳躍狀態 275 if player_jumping: 276 if jump_vel <0: 277 jump_vel += 0.6 278 elif jump_vel >= 0: 279 jump_vel += 0.8 280 player.Y += jump_vel 281 if player.Y > player_start_y: 282 player_jumping = False 283 player.Y = player_start_y 284 jump_vel = 0.0 285 286 287 #繪製背景 288 bg1.map_update() 289 bg2.map_update() 290 bg1.map_rolling() 291 bg2.map_rolling() 292 293 #更新精靈組 294 if not game_over: 295 group.update(ticks, 60) 296 group_exp.update(ticks,60) 297 group_fruit.update(ticks,60) 298 #循環播放背景音樂 299 music_time = time.clock() 300 if music_time > 150 and replay_flag: 301 replay_music() 302 replay_flag =False 303 #繪製精靈組 304 group.draw(screen) 305 group_fruit.draw(screen) 306 if player_hit or monster_hit: 307 group_exp.draw(screen) 308 print_text(font, 330, 560, "press SPACE to jump up!") 309 print_text(font, 200, 20, "You have get Score:",(219,224,22)) 310 print_text(font1, 380, 10, str(score),(255,0,0)) 311 if game_over: 312 start_time = time.clock() 313 current_time =time.clock()-start_time 314 while current_time<5: 315 screen.fill((200, 200, 200)) 316 print_text(font1, 300, 150,"GAME OVER!",(240,20,20)) 317 current_time =time.clock()-start_time 318 print_text(font1, 320, 250, "Best Score:",(120,224,22)) 319 if score >int (best_score): 320 best_score = score 321 print_text(font1, 370, 290, str(best_score),(255,0,0)) 322 print_text(font1, 270, 330, "This Game Score:",(120,224,22)) 323 print_text(font1, 370, 380, str(score),(255,0,0)) 324 pygame.display.update() 325 fd_2 = open("data.txt","w+") 326 fd_2.write(str(best_score)) 327 fd_2.close() 328 pygame.quit() 329 sys.exit() 330 pygame.display.update()