程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Python寫個小游戲:看圖猜成語(上)

編輯:Python

文章目錄

  • 前言
  • 看圖猜成語
    • 1. 玩法簡介
    • 2. 游戲流程
    • 3. 搭建游戲界面
      • 1). 初始化游戲窗口和背景
      • 2). 准備畫布
      • 3). 繪制游戲背景
      • 4). 插入游戲標題
      • 5). 顯示成語圖片
      • 6). 顯示成語填空處
      • 7). 插入20個按鈕
      • 8). 插入2個功能小按鈕
      • 9). 關卡說明
    • 4. 知識點回顧
  • 總結與思考


前言

大家好,又見面了,經過前幾篇文章的連載,相信大家再通過自學,一定都掌握了Python的基礎知識了吧。 今天開始,問哥開始帶著大家逐步向圖形化界面前進了。

說實話,玩游戲,更多的時候還是需要圖形化界面的,這都2022年了,文字版游戲的年代早過去了。雖然問哥也無比懷念滿屏黑底花花綠綠的文字刷屏的文字MUD年代,但也不得不承認,圖形化的游戲,甚至3D游戲,才能算得上游戲。所以,GUI編程不可避免。

大家也不用擔心,其實我們之前學的Python知識已經足夠了,而GUI是通過其他的模塊來實現的。比如Python內置的Tkinter,Turtle,第三方模塊Pygame和PyZero(Pygame簡化版),PyQT5等等等等。這些模塊各有各的特點和用法,如果把Python比喻成顏料,那這些模塊可以說是筆、圓規和直尺。沒有辦法只用一套規則就可以掌握所有工具。

但是這些工具各有所長,比如Pygame就是專門用來開發游戲的,所以我們也不需要全部掌握。不過考慮到Tkinter是Python自帶的圖形化工具,不需要另外安裝。雖然功能有限,還是可以開發一些有趣的小程序。問哥決定還是先用Tkinter做幾個游戲,再開始Pygame的學習。

首先想到的就是看圖猜成語,實現起來比較簡單,又可以了解到Tkinter的工作方式,還會接觸到一點點面向對象的知識。不過礙於篇幅,問哥還是打算分成上下篇:

上篇 —— 游戲界面的搭建
下篇 —— 後台程序的實現


看圖猜成語

1. 玩法簡介

看圖猜成語大家都玩過吧。規則很簡單:打開程序,自動會展示出一張形容成語的圖片,然後有若干個迷惑性的漢字。玩家要從裡面找到正確的漢字組成成語,成功了即進入下一關。

游戲截圖:

2. 游戲流程

GUI編程有些時候流程圖會比較難畫,因為鍵盤敲入、鼠標點擊或移動等事件的執行往往不是順序化的。但是如果能夠把流程圖畫出來,即使很簡單的流程圖,一樣會讓我們程序實現起來更加容易和清晰。

看圖猜成語的簡易流程圖如下:

NoYesYesNo 游戲開始 畫面展示 玩家選擇漢字組成成語 判斷玩家輸入是否正確 清空玩家的選擇 恭喜,是否進入下一關 重新選詞 游戲結束

3. 搭建游戲界面

編寫GUI游戲程序和純文本程序有很多不同,其中我認為比較重要的是,游戲的外觀設計所占的重要程度大大提高了。一款好的游戲,一定首先是要讓人看起來有想要玩的欲望。所以好的UI設計才那麼值錢。雖然獨立程序猿很難身兼數職,既寫代碼,又搞美工,但至少可以讓畫出來的GUI界面簡潔整齊一些,功能清楚一些。多嘗試,至少要先找到自己看著舒服的排版最重要。

拿這個小項目為例,分析游戲的截圖,可以看到游戲界面主要有以下7個部分:

  1. 游戲背景:網上搜尋到的一張適合休閒游戲的背景圖片素材
  2. 標題:用PS模板做出來的特效文字圖片
  3. 成語圖片:網上搜尋到的成語圖片素材
  4. 20個干擾漢字:20個顯示漢字的按鈕(因為要實現點擊的效果,所以問哥使用了按鈕)
  5. 成語填空處:4個空白文本框(矩形),顯示玩家選擇的漢字
  6. 關卡說明:第N關的文字,隨著游戲關卡變化
  7. 兩個功能按鈕:綁定了回調函數,實現兩個小功能:1)清空用戶的選擇,2)由電腦隨機提示一個漢字。

