前言
GUI ( Graphical User Interface ) 中文稱之爲『圖形用戶界面』。在Python 中,雖有默認的 UI 工具包 Tkinter,但如何與 pygame 融合使用?卻不是簡單易解的事。遊戲中的用戶界面,除了中文輸入外,其他的部份用 pygame 來實作並非難事。既然易用的輪子難尋,索性就自己建造屬於自己的 GUI 系統。
顯示文字
在 pygame 中要顯示文字,代碼如下:
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 480), 0, 32)
my_font = pygame.font.Font("simsun.ttc", 64)
text_surface = my_font.render(u"你好!", True, (0, 0, 255))
# 遊戲主循環
while True:
for event in pygame.event.get():
if event.type == QUIT:
exit()
screen.blit(text_surface, (100, 100))
pygame.display.update()
首先要使用 pygame.font.Font() 創建一個 Font 對象,然後利用 Font 對象的 render 方法創建一個 Surface 對象,其上面就有繪製的文本。最後在遊戲的主循環中將此 Surface 對象顯示到屏幕上。
瞭解文字在 pygame 中的顯示方式,就可以設計一個用於顯示文字的控件——Label 控件。
Label 控件
設計一個 Label 類,對應 Label 控件,主要功能就是用來顯示文字。創建時要設定顯示的位置及文字內容,當然使用的字體、大小、字色及底色,也要一併設定。例如:
label_1 = Label(left, top, text, font_info)
其中的 font_info 是一個字典結構,內容如下:
font_info = {}
font_info["font"] = pygame.font.Font('font/msjh.ttc', 36)
font_info["tc"] = (255, 255, 255) # 文字顏色
font_info["bc"] = (0, 0, 0) # 背景顏色
font_info["align"] = 0 # 靠左
font_info["valign"] = 0 # 靠上
render 方法
使用render() 將 Label 控件顯示到屏幕上:
label.render(screen)
render() 代碼如下:
def render(self, surface):
if self.text != "" and self.status >= 0:
surface.blit(self.text_surface, (self.display_x, self.display_y))
如果沒有文字內容或被設定成隱藏(status < 0)就不顯示,否則就顯示在屏幕的 (display_x, display_y) 位置上。創建 Label 時,給的是 ( left, top ) 位置,但因爲文字可能靠左、居中或靠右,所以經過計算後,真正的顯示位置爲 (display_x, display_y)。
set_align 方法
更改文字顯示時,水平的對齊方式,0 表靠左、1 表居中,而 2 就代表靠右。
label.set_align(2)
set_align() 代碼如下:
def set_align(self, align):
w, h = self.__get_size()
if align == 2:
self.display_x = self.left - w
elif align == 1:
self.display_x = self.left - int(w / 2)
else:
self.display_x = self.left
要判斷居中或靠右的位置時,必須知道文字的 surface 對象的大小,所以呼叫 __get_size() 取得其大小,然後再調整 x 座標。此處只處理水平居中及靠右的問題,所以忽略了垂直 y 座標的調整。
def __get_size(self):
if self.text_surface is None:
return (0, 0)
else:
return self.text_surface.get_size() # return (w, h)
set_valign 方法
更改文字顯示時,垂直的對齊方式,0 表靠上、1 表居中,而 2 就代表靠下。
label.set_valign(1)
set_valign() 代碼如下:
def set_valign(self, valign):
w, h = self.get_size()
if valign == 2:
self.display_y = self.pos_y - h
elif valign == 1:
self.display_y = self.pos_y - int(h / 2)
else:
self.display_y = self.pos_y
set_text 方法
用set_text() 方法來更改文字內容。因爲更改文字時會重新建立Surface 對象,所以就可以順便更改文字顏色及背景顏色。
label.set_text(text, tc=None, bc=None)
set_text() 代碼如下:
def set_text(self, text, tc=None, bc=None):
self.text = text
if tc is not None:
self.tc = tc
if bc is not None:
self.bc = bc
self.text_surface = self.font.render(self.text, True, self.tc, self.bc)
如果 tc 或 bc 爲 None,表示使用原來的字色及底色。
set_pos 方法
用 set_pos() 更改顯示的位置:
Label.set_pos(left, top)
set_pos() 代碼如下:
def set_pos(self, left, top, align=None, valign=None):
self.left = left
self.top = top
if align is not None:
self.align = align
if valign is not None:
self.valign = valing
self.set_align(self.align)
self.set_valign(self.valign)
重新設定 (left, top) 後,要再重新依對齊方式計算顯示的 x, y 位置。
set_hide 方法
Label 控件雖然用來顯示文字,但有時必須暫時隱藏不顯示,所以就用 set_hide 方法來設定顯示或隱藏。
label.set_hide(True)
set_hide() 代碼如下:
def set_hide(self, flag):
if flag:
self.status = -1
else:
self.status = 0
使用範例:
將所有的 Label 控件用一個 labels 字典來管理,創建的同時,也給定一個名稱,以便將來個別的管理。例如:
labels = {}
labels["label_1"] = Label(x1, y1, text1, font_info)
labels["label_2"] = Label(x2, y2, text2, font_info)
labels["label_3"] = Label(x3, y3, text3, font_info)
顯示所有的 Label 控件的方式如下:
for label in labels.values():
label.render(screen)
針對某個 label 做處理時,例如:
Labels["label_1"].set_text("重新設定文字內容!")
完整代碼
class Label(object):
def __init__(self, left, top, text, font_info):
self.status = 0 # <0 :- 不顯示。
self.left = left
self.top = top
self.text = text
self.display_x = left
self.display_y = top
if font_info is None:
self.font = pygame.font.Font('font/msjh.ttc', 20)
self.tc = (255, 255, 255)
self.bc = (0, 0, 0)
self.align = 0
self.valign = 0
else:
self.font = font_info["font"] # 字型及字體大小
self.tc = font_info["tc"]
self.bc = font_info["bc"]
self.align = font_info.get("align", 0)
self.valign = font_info.get("valign", 0)
if text == "":
self.text_surface = None
else:
self.set_text(text)
def render(self, surface):
if self.text != "" and self.status >= 0:
surface.blit(self.text_surface, (self.display_x, self.display_y))
def set_align(self, align):
w, h = self.__get_size()
if align == 2:
self.display_x = self.left - w
elif align == 1:
self.display_x = self.left - int(w / 2)
def set_valign(self, valign):
w, h = self.get_size()
if valign == 2:
self.display_y = self.pos_y - h
elif valign == 1:
self.display_y = self.pos_y - int(h / 2)
else:
self.display_y = self.pos_y
def __get_size(self):
if self.text_surface is None:
return (0, 0)
else:
return self.text_surface.get_size() # return (w, h)
def set_text(self, text, tc=None, bc=None):
self.text = text
if tc is not None:
self.tc = tc
if bc is not None:
self.bc = bc
self.text_surface = self.font.render(self.text, True, self.tc, self.bc)
self.set_align(self.align)
self.set_valign(self.valign)
def set_hide(self, flag):
if flag:
self.status = -1
else:
self.status = 0
後語
整個內容設計完全基於當前個人的使用範圍,雖可再擴充、再通用化,只是當前無此需求,就簡單的到此爲止。
自己造輪子有好有壞。好處是隻要考慮到自己當前的需求就好,相對簡單。使用別人的輪子,那還得先弄懂其使用方式,避免掉進坑洞裏。這兩者所花費的時間一比較,就知道該如何取捨?
至於自己造輪子的壞處,網絡上有太多不該重複造輪子的勸說,就不用多說了。
參考:
用Python和Pygame寫遊戲-從入門到精通
作者:目光博客