大家好,又見面了,經過前幾篇文章的連載,相信大家再通過自學,一定都掌握了Python的基礎知識了吧。 今天開始,問哥開始帶著大家逐步向圖形化界面前進了。
說實話,玩游戲,更多的時候還是需要圖形化界面的,這都2022年了,文字版游戲的年代早過去了。雖然問哥也無比懷念滿屏黑底花花綠綠的文字刷屏的文字MUD年代,但也不得不承認,圖形化的游戲,甚至3D游戲,才能算得上游戲。所以,GUI編程不可避免。
大家也不用擔心,其實我們之前學的Python知識已經足夠了,而GUI是通過其他的模塊來實現的。比如Python內置的Tkinter,Turtle,第三方模塊Pygame和PyZero(Pygame簡化版),PyQT5等等等等。這些模塊各有各的特點和用法,如果把Python比喻成顏料,那這些模塊可以說是筆、圓規和直尺。沒有辦法只用一套規則就可以掌握所有工具。
但是這些工具各有所長,比如Pygame就是專門用來開發游戲的,所以我們也不需要全部掌握。不過考慮到Tkinter是Python自帶的圖形化工具,不需要另外安裝。雖然功能有限,還是可以開發一些有趣的小程序。問哥決定還是先用Tkinter做幾個游戲,再開始Pygame的學習。
首先想到的就是看圖猜成語,實現起來比較簡單,又可以了解到Tkinter的工作方式,還會接觸到一點點面向對象的知識。不過礙於篇幅,問哥還是打算分成上下篇:
上篇 —— 游戲界面的搭建
下篇 —— 後台程序的實現
看圖猜成語大家都玩過吧。規則很簡單:打開程序,自動會展示出一張形容成語的圖片,然後有若干個迷惑性的漢字。玩家要從裡面找到正確的漢字組成成語,成功了即進入下一關。
游戲截圖:
GUI編程有些時候流程圖會比較難畫,因為鍵盤敲入、鼠標點擊或移動等事件的執行往往不是順序化的。但是如果能夠把流程圖畫出來,即使很簡單的流程圖,一樣會讓我們程序實現起來更加容易和清晰。
看圖猜成語的簡易流程圖如下:
編寫GUI游戲程序和純文本程序有很多不同,其中我認為比較重要的是,游戲的外觀設計所占的重要程度大大提高了。一款好的游戲,一定首先是要讓人看起來有想要玩的欲望。所以好的UI設計才那麼值錢。雖然獨立程序猿很難身兼數職,既寫代碼,又搞美工,但至少可以讓畫出來的GUI界面簡潔整齊一些,功能清楚一些。多嘗試,至少要先找到自己看著舒服的排版最重要。
拿這個小項目為例,分析游戲的截圖,可以看到游戲界面主要有以下7個部分:
接下來我們先一步步把這個框架搭出來,程序實現的部分留到後面再改。
首先我們要有一個游戲窗口,不然上面說的這些素材也沒地方放。所以我們可以使用tkinter模塊先畫出游戲窗口。
別忘記要先引用tikinter模塊
import tkinter as tk # 引入tkinter模塊並簡寫成tk
root = tk.Tk() # 定義一個主窗口
root.geometry("500x300") # 主窗口的尺寸
root.resizable(0,0) # 主窗口不可改變大小
root.title('看圖猜成語') # 主窗口的標題
root.mainloop() # 主窗口循環展示
有了上面這幾條代碼,我們就可以在電腦上畫出一個寬500像素,高300像素的窗口了。
大家唯一要注意的是代碼格式:
接下來我們就要像這張空白的窗口裡添加元素了。
雖然有很多種方法可以把圖片插入到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()
在這之前,我們先在畫布上放置其他組件
首先找到一張背景圖片。問哥是在網上搜的,大家也可以隨便找一張自己喜歡的圖片做背景。
需要指出的是,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),我們後面可以直接調用這個名字更改背景(如果有必要的話)。
然後我們需要寫上“看圖猜成語”的標題。這裡我們可以直接寫文字,但這樣太難看了,於是問哥最後還是決定插入一張圖片。大家可以從網上找到各種各樣的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),橫軸像素向右遞增,縱軸像素向下遞增。
注意: 一定要注意放入的先後順序,如果先放入標題再放入背景,背景圖片就會把標題蓋住了。在畫布上繪制其他組件的時候也是要注意這個“先放的在下面”的順序。
現在我們就可以把准備好的成語圖片,也如法炮制,插入到畫布的合適位置。如何隨機顯示成語圖片,我們在下篇代碼實現的部分再講解。這裡我們就選取第一張圖片放進來。
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方法的幾個常用參數。
問哥剛才說過,想要實現一個牛皮紙的效果,所以找了個鹿皮色代替了
記住,剛才說過,畫圖順序很重要,我們要把這一層矩形放在成語圖片和背景圖片之間,所以繪制的順序,也是先畫背景,再畫矩形,最後畫成語圖片。看起來樣子是這樣的:
由於我們後面並不需要改變這個矩形的位置等屬性,所以不用定義一個變量來代表這個矩形。
下面是要在成語圖片下面做幾個填漢字用的空白。因為在這個項目裡,我們不需要玩家使用鍵盤輸入漢字,所以並不需要使用文本框。所以只要繪制四個空白的矩形,預留好漢字的位置。等到後面代碼實現的時候,由玩家選擇按鈕來填充對應的漢字就可以了。
所以我們如法炮制,繪制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')
效果是這樣:
至於玩家如何實現選擇漢字插入,我們在下篇再介紹。
對於右邊放置可供玩家選擇的漢字字庫,我們可以使用剛才填空的方法繪制矩形,也可以使用按鈕。因為問哥考慮到後面我們要實現玩家點擊的效果,所以這裡就用tkinter的按鈕來實現了。
由於Canvas自己沒有按鈕,我們要使用tkinter的按鈕組件,聲明方式也和我們開始時聲明一個Canvas畫布組件類似,就是實例化tkinter模塊的Button類。下面這句代碼就是創建一個Button按鈕。
btn = tk.Button(root, font =('方正楷體簡體',11),width=2,relief='flat',background='lightyellow')
現在簡要介紹一下定義Button時的幾個常用參數:
background參數。也可以簡寫為bg,和Canvas的bg參數功能一樣,就是定義按鈕的背景顏色。問哥在這裡選擇了lightyellow,淺黃色。
width寬度。定義按鈕的寬度,這裡的數字並不是以像素為單位,而是必須是整數,且最小為0(負數取絕對值),表示一個單位寬度。如果省去這個參數,按鈕寬度將自動隨著按鈕上的文字大小改變寬度。
relief參數。按鈕的樣式。tkinter自帶的按鈕提供了6種樣式,分別是flat, groove, raised, ridge, solid, 和sunken。默認為raised,也就是我們平時常見的按鈕樣式:
flat:
groove:
ridge:
solid:
sunken:
這裡喜歡那個就用哪個,問哥使用了flat樣式,你也可以選擇更喜歡的。
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)
效果如圖:
按鈕上的漢字我們先留白,等到下篇代碼實現的時候,再把漢字打亂,顯示在按鈕上。
在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子模塊風格的按鈕是這個樣子的:
而且鼠標移到按鈕上面還會變色,使得按鈕靈動許多。
最後一個小細節就是在底部加上關卡說明文字,告訴玩家已經前進了多少關。我們可以直接在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的方法。這個方法的幾個常用參數如下:
到此,我們已經完整得把整個游戲界面搭起來了:
雖然它還不能動,不能和玩家交互,但看起來也有那麼點意思了。下篇,問哥會帶著大家一點點把Python代碼加進去,實現游戲背後的邏輯部分。
本篇雖然沒有講解游戲邏輯的代碼實現部分,但關於如果使用tkinter繪制游戲界面,還是介紹了不少東西,希望大家都能動手試試:
希望大家都能自己動手試試,雖然在代碼中問哥已經將坐標設定好,但是在創作的過程中,還是需要反復修改坐標值,不斷進行微調,才能找到令自己滿意的位置。這個過程如同畫畫,還是需要一點耐心的。相比而言,編程語言反而變得次要了些。
而游戲是動態的,需要和玩家交互,所以我們需要用代碼讓這些組件工作起來。下篇內容問哥會帶著大家使用代碼實現這個小項目。
感謝大家讀到這裡,我們下次再見!
Catalog One 、 Download and in