1. 程式人生 > >Python 0基礎開發遊戲:打地鼠(詳細教程)VS code版本

Python 0基礎開發遊戲:打地鼠(詳細教程)VS code版本

如果你沒有任何程式設計經驗,而且想嘗試一下學習程式設計開發,這個系列教程一定適合你,它將帶你學習最基本的Python語法,並讓你掌握小遊戲的開發技巧。你所需要的,就是付出一些時間和耐心來嘗試這些程式碼和操作。
@[top]

一、準備工作

1 下載安裝 python
2 下載安裝VS code編輯器
安裝時,要注意勾選 新增到path

3 安裝pygame模組

  • 在VisualStudioCode的頂部選單【Terminal-New Teminal】開啟命令列終端,然後輸入命令python -m pip install --upgrade pip,回車,等待完成。
  • 然後同樣輸入命令pip install pygame,等待完成安裝,可能需要幾分鐘

    二、建立專案

  1. 在桌面上建立一個資料夾mygame,然後在VSCode中使用選單【File-Open Folder】,選擇mygame資料夾,VSCode左側將會出現EXPLORER導航欄。

  2. 在左側導航欄中,【右鍵-New File】建立檔案main.py

- 將下面的程式碼貼上到右側`main.py`檔案中。
import pygame
import sys
pygame.display.set_mode([600,400])
while 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

在這裡我還是要說一下,文中素材以及可執行程式碼可以加群:456926667,獲取,這個是我建立的一個針對0基礎的夥伴一個交流群,下個文章我會更新關於pycharm版本的打地鼠。

  1. 執行程式碼。
*   仍然【Terminal-New terminal】終端中輸入命令`python main.py`,這將執行我們上面的程式碼,看到彈出一個黑色視窗。

三、可選操作

  1. 執行上面的操作的時候,VSCode的右下角會經常彈出一些提示,如果有【Install】字樣,可以放心的點選它進行安裝更多內容。

  2. 也可以從左側欄點選圖示開啟【EXTENSIONS】,然後搜尋@id:ms-python.python

    ,點選找到的結果,右側再點選【Install】按鈕進行安裝。安裝之後main.py檔案的右上角就會出現三角形執行按鈕,點選它同樣可以執行程式碼,相當於終端中輸入python main.py

  1. pip install ...安裝命令太慢。Windows使用者,可以從上面的網盤中下載pip.ini檔案,然後在【C盤-使用者-使用者名稱】資料夾下面建立pip資料夾,再把下載的pip.ini檔案拷貝進去,此後再執行pip install ...安裝速度就會快很多。

  2. 對於蘋果使用者就麻煩很多。先在終端執行cd ~切換到使用者資料夾,然後執行mkdir .pip建立.pip資料夾,它是隱身的,我們開啟訪達,從選單執行【前往-前往資料夾...】,前往~/.test目錄,把下載的pip.conf檔案貼上進去,搞定。

pip.ini或者pip.conf檔案是把原來pip預設從國外下載安裝改成了從國內下載,所以速度會變快很多。

  • 其中import是匯入我們要使用的外部程式碼模組,pygame當然是必須的,syssystem系統的簡寫,因為我們的遊戲要執行在系統(windows或者蘋果macOS)上面,所以我們會用到系統的一些命令,比如下面的sys.exit()這個命令。

  • pyagme.display.set_mode([600,400]),這裡的[600,400]是一對數字組合在一起的,叫二元陣列,這裡它表示寬600,高400的一個矩形。整句話就是設定要彈出的視窗的大小,display顯示,set設定,mode模式

  • while 1:...當是1的時候,就...,1在程式碼裡面表示正確的、真的、存在的,相反,0表示錯誤、假的、不存在的。while 1:do something那麼something就會做,如果while 0: do something那麼就不會做了。

  • for ... sys.exit()這一段暫時可以不深究,只是固定格式。只要知道它表示遊戲程式執行結束的時候系統把視窗也關掉,清理好計算機不要留痕跡,exit退出

遊戲開發的思路

遊戲開發都有固定的套路,無論是打地鼠、憤怒的小鳥,還是西瓜忍者,甚至是王者榮耀這樣的大型遊戲,他們大致都遵循下面幾個思路:

  1. 建立一個地圖場景,上面可能有些道具。

    比如幾個地鼠洞,一些可以放小豬的木盒子,甚至非常複雜的山谷地形,上面還有很多野怪。
    這些地圖上的元素一般都是被動的,就是你不去靠近或招惹野怪的話,它們不會互相打起來自相殘殺,同樣,小鳥還沒發射的時候,木盒子也不會自己倒塌。

  2. 建立至少一個玩家可以控制的元素,它可以和地圖場景發生互動。

    這個可以被控制的元素我們稱為玩家角色。在打地鼠遊戲中這個角色就是一個錘子,憤怒的小鳥中這個角色其實是彈弓,彈出的小鳥其實是個道具,在王者榮耀遊戲中玩家的角色就是自己的英雄。

  3. 必須要有評判標準,用來衡量輸贏勝敗。

    玩家控制的角色和地圖場景進行互動,發生反應,對應的也必須要有一個評判標準,比如計算3分鐘內擊中地鼠的次數,或者計算砸死的綠豬的數量,或者是打野怪獲得的經驗,這些規則一定要清晰而且不能互相矛盾。
    大多數遊戲都有輸贏勝敗,而勝敗往往本質上只是誰的積分首先達到某個臨界點。可以是某個關鍵道具的變化,比如對戰遊戲中塔被摧毀,也可以是玩家角色的屬性變化,比如格鬥遊戲中被擊殺;也可以只是純粹的某項積分評比,用排行榜代替輸贏。

遊戲開發的技術點

  1. 要能夠在視窗內繪製圖形。

    可以是直接用程式碼繪製幾何圖形,也可以是載入圖片顯示內容。

  2. 要能用程式碼控制每個元素(道具和角色)的動畫。

    動畫就是一組圖片不停地輪番變化。要能用程式碼控制播放和停止每個元素的動畫,還能在不同動畫之間快速切換。

  3. 能夠接收使用者的控制,並藉此影響遊戲中的元素。

    知道使用者什麼時候按了鍵盤,什麼時候點了滑鼠,按了哪個按鍵,滑鼠左鍵還是右鍵?我們經常把這些操作稱之為互動事件。

  4. 能夠對遊戲中各種元素產生的有效資料進行計算和管理。

    玩家角色一刀砍下去,怪物的血量減少了100點,這個就是資料,而且是很有用的資料,沒有這個資料的話怪物可能永遠砍不死了。
    有時候這些資料要儲存好,讓使用者下一次開啟遊戲的時候仍然看到自己的等級和裝備都還存在。有些時候這些資料要及時清理,比如新的一局又開始了,地圖上的道具和角色都要恢復原樣。

打地鼠遊戲

我們可以把經典的打地鼠遊戲簡化概括為:

  • 地圖和道具:隨機位置出現地鼠圖形
  • 互動角色:控制錘子圖形,點選地鼠圖形使其消失
  • 積分輸贏:限定時間內擊中地鼠圖形的次數

核心玩法簡化成一句話就是:點選隨機出現圖形。

繪製地鼠

我們用一個藍色的圓形代表地鼠。那怎麼在視窗中繪製一個圓形呢?