接下來我們先一步步把這個框架搭出來,程序實現的部分留到後面再改。

1). 初始化游戲窗口和背景

首先我們要有一個游戲窗口,不然上面說的這些素材也沒地方放。所以我們可以使用tkinter模塊先畫出游戲窗口。

別忘記要先引用tikinter模塊

import tkinter as tk # 引入tkinter模塊並簡寫成tk
root = tk.Tk() # 定義一個主窗口
root.geometry("500x300") # 主窗口的尺寸
root.resizable(0,0) # 主窗口不可改變大小
root.title('看圖猜成語') # 主窗口的標題
root.mainloop() # 主窗口循環展示

有了上面這幾條代碼,我們就可以在電腦上畫出一個寬500像素,高300像素的窗口了。

大家唯一要注意的是代碼格式:

  1. geometry方法裡的寬度和高度是字符串格式,而且橫縱之比中間是小寫字母x;
  2. title方法就是改變左上角的文字,當然左上角的小圖標也可以改;
  3. resizable方法是規定玩家可以改變窗口大小的范圍,橫縱都設置為0表示玩家不可用鼠標改變窗口大小;
  4. mainloop方法放在所有程序的最後,表示窗體循環展示,沒有這句代碼的話窗口一閃而過,啥也看不見。而其他所有的代碼都要放在mainloop之前,表示在循環之內。

接下來我們就要像這張空白的窗口裡添加元素了。

2). 准備畫布

雖然有很多種方法可以把圖片插入到tkinter裡,但問哥喜歡用的還是Canvas畫布方法。因為畫布可以有很大的靈活性,除了可以在上面插入圖片,還可以繪制各種形狀,而且其他組件也都可以通過定位准確地放在畫布上。

**Canvas是Tkinter的一個組件(widget),可以理解為可以擺放在窗口裡的一個小模塊,就像可以擺在家裡的家具一樣。還有其他很多組件,如Label, Button, Entry等等,等到我們用到的時候再說。而每個組件其實在程序裡就是一個類(面向對象),我們用創建實例的方法創建一個組件。

定義一個Canvas畫布只要一條命令,比如下面這樣就定義了一個和主窗口一樣大小的白色畫布:

cv=tk.Canvas(root,bg='white',width=500,height=300)

Canvas類需要的第一個參數就是主窗口,表示將要在哪裡放置Canvas畫布組件,我們已經在上面建立主窗口的時候定義了一個變量root,所以root就代表了主窗口。而其他的參數都是關鍵字傳參。包括Canvas在內的每個組件都有很多關鍵字屬性,不過常用的也就幾樣。這裡我們就只是用到了bg(背景),width(寬度),height(高度)。

而每種組件還需要使用pack()等定位方法“堆”(pack)到主窗口上,才能被我們看見:

cv.pack()


但是,在使用pack()把Canvas畫布放進窗口之前,我們必須先把其他的元素或組件繪制在畫布上,這就好比我們必須先在紙上畫好畫,才能把它裱起來。不然就只有這樣一張白色的畫布,其他的東西也畫不進去了。所以我們把pack()語句放在root的mainloop()前面,而且都放在最後。

cv.pack()
root.mainloop()

在這之前,我們先在畫布上放置其他組件

3). 繪制游戲背景

首先找到一張背景圖片。問哥是在網上搜的,大家也可以隨便找一張自己喜歡的圖片做背景。

需要指出的是,tkinter因為並不是為了支持動畫而設計的,所以對圖片的支持並不友好,如果你的tkinter的版本是8.5,將不能支持PNG圖片。

可以在控制台窗口使用一下命令查看tkinter的版本

import tkinter
tkinter.TkVersion
8.6

Python3.7之後的版本默認都自帶8.6版本的tkinter。即便如此,tkinter自己的方法也不支持縮小放大圖片等功能。所以使用起來會比較麻煩,需要使用python的另一個內置模塊PIL來處理圖片。我們以後也會用到,不過基於目前這個小項目,我們可以使用不需要更改大小的PNG圖片。

