前言
在上一篇中,我們初步熟悉了pygame的控制流程,但這對於一個游戲而言是遠遠不夠的。所以在這一篇中,我們的任務是添加一架飛機(玩家),並且能夠控制它進行移動,這樣我們就又離目標進了一步了~ε=ε=(ノ≧∇≦)ノ
正片開始!
1. 把我們的戰斗機搬上屏幕
在正式上代碼以前,有一點需要說明的,我們可以看到,所有屏幕上出現的元素都在資源文件(resources/image/shoot.png)中,那我們要怎么做才能把一只飛機給裁剪出來呢?在pygame中,所有在屏幕上顯示的元素都可以視為一個surface,利用這個特點,我們可以把資源文件導入(load()函數),然后用surface.subsurface()函數把shoot.png中我們想要的元素裁剪出來,這樣問題就解決了~(tips:怎樣才知道shoot.png中飛機的准確坐標呢?在resources/image/shoot.pack文件中已經詳細記錄了每個屏幕元素的左上角坐標以及它的寬和高了)
先上代碼:(在注釋# == new add ==之間的代碼為新加入代碼)
1 import pygame # 導入pygame庫 2 from pygame.locals import * # 導入pygame庫中的一些常量 3 from sys import exit # 導入sys庫中的exit函數 4 5 # 定義窗口的分辨率 6 SCREEN_WIDTH = 480 7 SCREEN_HEIGHT = 640 8 9 # 計數ticks == new add == 10 ticks = 0 11 # 計數ticks == new add == 12 13 # 初始化游戲 14 pygame.init() # 初始化pygame 15 screen = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT]) # 初始化窗口 16 pygame.display.set_caption('This is my first pygame-program') # 設置窗口標題 17 18 # 載入背景圖 19 background = pygame.image.load('resources/image/background.png') 20 21 # 載入資源圖片 == new add == 22 shoot_img = pygame.image.load('resources/image/shoot.png') 23 # 用subsurface剪切讀入的圖片 24 hero1_rect = pygame.Rect(0, 99, 102, 126) 25 hero2_rect = pygame.Rect(165, 360, 102, 126) 26 hero1 = shoot_img.subsurface(hero1_rect) 27 hero2 = shoot_img.subsurface(hero2_rect) 28 hero_pos = [200, 500] 29 # 載入資源圖片 == new add == 30 31 # 事件循環(main loop) 32 while True: 33 34 # 繪制背景 35 screen.blit(background, (0, 0)) 36 37 # 繪制飛機 == new add == 38 if ticks % 50 < 25: 39 screen.blit(hero1, hero_pos) 40 else: 41 screen.blit(hero2, hero_pos) 42 ticks += 1 43 # 繪制飛機 == new add == 44 45 # 更新屏幕 46 pygame.display.update() 47 48 # 處理游戲退出 49 # 從消息隊列中循環取 50 for event in pygame.event.get(): 51 if event.type == pygame.QUIT: 52 pygame.quit() 53 exit()
不妨運行一下:
可以看到,我們的飛機動了,究竟是怎樣實現動畫效果的呢?在代碼中,新加入的代碼一共有三處,顯示玩家飛機的那一段已經解釋過了,現在我們來解釋其余的兩段新加入的代碼。首先增加了一個計數變量ticks,在消息循環中,每循環一次就累加一次,可以理解為每一個周期就是1 tick,我們可以利用周期數來分隔不同的顯示效果;我們讀入了兩張不同的玩家飛機圖片,利用周期數實現每50個周期,前25個周期顯示hero1,后25個周期顯示hero2,這樣就有了緩慢變化的動態效果。不過值得注意的是,這樣每一次循環就計算並判斷一次ticks的做法明顯不是好方法。還有!(╯°口°)╯(┴—┴,Python是木有自增表達式的,我試圖++ticks結果報錯了。
戰斗機已經准備就緒,該教會飛行員怎樣操作了~(`・ω・´)
2. 用鍵盤控制飛機移動
我們知道,每敲一下鍵盤都會產生一個事件,而這個事件能被Python檢測到,既然知道了這個大前提,那控制飛機就很容易了。
come on code~ ( °∀°)ノ
1 import pygame # 導入pygame庫 2 from pygame.locals import * # 導入pygame庫中的一些常量 3 from sys import exit # 導入sys庫中的exit函數 4 5 # 定義窗口的分辨率 6 SCREEN_WIDTH = 480 7 SCREEN_HEIGHT = 640 8 9 ticks = 0 10 # dict == new add == 11 offset = {pygame.K_LEFT:0, pygame.K_RIGHT:0, pygame.K_UP:0, pygame.K_DOWN:0} 12 # dict == new add == 13 14 # 初始化游戲 15 pygame.init() # 初始化pygame 16 screen = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT]) # 初始化窗口 17 pygame.display.set_caption('This is my first pygame-program') # 設置窗口標題 18 19 # 載入背景圖 20 background = pygame.image.load('resources/image/background.png') 21 22 # 載入飛機圖片 23 shoot_img = pygame.image.load('resources/image/shoot.png') 24 # 用subsurface剪切讀入的圖片 25 hero1_rect = pygame.Rect(0, 99, 102, 126) 26 hero2_rect = pygame.Rect(165, 360, 102, 126) 27 hero1 = shoot_img.subsurface(hero1_rect) 28 hero2 = shoot_img.subsurface(hero2_rect) 29 hero_pos = [200, 500] 30 31 # 事件循環(main loop) 32 while True: 33 34 # 繪制背景 35 screen.blit(background, (0, 0)) 36 37 # 繪制飛機 38 if ticks % 50 < 25: 39 screen.blit(hero1, hero_pos) 40 else: 41 screen.blit(hero2, hero_pos) 42 ticks += 1 # python已略去自增運算符 43 44 # 更新屏幕 45 pygame.display.update() 46 47 # 處理游戲退出 48 # 從消息隊列中循環取 49 for event in pygame.event.get(): 50 if event.type == pygame.QUIT: 51 pygame.quit() 52 exit() 53 54 # Python中沒有switch-case 多用字典類型替代 55 # 控制方向 == new add == 56 if event.type == pygame.KEYDOWN: 57 if event.key in offset: 58 offset[event.key] = 3 59 elif event.type == pygame.KEYUP: 60 if event.key in offset: 61 offset[event.key] = 0 62 63 # part 1 64 #offset_x = offset[pygame.K_RIGHT] - offset[pygame.K_LEFT] 65 #offset_y = offset[pygame.K_DOWN] - offset[pygame.K_UP] 66 #hero_pos = [hero_pos[0] + offset_x, hero_pos[1] + offset_y] 67 # part 2 68 offset_x = offset[pygame.K_RIGHT] - offset[pygame.K_LEFT] 69 offset_y = offset[pygame.K_DOWN] - offset[pygame.K_UP] 70 hero_pos = [hero_pos[0] + offset_x, hero_pos[1] + offset_y] 71 # 控制方向 == new add ==
新加入了兩處,一個是字典類型的變量,一個是控制方向的代碼。跟之前控制程序退出的代碼一樣,依然是從事件隊列中取事件;當event.type為按鍵事件時,再判斷event.key是否屬於上下左右四個鍵位中的其中一個;最后在其相應方向上給一個偏移量,即完成判斷的過程;松鍵的話該方向上的偏移量賦值為0,這樣該鍵方向上就沒有位移了;最后將偏移量加到飛機的pos上,下一輪刷新時自然就到移動到新地方了。(這里插播一個事,我以前寫過c++和java,所以寫到判斷鍵盤鍵位時,很自然就想用switch-case語句,沒想到竟然出錯了(╯°口°)╯(┴—┴,后來才發現原來Python是沒有switch-case語句的,switch-case語句多用字典數據結構代替,感覺這樣的寫法就更靈活了)
上面講得都比較簡單,現在我們要思考一個問題,上面的代碼中,line 63-66和line 67-70的代碼是一樣的,但兩部分代碼的效果卻是很不一樣。part1代碼使得我們要按一次鍵飛機才會動一次,而在part2代碼中我們可以長按方向鍵來控制飛機,不禁感慨一下Python的縮進。part1是在for循環的影響下的,也就是說,事件隊列中有事件才會執行part1的代碼,假設我們長按方向左鍵(注意,擊鍵一次只會產生一個pygame.KEYDOWN事件,所以長按也只觸發一次),飛機也只會往左移3個像素點;而part2代碼由於不在for循環內,所以每一tick就向左移3個像素點直到松開方向左鍵。
演示一下效果:
好的,控制飛機已經不成問題了,不過大家發現飛機有一個小問題了嗎?它會穿到窗口的“外面”去!不過我們只要添加一個小小的限制就可以解決問題了,只要把line68-70換成以下邊界判斷代碼就可以了。
1 hero_x = hero_pos[0] + offset[pygame.K_RIGHT] - offset[pygame.K_LEFT] 2 hero_y = hero_pos[1] + offset[pygame.K_DOWN] - offset[pygame.K_UP] 3 if hero_x < 0: 4 hero_pos[0] = 0 5 elif hero_x > SCREEN_WIDTH - hero1_rect.width: 6 hero_pos[0] = SCREEN_WIDTH - hero1_rect.width 7 else: 8 hero_pos[0] = hero_x 9 10 if hero_y < 0: 11 hero_pos[1] = 0 12 elif hero_y > SCREEN_HEIGHT - hero1_rect.height: 13 hero_pos[1] = SCREEN_HEIGHT - hero1_rect.height 14 else: 15 hero_pos[1] = hero_y
今天功能就完成到這里吧~(`・ω・´)