可以百度【pygame 畫圓圈】類似的關鍵字,可以查到要使用pygame.draw.circle語句,它的具體語法可以從官方說明文件中找到,英文版詳細說明點這裡。

我們查到它的語法是:

pygame.draw.circle()
circle(surface, color, center, radius) -> Rect

這表示draw.circle()需要四個引數,分別是surface表面,color顏色,center中心點,radius半徑

我們繼續看surface引數的說明:

surface (Surface) -- surface to draw on

聽上去像是畫布,——先要有個畫布才能在上面畫圓。
點選Surface連結,找到更進一步說明:

Surface((width, height), flags=0, depth=0, masks=None) -> Surface

結尾的->Surface表示Surface((width....)這句話可以生成一個Surface表面,我們可以用下面的語句捕捉到這個生成的表面:

sur=pygame.Surface((600, 400)

這樣,sur就是我們生成的表面了。

顏色和位置

再返回來看color引數:

color (Color or int or tuple(int, int, int, [int])) 
-- color to draw with, 
the alpha value is optional if using a tuple (RGB[A])

很明顯它是表示畫什麼顏色的圓。tuple(int, int, int, [int])表示這裡需要三個整數int一起表示顏色,RGB是指red紅,green綠,blue藍,alpha透明度

clr=(0,0,255) #藍色

對於center中心位置我們也可以用同樣的方法得到,這裡的Vector2表示二元向量,及橫向x和豎向y的位置:

pos=pygame.vector2(300,200) #視窗中央

繪製圓形

引數都具備了,那麼就可以開始畫圓了。執行下面的程式碼:

import pygame
import sys

pygame.init()  # 初始化
window = pygame.display.set_mode([600, 400])  # 設定視窗

sur = pygame.Surface([600, 400])  # 繪製背景容器
clr = (0, 0, 255)
pos = (300,200)
rad = 100

while 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

    # 每幀迴圈執行的程式碼
    pygame.draw.circle(sur, clr, pos, 100# 繪製圓

    # 重新整理畫面
    window.blit(sur, (0, 0))
    pygame.display.flip()

注意這裡最底部重新整理畫面的兩行,其中window.blit(sur, (0, 0))表示把我們繪製好的表面sur重新整理到window視窗中;pygame.display.flip()表示進行視窗重新整理。

隨機出現

隨機出現就是隨機位置,我們必須確保每一次花圓的pos位置都不同,而且應該是固定的幾個地鼠洞位置。——別忘了我們要做打地鼠遊戲。

假設有6個地鼠位置pos分別是[200,200],[300,200],[400,200],[200,300],[300,300],[400,300],那麼如何隨機取到6箇中一個呢?也就是如何隨機取到1~6其中的一個數字即可。

我們可以百度【python 隨機數】查到需要使用random模組,這是python自帶的模組,不需要再重新pip install
如果搜尋【python random document】可以查詢到官方的語法說明,如下:

random.randint(a, b)
Return a random integer N such that a <= N <= b. 
Alias for randrange(a, b+1).

這是說可以隨機生成ab之間的一個數字。也可以從中文的菜鳥教程網
學習到這個知識。

新建一個test.py檔案,我們進行測試:

import random
a = random.randint(0, 5)
print(a)

每次執行都能生成不同的數字。

繼續測試:

import random
a = random.randint(0, 6)
pos6=[[200,200],[300,200],[400,200],[200,300],[300,300],[400,300]]
print(pos6[a])

這裡的pos6[a]表示pos6的六個位置中的第a個。執行這個程式碼就會每次生成不同的位置。

測試成功之後我們把它拷貝到剛才的畫圓程式碼中,得到:

import pygame
import sys
import random

pygame.init()  # 初始化
window = pygame.display.set_mode([600, 400])  # 設定視窗

sur = pygame.Surface([600, 400])  # 繪製背景容器
clr = (0, 0, 255)
pos6 = [[200, 200], [300, 200], [400, 200], [
    200, 300], [300, 300], [400, 300]]  # !!六個位置
rad = 100

while 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

    # 每幀迴圈執行的程式碼
    sur.fill((0, 0, 0))  # !!用黑色覆蓋前一幀的畫面,實現重新整理
    a = random.randint(0, 5)  # !!隨機0到5
    pygame.draw.circle(sur, clr, pos6[a], 100)  # !!使用隨機位置

    # 重新整理畫面
    window.blit(sur, (0, 0))
    pygame.display.flip()

注意新增了sur.fill...一行,這是用黑色(0,0,0)來清理掉上一幀的內容,避免出現多個圓。

隔n幀重新整理

上面的程式碼執行之後會看到藍色的圓四處亂跳,太快了,我們希望改變位置之後能停一下,等我們錘它。

我們需要畫面的圓每隔n幀再隨機變換一次,而不是現在的每幀都隨機變。思路是這樣的:我們設定一個計數器,開始是0,每幀都給它增加1,就是0,1,2,3,4...直到它增到到超過50,這時候我們就改變圓的位置並同時把計數器重置為0。

程式碼如下:

import pygame
import sys
import random

pygame.init()  # 初始化
window = pygame.display.set_mode([600, 400])  # 設定視窗

sur = pygame.Surface([600, 400])  # 繪製背景容器
clr = (0, 0, 255)
pos6 = [[200, 200], [300, 200], [400, 200], [
    200, 300], [300, 300], [400, 300]]  # 六個位置
rad = 100
tick=0 #!!計數器

while 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

    # 每幀迴圈執行的程式碼
    if tick>50: #每50次重新整理變換一次
        sur.fill((0, 0, 0))  # 用黑色覆蓋前一幀的畫面,實現重新整理
        a = random.randint(0, 5)  # 隨機0到5
        pygame.draw.circle(sur, clr, pos6[a], 100)  # 使用隨機位置
        tick=0
    else: #!!不重新整理變換的時候
        tick=tick+1 #!!增加計數器

    # 重新整理畫面
    window.blit(sur, (0, 0))
    pygame.display.flip()

增加互動點選

當用戶點選畫面的時候,我們要知道它點選了哪裡,是否點選到了我們畫的圓上面。

百度搜索【pygame 點選】可以找到相關資源,也可以直接在官方說明文件中找到。

思路是我們新增對event.type事件型別的實時監測,一旦發現點選事件就獲取位置座標。程式碼如下:

import pygame
import sys
import random
from pygame.locals import *  # 引入滑鼠事件型別

pygame.init()  # 初始化
window = pygame.display.set_mode([600, 400])  # 設定視窗

sur = pygame.Surface([600, 400])  # 繪製背景容器
clr = (0, 0, 255)
pos6 = [[200, 200], [300, 200], [400, 200], [
    200, 300], [300, 300], [400, 300]]  # 六個位置
rad = 100
tick = 0  # !!計數器
pos = pos6[0]  # !!在外面記錄圓的位置

while 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == MOUSEBUTTONDOWN:  # !!如果是滑鼠按下事件
            mpos = pygame.mouse.get_pos()  # !!獲取滑鼠位置
            print(mpos)

    # 每幀迴圈執行的程式碼
    if tick > 50:  # 每50次重新整理變換一次
        sur.fill((0, 0, 0))  # 用黑色覆蓋前一幀的畫面,實現重新整理
        a = random.randint(0, 5)  # 隨機0到5
        pos = pos6[a]  # !!更新外部記錄的圓的位置
        pygame.draw.circle(sur, clr, pos, 100)  # !!使用隨機位置
        tick = 0  # 重置計數器
    else:  # !!不重新整理變換的時候
        tick = tick+1  # !!增加計數器

    # 重新整理畫面
    window.blit(sur, (0, 0))
    pygame.display.flip()

執行這個程式碼,任意點選螢幕上的時候就會打印出檔期滑鼠點選的位置。

距離測量

知道當前圓的位置pos,也知道當前點選的位置mpos,這樣我們就可以計算出兩點之間的距離,距離大於圓半徑的就是沒有點到地鼠,距離小於半徑的就是點到地鼠了。

百度搜索【pygame 兩點距離】可以搜到一些計算距離的方法,我們這裡使用pygame官方提供的方法,測試下面程式碼:

import pygame
a=pygame.math.Vector2.length(pygame.math.Vector2(3,4))
print(a)

它會輸出5(勾三股四玄五)。這裡的(3,4)posmpos相減得到的差。
把這個思路帶入原來的程式碼,得到:

import pygame
import sys
import random
from pygame.locals import *  # 引入滑鼠事件型別

pygame.init()  # 初始化
window = pygame.display.set_mode([600, 400])  # 設定視窗

sur = pygame.Surface([600, 400])  # 繪製背景容器
clr = (0, 0, 255)
pos6 = [[200, 200], [300, 200], [400, 200], [
    200, 300], [300, 300], [400, 300]]  # 六個位置
rad = 50
tick = 0  # 計數器
pos = pos6[0]  # 外面記錄圓的位置

while 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == MOUSEBUTTONDOWN:  # 如果是滑鼠按下事件
            mpos = pygame.mouse.get_pos()  # 獲取滑鼠位置
            dis = pygame.math.Vector2(
                mpos[0]-pos[0], mpos[1]-pos[1])  # !!計算座標差
            len = pygame.math.Vector2.length(dis)  # !!計算距離
            if len < rad:
                tick = 51  # !!立即變換位置

    # 每幀迴圈執行的程式碼
    if tick > 50:  # 每50次重新整理變換一次
        sur.fill((0, 0, 0))  # 用黑色覆蓋前一幀的畫面,實現重新整理
        a = random.randint(0, 5)  # 隨機0到5
        pos = pos6[a]  # 更新外部記錄的圓的位置
        pygame.draw.circle(sur, clr, pos, 100)  # 使用隨機位置
        tick = 0  # 重置計數器
    else:  # 不重新整理變換的時候
        tick = tick+1  # 增加計數器

    # 重新整理畫面
    window.blit(sur, (0, 0))
    pygame.display.flip()

在這裡我們設定如果距離長度len小於圓半徑rad,那麼就立即設定tick=51使它大於50,立即進行隨機位置變換。

截止到這裡執行上面的程式碼,可以實現隨機出現地鼠(圓)並能夠點選使它消失,這也實現了遊戲的最基本邏輯功能。後續我們將進一步編寫更多內容,讓它更完善一些。

記錄分數

計算數字增加很容易,設定一個score=0,然後擊中地鼠的時候增加1就可以了。但是,如何把它顯示到螢幕上呢?

可以百度搜索【pygame 顯示文字】然後就可以找到大致方法,我們先進行一些測試:

import pygame

pygame.init()  # 初始化
window = pygame.display.set_mode([600, 400])  # 設定視窗

# 顯示文字
pygame.font.init()  # !!初始化文字
font = pygame.font.SysFont('微軟雅黑', 30)  # !!設定字型和字號
sur = font.render("Hello World!!{}".format(999), False, (255, 0, 0))  # !!生成w文字表面

while 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

    window.blit(sur, (200, 10))  # !!增加分數表面
    pygame.display.flip()

這段程式碼中可以看到pygame繪製文字分三步:

  • pygame.font.init()先要初始化
  • pygame.font.SysFont('微軟雅黑', 30)設定字型和字號大小
  • font.render("Hello World!!{}".format(999), False, (255, 0, 0))生成一個Surface表面
    當然,最後別忘了把表面放到窗口裡window.blit(sur, (200, 10))

執行上面的程式碼得到一個視窗如下:

我們根據這個經驗改進的程式碼:

import pygame
import sys
import random
from pygame.locals import *  # 引入滑鼠事件型別

pygame.init()  # 初始化
window = pygame.display.set_mode([600, 400])  # 設定視窗

sur = pygame.Surface([600, 400])  # 繪製背景容器
clr = (0, 0, 255)
pos6 = [[200, 200], [300, 200], [400, 200], [
    200, 300], [300, 300], [400, 300]]  # 六個位置
rad = 50
tick = 0  # 計數器
pos = pos6[0]  # 外面記錄圓的位置

# 分數
score = 0  # !!分數計數
pygame.font.init()  # !!初始化文字
score_font = pygame.font.SysFont('微軟雅黑', 30)  # !!設定字型和字號
score_sur = score_font.render(str(score), False, (255, 0, 0))  # !!生成計數表面

while 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == MOUSEBUTTONDOWN:  # 如果是滑鼠按下事件
            mpos = pygame.mouse.get_pos()  # 獲取滑鼠位置
            dis = pygame.math.Vector2(
                mpos[0]-pos[0], mpos[1]-pos[1])  # 計算座標差
            len = pygame.math.Vector2.length(dis)  # 計算距離
            if len < rad:
                tick = 1000  # 立即變換位置
                score = score+1  # 計分增加

    # 每幀迴圈執行的程式碼
    if tick > 50:  # 每50次重新整理變換一次
        score_sur = score_font.render(
            str(score), False, (255, 0, 0))  # !!重新生成分數文字表面
        sur.fill((0, 0, 0))  # 用黑色覆蓋前一幀的畫面,實現重新整理
        a = random.randint(0, 5)  # 隨機0到5
        pos = pos6[a]  # 更新外部記錄的圓的位置
        pygame.draw.circle(sur, clr, pos, 50)  # 使用隨機位置
        tick = 0  # 重置計數器
    else:  # 不重新整理變換的時候
        tick = tick+1  # 增加計數器

    # 重新整理畫面
    window.blit(sur, (0, 0))
    window.blit(score_sur, (200, 10))  # !!增加分數表面
    pygame.display.flip()

執行上面的程式碼,可以用滑鼠點選跳動的藍色圓,每次擊中就能獲得1分,實時顯示在頂部。

關於文字的更多內容可以參考官方文件說明。

滑鼠指標變錘子

現在視窗中顯示的仍然是滑鼠,而不是錘子,下面我們來看如何把滑鼠變為一個特定的圖形。

pygame關於滑鼠控制的模組是pygame.mouse,官方說明文件看這裡。

我們可以用pygame.mouse.set_visible(False)來隱藏滑鼠,但這樣一來我們就看不到滑鼠無法操作了。

不過不要緊,我們之前還記得當滑鼠點選的時候有一個mpos = pygame.mouse.get_pos()可以獲取當前滑鼠的位置,同樣我們可以在滑鼠移動的時候獲取滑鼠的位置,然後在這個位置上畫一個紅色圓圈代表滑鼠。

測試下面的程式碼:

import pygame
from pygame.locals import *

pygame.init()
window = pygame.display.set_mode([600, 400])

pygame.mouse.set_visible(False)  # 隱藏滑鼠
sur = pygame.Surface([600, 400])
mpos = [300, 200]  # 記錄滑鼠位置

while 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == MOUSEMOTION:  # 當滑鼠移動的時候
            mpos = pygame.mouse.get_pos()  # 更新滑鼠位置

    sur.fill((0, 0, 0))  # 填充黑色
    pygame.draw.circle(sur, (255, 0, 0), mpos, 10)  # 在滑鼠位置畫紅色圓
    window.blit(sur, (0, 0))
    pygame.display.flip()

執行這個程式碼將,當滑鼠劃到視窗上面的時候就會有一個紅點跟著滑鼠移動,紅點代替了原來的指標。

我們把這個紅點滑鼠程式碼放入到遊戲中,得到下面的程式碼:

import pygame
import sys
import random
from pygame.locals import *  # 引入滑鼠事件型別

pygame.init()  # 初始化
window = pygame.display.set_mode([600, 400])  # 設定視窗

sur = pygame.Surface([600, 400])  # 繪製背景容器
clr = (0, 0, 255)
pos6 = [[200, 200], [300, 200], [400, 200], [
    200, 300], [300, 300], [400, 300]]  # 六個位置
rad = 50
tick = 0  # 計數器
pos = pos6[0]  # 外面記錄圓的位置

# 分數
score = 0  # 分數計數
pygame.font.init()  # 初始化文字
score_font = pygame.font.SysFont('微軟雅黑', 30)  # !!設定字型和字號
score_sur = score_font.render(str(score), False, (255, 0, 0))  # !!生成計數表面

# 滑鼠
pygame.mouse.set_visible(False)  # !!隱藏滑鼠
mpos = [300, 200]  # !!記錄滑鼠位置

while 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == MOUSEBUTTONDOWN:  # 如果是滑鼠按下事件
            mpos = pygame.mouse.get_pos()  # 獲取滑鼠位置
            dis = pygame.math.Vector2(
                mpos[0]-pos[0], mpos[1]-pos[1])  # 計算座標差
            len = pygame.math.Vector2.length(dis)  # 計算距離
            if len < rad:
                tick = 1000  # 立即變換位置
                score = score+1  # 計分增加
        elif event.type == MOUSEMOTION:  # !!當滑鼠移動的時候
            mpos = pygame.mouse.get_pos()  # !!更新滑鼠位置

    # 每幀迴圈執行的程式碼
    if tick > 50:  # 每50次重新整理變換一次
        score_sur = score_font.render(
            str(score), False, (255, 0, 0))  # 重新生成分數文字表面        
        a = random.randint(0, 5)  # 隨機0到5
        pos = pos6[a]  # 更新外部記錄的圓的位置        
        tick = 0  # 重置計數器
    else:  # 不重新整理變換的時候
        tick = tick+1  # 增加計數器

    # 繪製滑鼠
    sur.fill((0, 0, 0))  # !用黑色覆蓋前一幀的畫面,實現重新整理
    pygame.draw.circle(sur, clr, pos, 50)  # !使用隨機位置畫地鼠
    pygame.draw.circle(sur, (255, 0, 0), mpos, 10)  # !!在滑鼠位置畫紅色圓

    # 重新整理畫面
    window.blit(sur, (0, 0))
    window.blit(score_sur, (200, 10))  # 增加分數表面
    pygame.display.flip()

主義者了把sur.fill和原來畫地鼠藍圓的程式碼移到了下面,和畫滑鼠紅點的程式碼放在了一起,這樣把繪圖內容放在一起更加合理。

限定每局時間

我們有很多辦法限定每局的長度,比如計時限定1分鐘,或者限定地鼠跳出總計100次。我們這裡使用第二種限制,跳出100次就結束並統計分數。

新增一個計數器times=0,然後每次隨機位置都給它增加1,當times>100的時候,我們就結束遊戲並顯示結束畫面統計戰果。

具體的程式碼沒有新的內容,不多解釋,直接上結果:

import pygame
import sys
import random
from pygame.locals import *  # 引入滑鼠事件型別

pygame.init()  # 初始化
window = pygame.display.set_mode([600, 400])  # 設定視窗

sur = pygame.Surface([600, 400])  # 繪製背景容器
clr = (0, 0, 255)
pos6 = [[200, 200], [300, 200], [400, 200], [
    200, 300], [300, 300], [400, 300]]  # 六個位置
rad = 50
tick = 0  # 計數器
pos = pos6[0]  # 外面記錄圓的位置

# 分數
score = 0  # 分數計數
pygame.font.init()  # 初始化文字
score_font = pygame.font.SysFont('微軟雅黑', 30)  # !!設定字型和字號
score_sur = score_font.render(str(score), False, (255, 0, 0))  # !!生成計數表面

# 滑鼠
pygame.mouse.set_visible(False)  # !!隱藏滑鼠
mpos = [300, 200]  # !!記錄滑鼠位置

times = 0  # 地鼠跳出的次數
times_max=10 #最多次數
tick_max=15 #地鼠每次跳多少幀

while 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == MOUSEBUTTONDOWN:  # 如果是滑鼠按下事件
            mpos = pygame.mouse.get_pos()  # 獲取滑鼠位置
            dis = pygame.math.Vector2(
                mpos[0]-pos[0], mpos[1]-pos[1])  # 計算座標差
            len = pygame.math.Vector2.length(dis)  # 計算距離
            if len < rad:
                tick = 1000  # 立即變換位置
                score = score+1  # 計分增加
        elif event.type == MOUSEMOTION:  # !!當滑鼠移動的時候
            mpos = pygame.mouse.get_pos()  # !!更新滑鼠位置

        if times > times_max:
            # 顯示結束畫面
            sur.fill((0, 0, 0))
            pygame.mouse.set_visible(True) 
            sur.fill((0, 0, 0)) 
            end_font = pygame.font.SysFont('微軟雅黑', 80)  # !!設定字型和字號
            end_sur = score_font.render("Your Score is:{}/{}!".format(score,times_max), False, (255, 0, 0))  # !!生成計數表面
            window.blit(sur, (0, 0))
            window.blit(end_sur, (100, 100))  # 增加分數表面
            pygame.display.flip()
        else:
            # 每幀迴圈執行的程式碼
            if tick > tick_max:  # 每50次重新整理變換一次
                times=times+1 #增加計次
                score_sur = score_font.render(
                    str(score), False, (255, 0, 0))  # 重新生成分數文字表面        
                a = random.randint(0, 5)  # 隨機0到5
                pos = pos6[a]  # 更新外部記錄的圓的位置        
                tick = 0  # 重置計數器
            else:  # 不重新整理變換的時候
                tick = tick+1  # 增加計數器

            # 繪製滑鼠
            sur.fill((0, 0, 0))  # !用黑色覆蓋前一幀的畫面,實現重新整理
            pygame.draw.circle(sur, clr, pos, 50)  # !使用隨機位置畫地鼠
            pygame.draw.circle(sur, (255, 0, 0), mpos, 10)  # !!在滑鼠位置畫紅色圓

            # 重新整理畫面
            window.blit(sur, (0, 0))
            window.blit(score_sur, (200, 10))  # 增加分數表面
            pygame.display.flip()

執行這個程式碼,用滑鼠點選藍圓,藍圓跳動10次之後結束,然後顯示擊中的次數。你可以通過調整tick_max的數字讓圓跳動的更快或更慢,調整times_max=100來讓地鼠跳動100次後再結束。

現在我們的地鼠遊戲已經有些模樣了,但還都是藍色紅色的圓圈和圓點,下一篇我們來改變成為圖片。

中文字型

在上一節中我們只使用了英文字型,怎麼顯示中文字型呢?

直接下載網盤裡面的檔案,放在你的main.py一起,將原來的

score_font = pygame.font.SysFont('微軟雅黑', 30)

修改為:

score_font = pygame.font.Font('MicrosoftYaqiHeiLight-2.ttf', 30) 

然後在render裡面使用中文就可以正常顯示了:

end_sur = score_font.render("你的得分:{}/{}!".format(score,times_max), False, (255, 0, 0))

另外,也可以使用系統的中文字型,但是我們不清楚系統裡面到底裝了哪些字型,可以用print(pygame.font.get_fonts())將所有系統字型都打印出來,然後只能從名字猜出哪些是中文字型了,注意系統字型還是要用font.SysFont而不只是font.Font

顯示背景圖片

這是我們的背景圖片dds-map.jpg

我們可以用map=pygame.image.load('dds-map.jpg')把圖片讀取到程式碼裡面。
更多官方關於圖片的操作說明看這裡
注意pygame.image.load()得到的是一個表面surface,我們可以直接把它blit到視窗wind,也可以把它blit到。

這裡是完整程式碼:

import pygame
import sys
import random
from pygame.locals import *  # 引入滑鼠事件型別

pygame.init()  # 初始化
window = pygame.display.set_mode([600, 400])  # 設定視窗

sur = pygame.Surface([600, 400])  # 繪製背景容器
clr = (0, 0, 255)
posAll = [[100, 150], [300, 150], [500, 150], [
    200, 300], [400, 300]]  # 六個位置
rad = 50
tick = 0  # 計數器
pos = posAll[0]  # 外面記錄圓的位置

# 分數
score = 0  # 分數計數
pygame.font.init()  # 初始化文字
score_font = pygame.font.Font('MicrosoftYaqiHeiLight-2.ttf', 30)  # !!設定字型和字號
score_sur = score_font.render(str(score), False, (255, 0, 0))  # !!生成計數表面

# 滑鼠
pygame.mouse.set_visible(False)  # !!隱藏滑鼠
mpos = [300, 200]  # !!記錄滑鼠位置

times = 0  # 地鼠跳出的次數
times_max=10 #最多次數
tick_max=30 #地鼠每次跳多少幀
map=pygame.image.load('dds-map.jpg')#!!讀取圖片

while 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == MOUSEBUTTONDOWN:  # 如果是滑鼠按下事件
            dis = pygame.math.Vector2(
                mpos[0]-pos[0], mpos[1]-pos[1])  # 計算座標差
            len = pygame.math.Vector2.length(dis)  # 計算距離
            if len < rad:
                tick = 1000  # 立即變換位置
                score = score+1  # 計分增加
        elif event.type == MOUSEMOTION:  # 當滑鼠移動的時候
            mpos = pygame.mouse.get_pos()  # 更新滑鼠位置        

    if times >= times_max:
        # 顯示結束畫面
        sur.fill((0, 0, 0)) #!!結束時候仍然用黑色清空畫面
        pygame.mouse.set_visible(True) 
        end_font = pygame.font.Font('MicrosoftYaqiHeiLight-2.ttf',48) # !!設定字型和字號
        end_sur = score_font.render("你的分數是:{}/{}!".format(score,times_max), True, (255, 0, 0))  # !!生成計數表面
        window.blit(sur, (0, 0))
        window.blit(end_sur, (100, 100))  # 增加分數表面
    else:
        sur.blit(map, (0, 0)) #!!新增背景圖片
        # 每幀迴圈執行的程式碼
        if tick > tick_max:  # 每50次重新整理變換一次
            times=times+1 #增加計次
            score_sur = score_font.render(
                "分數:{}/{}!".format(score,times), False, (255, 0, 0))  # 重新生成分數文字表面        
            a = random.randint(0, 4)  # 隨機0到4
            pos = posAll[a]  # 更新外部記錄的圓的位置        
            tick = 0  # 重置計數器
        else:  # 不重新整理變換的時候
            tick = tick+1  # 增加計數器      

        # 繪製滑鼠
        pygame.draw.circle(sur, clr, pos, 50)  # 使用隨機位置畫地鼠            
        pygame.draw.circle(sur, (255, 0, 0), mpos, 10)  # !在滑鼠位置畫紅色圓

    # 重新整理畫面            
    window.blit(sur, (0, 0))
    window.blit(score_sur, (200, 10))  # 增加分數表面
    pygame.display.flip() #重新整理畫面

注意我們先把圖片讀取,然後在每幀裡面決定是否使用。執行後如下圖:

使用動態圖片

地鼠和錘子各有兩個狀態,正常的地鼠和被擊打的地鼠,正常的錘子和砸下的錘子,如下圖所示(下圖無法直接使用,請從網盤下載):

我們可以先把四個圖片都load讀取進來成為rat1,rat2,ham1,ham2,然後我們使用ratsurhamsur表示真正要使用的表面,當滑鼠按下的時候我們設定hamsur=ham2是砸下圖片,當滑鼠點選位置距離地鼠小於地鼠半徑的時候我們使用ratsur=rat2被砸中的圖片。最後我們再分別把地鼠和錘頭blitsur上面。

改造後的程式碼如下:

import pygame
import sys
import random
from pygame.locals import *  # 引入滑鼠事件型別
import time

pygame.init()  # 初始化
window = pygame.display.set_mode([600, 400])  # 設定視窗

sur = pygame.Surface([600, 400])  # 繪製背景容器
clr = (0, 0, 255)
posAll = [[100, 150], [300, 150], [500, 150], [
    200, 300], [400, 300]]  # 六個位置
rad = 50
tick = 0  # 計數器
pos = posAll[0]  # 外面記錄圓的位置

# 分數
score = 0  # 分數計數
pygame.font.init()  # 初始化文字
score_font = pygame.font.Font('MicrosoftYaqiHeiLight-2.ttf', 30)  # !!設定字型和字號
score_sur = score_font.render(str(score), False, (255, 0, 0))  # !!生成計數表面

# 滑鼠
pygame.mouse.set_visible(False)  # !!隱藏滑鼠
mpos = [300, 200]  # !!記錄滑鼠位置

times = 0  # 地鼠跳出的次數
times_max=10 #最多次數
tick_max=30 #地鼠每次跳多少幀
map=pygame.image.load('dds-map.jpg')#!!讀取圖片
rat1=pygame.image.load('rat1.png')#!!讀取地鼠圖片
rat2=pygame.image.load('rat2.png')#!!讀取被砸地鼠圖片
ham1=pygame.image.load('hammer1.png')#!!讀取錘子圖片
ham2=pygame.image.load('hammer2.png')#!!讀取砸下錘子圖片

while 1:
    hamsur=ham1
    ratsur=rat1
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == MOUSEBUTTONDOWN:  # 如果是滑鼠按下事件
            hamsur=ham2 #!!使用下落錘子
            mpos = pygame.mouse.get_pos()  # 獲取滑鼠位置
            dis = pygame.math.Vector2(
                mpos[0]-pos[0], mpos[1]-pos[1])  # 計算座標差
            len = pygame.math.Vector2.length(dis)  # 計算距離
            if len < rad:
                tick = 1000  # 立即變換位置
                score = score+1  # 計分增加
                ratsur=rat2 #!!使用被砸地鼠
        elif event.type == MOUSEMOTION:  # 當滑鼠移動的時候
            mpos = pygame.mouse.get_pos()  # 更新滑鼠位置        

    if times >= times_max:
        # 顯示結束畫面
        sur.fill((0, 0, 0)) #結束時候仍然用黑色清空畫面
        pygame.mouse.set_visible(True) 
        end_font = pygame.font.Font('MicrosoftYaqiHeiLight-2.ttf',48) # !!設定字型和字號
        end_sur = score_font.render("你的分數是:{}/{}!".format(score,times_max), True, (255, 0, 0))  # !!生成計數表面
        window.blit(sur, (0, 0))
        window.blit(end_sur, (100, 100))  # 增加分數表面
    else:
        sur.blit(map, (0, 0)) #新增背景圖片
        # 每幀迴圈執行的程式碼
        if tick > tick_max:  # 每50次重新整理變換一次
            times=times+1 #增加計次
            score_sur = score_font.render(
                "分數:{}/{}!".format(score,times), False, (255, 0, 0))  # 重新生成分數文字表面        
            a = random.randint(0, 4)  # 隨機0到4
            pos = posAll[a]  # 更新外部記錄的圓的位置        
            tick = 0  # 重置計數器
        else:  # 不重新整理變換的時候
            tick = tick+1  # 增加計數器

        sur.blit(ratsur,(pos[0]-50,pos[1]-70)) #繪製地鼠
        sur.blit(hamsur,(mpos[0]-50,mpos[1]-100)) #繪製錘頭

    # 重新整理畫面            
    window.blit(sur, (0, 0))
    window.blit(score_sur, (200, 10))  # 增加分數表面
    pygame.display.flip() #重新整理畫面
    time.sleep(0.04) #!!保持畫面一點時間

注意這裡的import timetime.sleep(0.04)這是讓每一幀停留一點點時間,0.04秒,每秒25幀(假設每幀畫圖不需要時間的話)。
另外我們再blit的時候使用了(pos[0]-50,pos[1]-50)這樣的偏移,因為圖片總是用左上角作為位置的起點,這樣偏移之後就變到了圖片中心,實際上我們又故意讓地鼠和錘子更高一些,就使用了(pos[0]-50,pos[1]-70)

執行之後的樣子如下圖:

讓遊戲重新開始

每次顯示最終成績之後,能不能讓遊戲3秒後重新開始呢?

我們設定一個gameover=0,遊戲結束後每幀都增加這個數字,如果gameover>100,就是過了100幀,那麼我們就重新開始。
重新開始必須意味著各種資料(分數,計時什麼的)和畫面都要重置到原來的狀態。
修改後的整體程式碼如下:

import pygame
import sys
import random
from pygame.locals import *  # 引入滑鼠事件型別
import time

pygame.init()  # 初始化
window = pygame.display.set_mode([600, 400])  # 設定視窗

sur = pygame.Surface([600, 400])  # 繪製背景容器
clr = (0, 0, 255)
posAll = [[100, 150], [300, 150], [500, 150], [200, 300], [400, 300]]  # 六個位置
rad = 50
tick = 0  # 計數器
pos = posAll[0]  # 外面記錄圓的位置

# 分數
score = 0  # 分數計數
pygame.font.init()  # 初始化文字
score_font = pygame.font.Font("MicrosoftYaqiHeiLight-2.ttf", 30)  # !!設定字型和字號
score_sur = score_font.render(str(score), False, (255, 0, 0))  # !!生成計數表面

# 滑鼠
pygame.mouse.set_visible(False)  # !!隱藏滑鼠
mpos = [300, 200]  # !!記錄滑鼠位置

times = 0  # 地鼠跳出的次數
times_max = 10  # 最多次數
tick_max = 30  # 地鼠每次跳多少幀
map = pygame.image.load("dds-map.jpg")  # !!讀取圖片
rat1 = pygame.image.load("rat1.png")  # !!讀取地鼠圖片
rat2 = pygame.image.load("rat2.png")  # !!讀取被砸地鼠圖片
ham1 = pygame.image.load("hammer1.png")  # !!讀取錘子圖片
ham2 = pygame.image.load("hammer2.png")  # !!讀取砸下錘子圖片

gameover = 0 #!!結束計時
gameover_max = 100 #!!結束計時最大值,超過這個值就重新開始

while 1:
    hamsur = ham1
    ratsur = rat1
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == MOUSEBUTTONDOWN:  # 如果是滑鼠按下事件
            hamsur = ham2  # !!使用下落錘子
            mpos = pygame.mouse.get_pos()  # 獲取滑鼠位置
            dis = pygame.math.Vector2(mpos[0] - pos[0], mpos[1] - pos[1])  # 計算座標差
            len = pygame.math.Vector2.length(dis)  # 計算距離
            if len < rad:
                tick = 1000  # 立即變換位置
                score = score + 1  # 計分增加
                ratsur = rat2  # !!使用被砸地鼠
        elif event.type == MOUSEMOTION:  # 當滑鼠移動的時候
            mpos = pygame.mouse.get_pos()  # 更新滑鼠位置

    if times >= times_max:
        # 顯示結束畫面
        sur.fill((0, 0, 0))  # 結束時候仍然用黑色清空畫面
        pygame.mouse.set_visible(True)
        end_font = pygame.font.Font("MicrosoftYaqiHeiLight-2.ttf", 48)  # !!設定字型和字號
        end_sur = score_font.render(
            "你的分數是:{}/{}!".format(score, times_max), True, (255, 0, 0)
        )  # !!生成計數表面
        sur.blit(end_sur, (100, 150))
        cd = int((gameover_max - gameover) / 10)
        cd_sur = score_font.render(
            "重新開始倒計時{}".format(cd), True, (255, 0, 0)
        )  # !!生成計數表面
        sur.blit(cd_sur, (100, 200))  # 增加分數表面
        gameover = gameover + 1 #!!增加結束計時
    else:
        sur.blit(map, (0, 0))  # 新增背景圖片
        score_sur = score_font.render(
            "分數:{}/{}!".format(score, times + 1), False, (255, 0, 0)
        )  # 重新生成分數文字表面
        sur.blit(score_sur, (200, 10))  # 增加分數表面
        if tick > tick_max:  # 每50次重新整理變換一次
            times = times + 1  # 增加計次
            a = random.randint(0, 4)  # 隨機0到4
            pos = posAll[a]  # 更新外部記錄的圓的位置
            tick = 0  # 重置計數器
        else:  # 不重新整理變換的時候
            tick = tick + 1  # 增加計數器
        if tick > 5:  # 開始幾幀不顯示地鼠
            sur.blit(ratsur, (pos[0] - 50, pos[1] - 70))  # 繪製地鼠
        sur.blit(hamsur, (mpos[0] - 50, mpos[1] - 100))  # 繪製錘頭

    # 重新整理畫面
    window.blit(sur, (0, 0))
    pygame.display.flip()  # 重新整理畫面
    time.sleep(0.04)  # !!保持畫面一點時間

    # !!重置遊戲
    if gameover > gameover_max:
        pygame.mouse.set_visible(False)
        times = 0
        score = 0
        gameover = 0

執行這個程式碼就能反覆玩遊戲了。
到這裡遊戲看上去好了很多,但是還沒有背景音樂,打地鼠的時候也沒有音效,下一節我們繼續新增聲音。

新增音效

遊戲裡面的聲音分為兩種,一種叫音樂music,另一種叫音效sound。背景音樂是music,遊戲裡面的擊打聲點選聲都是音效。同一時間播放的音樂一般只有一個,但音效可以有很多個同時播放。

pygame可以使用pygame.mixer.music.load('bg.mp3')來載入foo.mp3音樂,然後pygame.mixer.music.play(0)就可以播放,這裡0表示播放1次,如果要無限次的播放則要改為-1.

但是如果要播放音效sound,那麼pygame裡面只能使用wav格式(並且不支援32位深,只支援16位深)。載入音效的方法是sd=pygame.mixer.Sound("hit.wav"),播放是sd.play(0),這裡0也是1次,一般音效不需要連續播放。

我們在遊戲一開始就可以播放背景音樂了,但只有在點選滑鼠event.type == MOUSEBUTTONDOWN的時候才播放錘子的聲音,只有在擊中地鼠的時候才播放地鼠的叫聲。

修改之後的程式碼如下:

import pygame
import sys
import random
from pygame.locals import *  # 引入滑鼠事件型別
import time

pygame.init()  # 初始化
window = pygame.display.set_mode([600, 400])  # 設定視窗

sur = pygame.Surface([600, 400])  # 繪製背景容器
clr = (0, 0, 255)
posAll = [[100, 150], [300, 150], [500, 150], [200, 300], [400, 300]]  # 六個位置
rad = 50
tick = 0  # 計數器
pos = posAll[0]  # 外面記錄圓的位置

# 分數
score = 0  # 分數計數
pygame.font.init()  # 初始化文字
score_font = pygame.font.Font("MicrosoftYaqiHeiLight-2.ttf", 30)  # !!設定字型和字號
score_sur = score_font.render(str(score), False, (255, 0, 0))  # !!生成計數表面

# 滑鼠
pygame.mouse.set_visible(False)  # !!隱藏滑鼠
mpos = [300, 200]  # !!記錄滑鼠位置

times = 0  # 地鼠跳出的次數
times_max = 10  # 最多次數
tick_max = 30  # 地鼠每次跳多少幀
map = pygame.image.load("dds-map.jpg")  # !!讀取圖片
rat1 = pygame.image.load("rat1.png")  # !!讀取地鼠圖片
rat2 = pygame.image.load("rat2.png")  # !!讀取被砸地鼠圖片
ham1 = pygame.image.load("hammer1.png")  # !!讀取錘子圖片
ham2 = pygame.image.load("hammer2.png")  # !!讀取砸下錘子圖片

gameover = 0  # !!結束計時
gameover_max = 100  # !!結束計時最大值,超過這個值就重新開始

# 音樂和音效
pygame.mixer.music.load("bg.mp3")  # !!載入背景音樂
pygame.mixer.music.play(-1)  # !!無限播放背景音樂
hitsound = pygame.mixer.Sound("hit.wav")  # !!載入擊打聲音
hurtsound = pygame.mixer.Sound("aiyo2.wav")  # !!載入地鼠叫聲

while 1:
    hamsur = ham1
    ratsur = rat1
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == MOUSEBUTTONDOWN:  # 如果是滑鼠按下事件
            hamsur = ham2  # 使用下落錘子
            hitsound.play()  # !!播放擊打聲音
            mpos = pygame.mouse.get_pos()  # 獲取滑鼠位置
            dis = pygame.math.Vector2(mpos[0] - pos[0], mpos[1] - pos[1])  # 計算座標差
            len = pygame.math.Vector2.length(dis)  # 計算距離
            if len < rad:
                tick = 1000  # 立即變換位置
                score = score + 1  # 計分增加
                ratsur = rat2  # 使用被砸地鼠
                hurtsound.play()  # !!播放地鼠聲音
        elif event.type == MOUSEMOTION:  # 當滑鼠移動的時候
            mpos = pygame.mouse.get_pos()  # 更新滑鼠位置

    if times >= times_max:
        # 顯示結束畫面
        sur.fill((0, 0, 0))  # 結束時候仍然用黑色清空畫面
        pygame.mouse.set_visible(True)
        end_font = pygame.font.Font("MicrosoftYaqiHeiLight-2.ttf", 48)  # 設定字型和字號
        end_sur = score_font.render(
            "你的分數是:{}/{}!".format(score, times_max), True, (255, 0, 0)
        )  # 生成計數表面
        sur.blit(end_sur, (100, 150))
        cd = int((gameover_max - gameover) / 10)
        cd_sur = score_font.render(
            "重新開始倒計時{}".format(cd), True, (255, 0, 0)
        )  # 生成計數表面
        sur.blit(cd_sur, (100, 200))  # 增加分數表面
        gameover = gameover + 1  # !!增加結束計時
    else:
        sur.blit(map, (0, 0))  # 新增背景圖片
        score_sur = score_font.render(
            "分數:{}/{}!".format(score, times + 1), False, (255, 0, 0)
        )  # 重新生成分數文字表面
        sur.blit(score_sur, (200, 10))  # 增加分數表面
        if tick > tick_max:  # 每50次重新整理變換一次
            times = times + 1  # 增加計次
            a = random.randint(0, 4)  # 隨機0到4
            pos = posAll[a]  # 更新外部記錄的圓的位置
            tick = 0  # 重置計數器
        else:  # 不重新整理變換的時候
            tick = tick + 1  # 增加計數器
        if tick > 5:  # 開始幾幀不顯示地鼠
            sur.blit(ratsur, (pos[0] - 50, pos[1] - 70))  # 繪製地鼠
        sur.blit(hamsur, (mpos[0] - 50, mpos[1] - 100))  # 繪製錘頭

    # 重新整理畫面
    window.blit(sur, (0, 0))
    pygame.display.flip()  # 重新整理畫面
    time.sleep(0.04)  # 保持畫面一點時間

    # 重置遊戲
    if gameover > gameover_max:
        pygame.mouse.set_visible(False)
        times = 0
        score = 0
        gameover = 0

執行上面的程式碼,可以聽到歡快的背景音樂,點選滑鼠時候會有捶地聲音,打中地鼠會有哎呦的叫聲。

釋出軟體

我們寫的程式碼目前只能在自己的電腦上執行,因為我們先要安裝python,然後還要安裝pygame才行,這和我們平常下載的軟體不同,下載的軟體可以直接執行(或者安裝自身後執行)。

Python給我們提供了自動把程式碼打包成軟體的工具,Windows下推薦使用auto-py-to-exe工具。同樣先安裝pip install auto-py-to-exe,然後只要執行auto-py-to-exe就會開啟一個視窗。

基本設定如下:

注意幾個地方:

  • Script Location要指向你的主要.py檔案,這裡是main.py
  • Onefile選One Directory,這會把生產的所有檔案放在一個資料夾中
  • Console Window選Console Based,因為我們的pygame是基於控制檯的
  • Icon圖示,你可以在網上下載.ico檔案,比如easyicon有很多,網盤裡面有一個地鼠圖示icon.ico
  • Additional Files附加檔案,點選Add Files按鈕要把全部用到的字型、圖片、聲音都選擇
  • CONVERT .PY TO .EXE點選這個按鈕進行生成,生成後會變為兩個藍色按鈕

點選OPEN OUTPUT FOLDER開啟生產的軟體目錄(預設在你的程式碼資料夾下面的output資料夾內),找到那個和你的Script Location同名的檔案,點選它就可以運行遊戲了。

也可以把這個MAIN.exe複製然後在桌面上貼上快捷方式,以後只要點這個快捷方式就可以了。

在網盤檔案中包含一個main.rar檔案,下載它然後解壓就可以得到我打包生成的軟體了。

關於Mac蘋果電腦下面生成軟體的方法暫時遇到一點麻煩,搞定之後再更新,敬請關注。

第一個小遊戲似乎開發完成了,但是還有很多內容,我們的程式碼也有很多不合理的地方,下一篇我們一起來回顧和整理,並且繼續介紹更多小遊戲的開發方法。

新增音效

遊戲裡面的聲音分為兩種,一種叫音樂music,另一種叫音效sound。背景音樂是music,遊戲裡面的擊打聲點選聲都是音效。同一時間播放的音樂一般只有一個,但音效可以有很多個同時播放。

pygame可以使用pygame.mixer.music.load('bg.mp3')來載入foo.mp3音樂,然後pygame.mixer.music.play(0)就可以播放,這裡0表示播放1次,如果要無限次的播放則要改為-1.

但是如果要播放音效sound,那麼pygame裡面只能使用wav格式(並且不支援32位深,只支援16位深)。載入音效的方法是sd=pygame.mixer.Sound("hit.wav"),播放是sd.play(0),這裡0也是1次,一般音效不需要連續播放。

我們在遊戲一開始就可以播放背景音樂了,但只有在點選滑鼠event.type == MOUSEBUTTONDOWN的時候才播放錘子的聲音,只有在擊中地鼠的時候才播放地鼠的叫聲。

修改之後的程式碼如下:

import pygame
import sys
import random
from pygame.locals import *  # 引入滑鼠事件型別
import time

pygame.init()  # 初始化
window = pygame.display.set_mode([600, 400])  # 設定視窗

sur = pygame.Surface([600, 400])  # 繪製背景容器
clr = (0, 0, 255)
posAll = [[100, 150], [300, 150], [500, 150], [200, 300], [400, 300]]  # 六個位置
rad = 50
tick = 0  # 計數器
pos = posAll[0]  # 外面記錄圓的位置

# 分數
score = 0  # 分數計數
pygame.font.init()  # 初始化文字
score_font = pygame.font.Font("MicrosoftYaqiHeiLight-2.ttf", 30)  # !!設定字型和字號
score_sur = score_font.render(str(score), False, (255, 0, 0))  # !!生成計數表面

# 滑鼠
pygame.mouse.set_visible(False)  # !!隱藏滑鼠
mpos = [300, 200]  # !!記錄滑鼠位置

times = 0  # 地鼠跳出的次數
times_max = 10  # 最多次數
tick_max = 30  # 地鼠每次跳多少幀
map = pygame.image.load("dds-map.jpg")  # !!讀取圖片
rat1 = pygame.image.load("rat1.png")  # !!讀取地鼠圖片
rat2 = pygame.image.load("rat2.png")  # !!讀取被砸地鼠圖片
ham1 = pygame.image.load("hammer1.png")  # !!讀取錘子圖片
ham2 = pygame.image.load("hammer2.png")  # !!讀取砸下錘子圖片

gameover = 0  # !!結束計時
gameover_max = 100  # !!結束計時最大值,超過這個值就重新開始

# 音樂和音效
pygame.mixer.music.load("bg.mp3")  # !!載入背景音樂
pygame.mixer.music.play(-1)  # !!無限播放背景音樂
hitsound = pygame.mixer.Sound("hit.wav")  # !!載入擊打聲音
hurtsound = pygame.mixer.Sound("aiyo2.wav")  # !!載入地鼠叫聲

while 1:
    hamsur = ham1
    ratsur = rat1
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == MOUSEBUTTONDOWN:  # 如果是滑鼠按下事件
            hamsur = ham2  # 使用下落錘子
            hitsound.play()  # !!播放擊打聲音
            mpos = pygame.mouse.get_pos()  # 獲取滑鼠位置
            dis = pygame.math.Vector2(mpos[0] - pos[0], mpos[1] - pos[1])  # 計算座標差
            len = pygame.math.Vector2.length(dis)  # 計算距離
            if len < rad:
                tick = 1000  # 立即變換位置
                score = score + 1  # 計分增加
                ratsur = rat2  # 使用被砸地鼠
                hurtsound.play()  # !!播放地鼠聲音
        elif event.type == MOUSEMOTION:  # 當滑鼠移動的時候
            mpos = pygame.mouse.get_pos()  # 更新滑鼠位置

    if times >= times_max:
        # 顯示結束畫面
        sur.fill((0, 0, 0))  # 結束時候仍然用黑色清空畫面
        pygame.mouse.set_visible(True)
        end_font = pygame.font.Font("MicrosoftYaqiHeiLight-2.ttf", 48)  # 設定字型和字號
        end_sur = score_font.render(
            "你的分數是:{}/{}!".format(score, times_max), True, (255, 0, 0)
        )  # 生成計數表面
        sur.blit(end_sur, (100, 150))
        cd = int((gameover_max - gameover) / 10)
        cd_sur = score_font.render(
            "重新開始倒計時{}".format(cd), True, (255, 0, 0)
        )  # 生成計數表面
        sur.blit(cd_sur, (100, 200))  # 增加分數表面
        gameover = gameover + 1  # !!增加結束計時
    else:
        sur.blit(map, (0, 0))  # 新增背景圖片
        score_sur = score_font.render(
            "分數:{}/{}!".format(score, times + 1), False, (255, 0, 0)
        )  # 重新生成分數文字表面
        sur.blit(score_sur, (200, 10))  # 增加分數表面
        if tick > tick_max:  # 每50次重新整理變換一次
            times = times + 1  # 增加計次
            a = random.randint(0, 4)  # 隨機0到4
            pos = posAll[a]  # 更新外部記錄的圓的位置
            tick = 0  # 重置計數器
        else:  # 不重新整理變換的時候
            tick = tick + 1  # 增加計數器
        if tick > 5:  # 開始幾幀不顯示地鼠
            sur.blit(ratsur, (pos[0] - 50, pos[1] - 70))  # 繪製地鼠
        sur.blit(hamsur, (mpos[0] - 50, mpos[1] - 100))  # 繪製錘頭

    # 重新整理畫面
    window.blit(sur, (0, 0))
    pygame.display.flip()  # 重新整理畫面
    time.sleep(0.04)  # 保持畫面一點時間

    # 重置遊戲
    if gameover > gameover_max:
        pygame.mouse.set_visible(False)
        times = 0
        score = 0
        gameover = 0

執行上面的程式碼,可以聽到歡快的背景音樂,點選滑鼠時候會有捶地聲音,打中地鼠會有哎呦的叫聲