首先使用tkinter的PhotoImage方法將bg.png圖片讀取到內存中,取個名字叫bg。然後就可以使用Canvas的create_image方法可以將背景圖片bg.png放在畫布上,具體代碼如下:

bg = tk.PhotoImage(file=r"images\bg.png")
cv_bg = cv.create_image(250,150,image = bg)

cv就是我們剛才創建的Canvas畫布實例,調用它的create_image方法將內存中的圖片bg加載到坐標(250,150)上。這個坐標是圖片中心的位置。因為我們的窗口大小是500x300,所以為了讓圖片居中,就要把圖片的中心放到窗口中心位置上,也就是橫縱坐標各自減半。

最後可以將創建好的背景取個名字叫cv_gb),我們後面可以直接調用這個名字更改背景(如果有必要的話)。

4). 插入游戲標題

然後我們需要寫上“看圖猜成語”的標題。這裡我們可以直接寫文字,但這樣太難看了,於是問哥最後還是決定插入一張圖片。大家可以從網上找到各種各樣的ps字體文件,找一張自己喜歡的,然後把文字換成“看圖猜成語”。由於問哥對ps也不擅長,這裡就不過多介紹。總之,得到一張“title.png”的圖片,
再使用剛才插入背景的方法把這張標題圖片放進畫布。

title = tk.PhotoImage(file=r"images\title.png")
cv_tt = cv.create_image(250,30,image = title)

放入的坐標(250,30)表示橫軸居中(窗口寬度500的一半),縱向從上面向下30個像素。

圖形化界面的坐標總是以左上角為原點(0, 0),橫軸像素向右遞增,縱軸像素向下遞增。

注意: 一定要注意放入的先後順序,如果先放入標題再放入背景,背景圖片就會把標題蓋住了。在畫布上繪制其他組件的時候也是要注意這個“先放的在下面”的順序。

5). 顯示成語圖片

現在我們就可以把准備好的成語圖片,也如法炮制,插入到畫布的合適位置。如何隨機顯示成語圖片,我們在下篇代碼實現的部分再講解。這裡我們就選取第一張圖片放進來。

img = tk.PhotoImage(file=f"images\words\一帆風順.png")
cv_word = cv.create_image(150,120,image = img)

位置坐標是問哥多次調整選擇的最佳位置,大家不用糾結,可以自己改動坐標看看位置怎樣才順眼。

這時我們發現,因為成語圖片是透明的(PNG格式),所以背景圖片也顯示出來了。這樣不一定不好,但容易讓成語圖片裡的元素和背景圖片混淆起來,使得成語看起來不那麼清楚。

於是我們還在在成語圖片的下面,背景圖片的上面,“墊”上一層“布”。而我們可以在Canvas畫布上面繪制一個矩形的圖片,來充當這層“布”的作用。

可以調用Canvas類的create_rectangle方法來繪制這樣一個矩形。

cv.create_rectangle(90,60,210,180,fill='moccasin',outline = '')

下面來講解一下create_rectangle方法的幾個常用參數。

  1. 前面4個數字代表的是矩形的左上角橫縱坐標右下角橫縱坐標。大家注意這與繪制圖片的位置坐標是不同的,圖片的坐標是代表圖片中心位置。而繪制矩形使用左上角與右下角坐標,也就代表著可以隨意改變矩形的大小。這裡為了可以完美地“墊”在成語圖片下面,我們需要繪制一個大小和位置都和成語圖片相同的矩形。所以要做一點小算數。已知成語圖片的大小是120x120,中心坐標是(150, 120),所以橫軸方向150減去120的一半,得到90,縱軸方向120減去120的一半,得到60,就是矩形的左上角的坐標;同理,(150+120/2,120+120/2)就是矩形右下角的坐標。這樣就畫出一個與成語圖片大小和位置都相同的矩形了。
  2. outline參數時矩形邊框的顏色,默認為黑色。問哥是想實現一個“牛皮紙”的效果,所以不需要邊框,這裡就設置為空了。
  3. fill參數表示矩形填充的顏色。這裡可以使用顏色的16進制編碼,也可以使用tkinter內置的一些定義好的顏色名稱。tkinter內置的顏色名稱可以參考下圖:


問哥剛才說過,想要實現一個牛皮紙的效果,所以找了個鹿皮色代替了

