正片開始!
- 把我們的戰鬥機搬上螢幕
在正式上程式碼以前,有一點需要說明的,我們可以看到,所有螢幕上出現的元素都在資原始檔(resources/image/shoot.png)中,那我們要怎麼做才能把一隻飛機給裁剪出來呢?在pygame中,所有在螢幕上顯示的元素都可以視為一個surface,利用這個特點,我們可以把資原始檔匯入(load()函式),然後用surface.subsurface()函式把shoot.png中我們想要的元素裁剪出來,這樣問題就解決了~(tips:怎樣才知45454道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結果報錯了。
戰鬥機已經準備就緒,該教會飛行員怎樣操作了~(`・ω・´)
- 用鍵盤控制飛機移動
我們知道,每敲一下鍵盤都會產生一個事件,而這個事件能被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
複製程式碼
今天功能就完成到這裡吧~(`・ω・´)
本作品採用《CC 協議》,轉載必須註明作者和本文連結