記住,剛才說過,畫圖順序很重要,我們要把這一層矩形放在成語圖片和背景圖片之間,所以繪制的順序,也是先畫背景,再畫矩形,最後畫成語圖片。看起來樣子是這樣的:

由於我們後面並不需要改變這個矩形的位置等屬性,所以不用定義一個變量來代表這個矩形。

6). 顯示成語填空處

下面是要在成語圖片下面做幾個填漢字用的空白。因為在這個項目裡,我們不需要玩家使用鍵盤輸入漢字,所以並不需要使用文本框。所以只要繪制四個空白的矩形,預留好漢字的位置。等到後面代碼實現的時候,由玩家選擇按鈕來填充對應的漢字就可以了。

所以我們如法炮制,繪制4個矩形:

cv.create_rectangle(50,210,86,246,fill='ivory')
cv.create_rectangle(100,210,136,246,fill='ivory')
cv.create_rectangle(150,210,186,246,fill='ivory')
cv.create_rectangle(200,210,236,246,fill='ivory')

這些坐標是問哥多次嘗試出來的較為合適的位置,大家也可以自己改動,看看效果如何。

問哥覺得這四個矩形保留邊框比較合適,看起來更像是填空,所以就沒有指定outline參數。而顏色則使用了上面顏色列表裡的象牙白(ivory)。同樣,我們也不需要再定義額外的變量來代表這四個矩形,只需要畫一遍就可以了。

然後我們注意到,其實這四個矩形都是平行的,然後橫向間距相等,所以可以利用一個4次的循環語句,是代碼看起來簡潔一些。如下代碼實現的是相同的效果:

for i in range(4):
cv.create_rectangle(50*i+50,210,50*i+86,246,fill='ivory')

效果是這樣:

至於玩家如何實現選擇漢字插入,我們在下篇再介紹。

7). 插入20個按鈕

對於右邊放置可供玩家選擇的漢字字庫,我們可以使用剛才填空的方法繪制矩形,也可以使用按鈕。因為問哥考慮到後面我們要實現玩家點擊的效果,所以這裡就用tkinter的按鈕來實現了。

由於Canvas自己沒有按鈕,我們要使用tkinter的按鈕組件,聲明方式也和我們開始時聲明一個Canvas畫布組件類似,就是實例化tkinter模塊的Button類。下面這句代碼就是創建一個Button按鈕。

btn = tk.Button(root, font =('方正楷體簡體',11),width=2,relief='flat',background='lightyellow')

現在簡要介紹一下定義Button時的幾個常用參數:

  1. background參數。也可以簡寫為bg,和Canvas的bg參數功能一樣,就是定義按鈕的背景顏色。問哥在這裡選擇了lightyellow,淺黃色。

  2. width寬度。定義按鈕的寬度,這裡的數字並不是以像素為單位,而是必須是整數,且最小為0(負數取絕對值),表示一個單位寬度。如果省去這個參數,按鈕寬度將自動隨著按鈕上的文字大小改變寬度。

  3. relief參數。按鈕的樣式。tkinter自帶的按鈕提供了6種樣式,分別是flat, groove, raised, ridge, solid, 和sunken。默認為raised,也就是我們平時常見的按鈕樣式:

    flat:

    groove:

    ridge:

    solid:

    sunken:

    這裡喜歡那個就用哪個,問哥使用了flat樣式,你也可以選擇更喜歡的。

  4. font字體參數。font參數用於定義按鈕上文字顯示的一些屬性,在這個參數裡我們可以賦值一個元組,裡面是字體的名稱、大小、粗斜體等等。查看電腦裡有哪些字體可以被tkinter調用,可以輸入下面這條命令,注意,一定要先定義一個主窗口,才可以查看字體。

from tkinter import Tk, font
root = Tk()
font.families()

按鈕創建好了,我們要把它們繪制在畫布上。由於Button按鈕是和Canvas同級的類,所以我們要使用Canvas的create_window方法,把按鈕這個組件放在畫布的哪個位置。

btn_window = cv.create_window(300, 75, window=btn)

和剛才放置填空的矩形一樣,我們找到規律以後,就可以使用雙層循環來布置一個5行4列的等距按鈕,代碼如下:

for i in range(4):
for j in range(5):
btn = tk.Button(root, font =('方正楷體簡體',11),width=2,relief='flat',bg='lightyellow')
btn_window = cv.create_window(300+40*i, 75+35*j, window=btn)

效果如圖:

按鈕上的漢字我們先留白,等到下篇代碼實現的時候,再把漢字打亂,顯示在按鈕上。

8). 插入2個功能小按鈕

在20個打亂的漢字下面,我們還想要提供兩個小按鈕,一個使玩家可以擦掉自己選錯的漢字,另一個是可以使電腦提示一個漢字,這種類似作弊的選項。注意:我們並不需要再額外設置一個“提交”按鈕讓電腦來判斷玩家的選擇正確不正確,因為只要玩家選擇了4個漢字,電腦就可以自動來判斷。如果不對,則自動擦掉,讓玩家重新選擇,如果正確,則彈出對話框,詢問是否進入下一關。

關於按鈕的樣式,我們在上一節已經詳細解釋了。可是看來看去,問哥沒有選中心儀的按鈕樣式。比如,如果使用下面代碼,選擇默認的按鈕樣式,按鈕會這樣展現:

btn_clean=tk.Button(root, text='清空', width=5)
btn_submit=tk.Button(root, text='提示', width=5)
cv.create_window(320, 265, window=btn_clean)
cv.create_window(400, 265, window=btn_submit)


這樣並不是不好,但問哥總是覺得看起來不太舒服,容易讓人有那種死板的辦公軟件的感覺(可能是問哥自己的原因 )。所以我們可以使用tkinter的另一個子模塊ttk來創建另一種風格的按鈕。

注意代碼的區別,多了一個字母 t :

from tkinter import ttk
btn_clean=ttk.Button(root, text='清空', width=5)
btn_submit=ttk.Button(root, text='提示', width=5)
cv.create_window(320, 265, window=btn_clean)
cv.create_window(400, 265, window=btn_submit)

ttk子模塊風格的按鈕是這個樣子的:

而且鼠標移到按鈕上面還會變色,使得按鈕靈動許多。

9). 關卡說明

最後一個小細節就是在底部加上關卡說明文字,告訴玩家已經前進了多少關。我們可以直接在Canvas畫布上面創建文字。同時創建一個變量level,用來記錄當前的關卡號。

使用下面的代碼可以把這段文字放在指定位置:

level=1
level_indicator = cv.create_text(150,270,text=f'第 {
level} 關', fill='black', font=('微軟正楷',9,'bold'))

和上面介紹過的Canvas的create_image,create_rectangle,create_window等方法類似,創建文本,我們可以使用create_text的方法。這個方法的幾個常用參數如下:

  1. 坐標:和其他方法裡的坐標一樣,需要注意的是,這個坐標代表的也是文本中心的位置。
  2. fill參數:文字的顏色。
  3. font參數:和其他組件的font參數一樣,指定文本的字體、大小、粗細等。

到此,我們已經完整得把整個游戲界面搭起來了:

雖然它還不能動,不能和玩家交互,但看起來也有那麼點意思了。下篇,問哥會帶著大家一點點把Python代碼加進去,實現游戲背後的邏輯部分。

4. 知識點回顧

本篇雖然沒有講解游戲邏輯的代碼實現部分,但關於如果使用tkinter繪制游戲界面,還是介紹了不少東西,希望大家都能動手試試:

  1. tkinter模塊的引入
  2. Canvas類畫布組件
    2.1 插入圖片方法create_image
    2.2 繪制矩形方法create_rectangle
    2.3 插入tkinter裡其它與Canvas同級的組件方法create_window
    2.4 插入文本方法create_text
  3. Button類按鈕組件
  4. ttk 子模塊的按鈕樣式

總結與思考

希望大家都能自己動手試試,雖然在代碼中問哥已經將坐標設定好,但是在創作的過程中,還是需要反復修改坐標值,不斷進行微調,才能找到令自己滿意的位置。這個過程如同畫畫,還是需要一點耐心的。相比而言,編程語言反而變得次要了些。

而游戲是動態的,需要和玩家交互,所以我們需要用代碼讓這些組件工作起來。下篇內容問哥會帶著大家使用代碼實現這個小項目。

感謝大家讀到這裡,我們下次再見!


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved