總結(jié)Python圖形用戶(hù)界面和游戲開(kāi)發(fā)知識(shí)點(diǎn)
基于tkinter模塊的GUI
GUI是圖形用戶(hù)界面的縮寫(xiě),圖形化的用戶(hù)界面對(duì)使用過(guò)計(jì)算機(jī)的人來(lái)說(shuō)應(yīng)該都不陌生,在此也無(wú)需進(jìn)行贅述。Python默認(rèn)的GUI開(kāi)發(fā)模塊是tkinter(在Python 3以前的版本中名為T(mén)kinter),從這個(gè)名字就可以看出它是基于Tk的,Tk是一個(gè)工具包,最初是為T(mén)cl設(shè)計(jì)的,后來(lái)被移植到很多其他的腳本語(yǔ)言中,它提供了跨平臺(tái)的GUI控件。當(dāng)然Tk并不是最新和最好的選擇,也沒(méi)有功能特別強(qiáng)大的GUI控件,事實(shí)上,開(kāi)發(fā)GUI應(yīng)用并不是Python最擅長(zhǎng)的工作,如果真的需要使用Python開(kāi)發(fā)GUI應(yīng)用,wxPython、PyQt、PyGTK等模塊都是不錯(cuò)的選擇。
基本上使用tkinter來(lái)開(kāi)發(fā)GUI應(yīng)用需要以下5個(gè)步驟:
- 導(dǎo)入tkinter模塊中我們需要的東西。
- 創(chuàng)建一個(gè)頂層窗口對(duì)象并用它來(lái)承載整個(gè)GUI應(yīng)用。
- 在頂層窗口對(duì)象上添加GUI組件。
- 通過(guò)代碼將這些GUI組件的功能組織起來(lái)。
- 進(jìn)入主事件循環(huán)(main loop)。
下面的代碼演示了如何使用tkinter做一個(gè)簡(jiǎn)單的GUI應(yīng)用。
import tkinter import tkinter.messagebox def main(): flag = True # 修改標(biāo)簽上的文字 def change_label_text(): nonlocal flag flag = not flag color, msg = ('red', 'Hello, world!')\ if flag else ('blue', 'Goodbye, world!') label.config(text=msg, fg=color) # 確認(rèn)退出 def confirm_to_quit(): if tkinter.messagebox.askokcancel('溫馨提示', '確定要退出嗎?'): top.quit() # 創(chuàng)建頂層窗口 top = tkinter.Tk() # 設(shè)置窗口大小 top.geometry('240x160') # 設(shè)置窗口標(biāo)題 top.title('小游戲') # 創(chuàng)建標(biāo)簽對(duì)象并添加到頂層窗口 label = tkinter.Label(top, text='Hello, world!', font='Arial -32', fg='red') label.pack(expand=1) # 創(chuàng)建一個(gè)裝按鈕的容器 panel = tkinter.Frame(top) # 創(chuàng)建按鈕對(duì)象 指定添加到哪個(gè)容器中 通過(guò)command參數(shù)綁定事件回調(diào)函數(shù) button1 = tkinter.Button(panel, text='修改', command=change_label_text) button1.pack(side='left') button2 = tkinter.Button(panel, text='退出', command=confirm_to_quit) button2.pack(side='right') panel.pack(side='bottom') # 開(kāi)啟主事件循環(huán) tkinter.mainloop() if __name__ == '__main__': main()
需要說(shuō)明的是,GUI應(yīng)用通常是事件驅(qū)動(dòng)式的,之所以要進(jìn)入主事件循環(huán)就是要監(jiān)聽(tīng)鼠標(biāo)、鍵盤(pán)等各種事件的發(fā)生并執(zhí)行對(duì)應(yīng)的代碼對(duì)事件進(jìn)行處理,因?yàn)槭录?huì)持續(xù)的發(fā)生,所以需要這樣的一個(gè)循環(huán)一直運(yùn)行著等待下一個(gè)事件的發(fā)生。另一方面,Tk為控件的擺放提供了三種布局管理器,通過(guò)布局管理器可以對(duì)控件進(jìn)行定位,這三種布局管理器分別是:Placer(開(kāi)發(fā)者提供控件的大小和擺放位置)、Packer(自動(dòng)將控件填充到合適的位置)和Grid(基于網(wǎng)格坐標(biāo)來(lái)擺放控件),此處不進(jìn)行贅述。
使用Pygame進(jìn)行游戲開(kāi)發(fā)
Pygame是一個(gè)開(kāi)源的Python模塊,專(zhuān)門(mén)用于多媒體應(yīng)用(如電子游戲)的開(kāi)發(fā),其中包含對(duì)圖像、聲音、視頻、事件、碰撞等的支持。Pygame建立在SDL的基礎(chǔ)上,SDL是一套跨平臺(tái)的多媒體開(kāi)發(fā)庫(kù),用C語(yǔ)言實(shí)現(xiàn),被廣泛的應(yīng)用于游戲、模擬器、播放器等的開(kāi)發(fā)。而Pygame讓游戲開(kāi)發(fā)者不再被底層語(yǔ)言束縛,可以更多的關(guān)注游戲的功能和邏輯。
下面我們來(lái)完成一個(gè)簡(jiǎn)單的小游戲,游戲的名字叫“大球吃小球”,當(dāng)然完成這個(gè)游戲并不是重點(diǎn),學(xué)會(huì)使用Pygame也不是重點(diǎn),最重要的我們要在這個(gè)過(guò)程中體會(huì)如何使用前面講解的面向?qū)ο蟪绦蛟O(shè)計(jì),學(xué)會(huì)用這種編程思想去解決現(xiàn)實(shí)中的問(wèn)題。
制作游戲窗口
import pygame def main(): # 初始化導(dǎo)入的pygame中的模塊 pygame.init() # 初始化用于顯示的窗口并設(shè)置窗口尺寸 screen = pygame.display.set_mode((800, 600)) # 設(shè)置當(dāng)前窗口的標(biāo)題 pygame.display.set_caption('大球吃小球') running = True # 開(kāi)啟一個(gè)事件循環(huán)處理發(fā)生的事件 while running: # 從消息隊(duì)列中獲取事件并對(duì)事件進(jìn)行處理 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False if __name__ == '__main__': main()
在窗口中繪圖
可以通過(guò)pygame中draw模塊的函數(shù)在窗口上繪圖,可以繪制的圖形包括:線條、矩形、多邊形、圓、橢圓、圓弧等。需要說(shuō)明的是,屏幕坐標(biāo)系是將屏幕左上角設(shè)置為坐標(biāo)原點(diǎn)(0, 0),向右是x軸的正向,向下是y軸的正向,在表示位置或者設(shè)置尺寸的時(shí)候,我們默認(rèn)的單位都是像素。所謂像素就是屏幕上的一個(gè)點(diǎn),你可以用瀏覽圖片的軟件試著將一張圖片放大若干倍,就可以看到這些點(diǎn)。pygame中表示顏色用的是色光三原色表示法,即通過(guò)一個(gè)元組或列表來(lái)指定顏色的RGB值,每個(gè)值都在0~255之間,因?yàn)槭敲糠N原色都用一個(gè)8位(bit)的值來(lái)表示,三種顏色相當(dāng)于一共由24位構(gòu)成,這也就是常說(shuō)的“24位顏色表示法”。
import pygame def main(): # 初始化導(dǎo)入的pygame中的模塊 pygame.init() # 初始化用于顯示的窗口并設(shè)置窗口尺寸 screen = pygame.display.set_mode((800, 600)) # 設(shè)置當(dāng)前窗口的標(biāo)題 pygame.display.set_caption('大球吃小球') # 設(shè)置窗口的背景色(顏色是由紅綠藍(lán)三原色構(gòu)成的元組) screen.fill((242, 242, 242)) # 繪制一個(gè)圓(參數(shù)分別是: 屏幕, 顏色, 圓心位置, 半徑, 0表示填充圓) pygame.draw.circle(screen, (255, 0, 0,), (100, 100), 30, 0) # 刷新當(dāng)前窗口(渲染窗口將繪制的圖像呈現(xiàn)出來(lái)) pygame.display.flip() running = True # 開(kāi)啟一個(gè)事件循環(huán)處理發(fā)生的事件 while running: # 從消息隊(duì)列中獲取事件并對(duì)事件進(jìn)行處理 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False if __name__ == '__main__': main()
加載圖像
如果需要直接加載圖像到窗口上,可以使用pygame中image模塊的函數(shù)來(lái)加載圖像,再通過(guò)之前獲得的窗口對(duì)象的blit方法渲染圖像,代碼如下所示。
import pygame def main(): # 初始化導(dǎo)入的pygame中的模塊 pygame.init() # 初始化用于顯示的窗口并設(shè)置窗口尺寸 screen = pygame.display.set_mode((800, 600)) # 設(shè)置當(dāng)前窗口的標(biāo)題 pygame.display.set_caption('大球吃小球') # 設(shè)置窗口的背景色(顏色是由紅綠藍(lán)三原色構(gòu)成的元組) screen.fill((255, 255, 255)) # 通過(guò)指定的文件名加載圖像 ball_image = pygame.image.load('./res/ball.png') # 在窗口上渲染圖像 screen.blit(ball_image, (50, 50)) # 刷新當(dāng)前窗口(渲染窗口將繪制的圖像呈現(xiàn)出來(lái)) pygame.display.flip() running = True # 開(kāi)啟一個(gè)事件循環(huán)處理發(fā)生的事件 while running: # 從消息隊(duì)列中獲取事件并對(duì)事件進(jìn)行處理 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False if __name__ == '__main__': main()
實(shí)現(xiàn)動(dòng)畫(huà)效果
說(shuō)到動(dòng)畫(huà)這個(gè)詞大家都不會(huì)陌生,事實(shí)上要實(shí)現(xiàn)動(dòng)畫(huà)效果,本身的原理也非常簡(jiǎn)單,就是將不連續(xù)的圖片連續(xù)的播放,只要每秒鐘達(dá)到了一定的幀數(shù),那么就可以做出比較流暢的動(dòng)畫(huà)效果。如果要讓上面代碼中的小球動(dòng)起來(lái),可以將小球的位置用變量來(lái)表示,并在循環(huán)中修改小球的位置再刷新整個(gè)窗口即可。
import pygame def main(): # 初始化導(dǎo)入的pygame中的模塊 pygame.init() # 初始化用于顯示的窗口并設(shè)置窗口尺寸 screen = pygame.display.set_mode((800, 600)) # 設(shè)置當(dāng)前窗口的標(biāo)題 pygame.display.set_caption('大球吃小球') # 定義變量來(lái)表示小球在屏幕上的位置 x, y = 50, 50 running = True # 開(kāi)啟一個(gè)事件循環(huán)處理發(fā)生的事件 while running: # 從消息隊(duì)列中獲取事件并對(duì)事件進(jìn)行處理 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False screen.fill((255, 255, 255)) pygame.draw.circle(screen, (255, 0, 0,), (x, y), 30, 0) pygame.display.flip() # 每隔50毫秒就改變小球的位置再刷新窗口 pygame.time.delay(50) x, y = x + 5, y + 5 if __name__ == '__main__': main()
碰撞檢測(cè)
通常一個(gè)游戲中會(huì)有很多對(duì)象出現(xiàn),而這些對(duì)象之間的“碰撞”在所難免,比如炮彈擊中了飛機(jī)、箱子撞到了地面等。碰撞檢測(cè)在絕大多數(shù)的游戲中都是一個(gè)必須得處理的至關(guān)重要的問(wèn)題,pygame的sprite(動(dòng)畫(huà)精靈)模塊就提供了對(duì)碰撞檢測(cè)的支持,這里我們暫時(shí)不介紹sprite模塊提供的功能,因?yàn)橐獧z測(cè)兩個(gè)小球有沒(méi)有碰撞其實(shí)非常簡(jiǎn)單,只需要檢查球心的距離有沒(méi)有小于兩個(gè)球的半徑之和。為了制造出更多的小球,我們可以通過(guò)對(duì)鼠標(biāo)事件的處理,在點(diǎn)擊鼠標(biāo)的位置創(chuàng)建顏色、大小和移動(dòng)速度都隨機(jī)的小球,當(dāng)然要做到這一點(diǎn),我們可以把之前學(xué)習(xí)到的面向?qū)ο蟮闹R(shí)應(yīng)用起來(lái)。
from enum import Enum, unique from math import sqrt from random import randint import pygame @unique class Color(Enum): """顏色""" RED = (255, 0, 0) GREEN = (0, 255, 0) BLUE = (0, 0, 255) BLACK = (0, 0, 0) WHITE = (255, 255, 255) GRAY = (242, 242, 242) @staticmethod def random_color(): """獲得隨機(jī)顏色""" r = randint(0, 255) g = randint(0, 255) b = randint(0, 255) return (r, g, b) class Ball(object): """球""" def __init__(self, x, y, radius, sx, sy, color=Color.RED): """初始化方法""" self.x = x self.y = y self.radius = radius self.sx = sx self.sy = sy self.color = color self.alive = True def move(self, screen): """移動(dòng)""" self.x += self.sx self.y += self.sy if self.x - self.radius <= 0 or \ self.x + self.radius >= screen.get_width(): self.sx = -self.sx if self.y - self.radius <= 0 or \ self.y + self.radius >= screen.get_height(): self.sy = -self.sy def eat(self, other): """吃其他球""" if self.alive and other.alive and self != other: dx, dy = self.x - other.x, self.y - other.y distance = sqrt(dx ** 2 + dy ** 2) if distance < self.radius + other.radius \ and self.radius > other.radius: other.alive = False self.radius = self.radius + int(other.radius * 0.146) def draw(self, screen): """在窗口上繪制球""" pygame.draw.circle(screen, self.color, (self.x, self.y), self.radius, 0)
事件處理
可以在事件循環(huán)中對(duì)鼠標(biāo)事件進(jìn)行處理,通過(guò)事件對(duì)象的type屬性可以判定事件類(lèi)型,再通過(guò)pos屬性就可以獲得鼠標(biāo)點(diǎn)擊的位置。如果要處理鍵盤(pán)事件也是在這個(gè)地方,做法與處理鼠標(biāo)事件類(lèi)似。
def main(): # 定義用來(lái)裝所有球的容器 balls = [] # 初始化導(dǎo)入的pygame中的模塊 pygame.init() # 初始化用于顯示的窗口并設(shè)置窗口尺寸 screen = pygame.display.set_mode((800, 600)) # 設(shè)置當(dāng)前窗口的標(biāo)題 pygame.display.set_caption('大球吃小球') running = True # 開(kāi)啟一個(gè)事件循環(huán)處理發(fā)生的事件 while running: # 從消息隊(duì)列中獲取事件并對(duì)事件進(jìn)行處理 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False # 處理鼠標(biāo)事件的代碼 if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: # 獲得點(diǎn)擊鼠標(biāo)的位置 x, y = event.pos radius = randint(10, 100) sx, sy = randint(-10, 10), randint(-10, 10) color = Color.random_color() # 在點(diǎn)擊鼠標(biāo)的位置創(chuàng)建一個(gè)球(大小、速度和顏色隨機(jī)) ball = Ball(x, y, radius, sx, sy, color) # 將球添加到列表容器中 balls.append(ball) screen.fill((255, 255, 255)) # 取出容器中的球 如果沒(méi)被吃掉就繪制 被吃掉了就移除 for ball in balls: if ball.alive: ball.draw(screen) else: balls.remove(ball) pygame.display.flip() # 每隔50毫秒就改變球的位置再刷新窗口 pygame.time.delay(50) for ball in balls: ball.move(screen) # 檢查球有沒(méi)有吃到其他的球 for other in balls: ball.eat(other) if __name__ == '__main__': main()
上面的兩段代碼合在一起,我們就完成了“大球吃小球”的游戲(如下圖所示),準(zhǔn)確的說(shuō)它算不上一個(gè)游戲,但是做一個(gè)小游戲的基本知識(shí)我們已經(jīng)通過(guò)這個(gè)例子告訴大家了,有了這些知識(shí)已經(jīng)可以開(kāi)始你的小游戲開(kāi)發(fā)之旅了。其實(shí)上面的代碼中還有很多值得改進(jìn)的地方,比如刷新窗口以及讓球移動(dòng)起來(lái)的代碼并不應(yīng)該放在事件循環(huán)中,等學(xué)習(xí)了多線程的知識(shí)后,用一個(gè)后臺(tái)線程來(lái)處理這些事可能是更好的選擇。如果希望獲得更好的用戶(hù)體驗(yàn),我們還可以在游戲中加入背景音樂(lè)以及在球與球發(fā)生碰撞時(shí)播放音效,利用pygame的mixer和music模塊,我們可以很容易的做到這一點(diǎn),大家可以自行了解這方面的知識(shí)。事實(shí)上,想了解更多的關(guān)于pygame的知識(shí),最好的教程是pygame的官方網(wǎng)站,如果英語(yǔ)沒(méi)毛病就可以趕緊去看看啦。 如果想開(kāi)發(fā)3D游戲,pygame就顯得力不從心了,對(duì)3D游戲開(kāi)發(fā)如果有興趣的讀者不妨看看Panda3D。
相關(guān)文章
python pytesseract庫(kù)的實(shí)例用法
在本篇文章里小編給大家整理的是一篇關(guān)于python pytesseract庫(kù)的實(shí)例用法,有需要的朋友們可以學(xué)習(xí)參考下。2021-07-07Python人工智能學(xué)習(xí)PyTorch實(shí)現(xiàn)WGAN示例詳解
這篇文章主要為大家介紹了人工智能學(xué)習(xí)PyTorch實(shí)現(xiàn)WGAN的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2021-11-11python3 中時(shí)間戳、時(shí)間、日期的轉(zhuǎn)換和加減操作
這篇文章主要介紹了python3 中時(shí)間戳、時(shí)間、日期的轉(zhuǎn)換和加減操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-07-07python flask基于cookie和session來(lái)實(shí)現(xiàn)會(huì)話(huà)控制的實(shí)戰(zhàn)代碼
所謂的會(huì)話(huà)(session),就是客戶(hù)端瀏覽器和服務(wù)端網(wǎng)站之間一次完整的交互過(guò)程,本文介紹falsk通過(guò)cookie和session來(lái)控制http會(huì)話(huà)的全部解析,通常我們可以用cookie和session來(lái)保持用戶(hù)登錄等,感興趣的朋友一起看看吧2024-03-03ORM Django 終端打印 SQL 語(yǔ)句實(shí)現(xiàn)解析
這篇文章主要介紹了ORM Django 終端打印 SQL 語(yǔ)句實(shí)現(xiàn)解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-08-08如何用python實(shí)現(xiàn)結(jié)構(gòu)體數(shù)組
這篇文章主要介紹了如何用python實(shí)現(xiàn)結(jié)構(gòu)體數(shù)組,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05Python 3實(shí)戰(zhàn)爬蟲(chóng)之爬取京東圖書(shū)的圖片詳解
最近在學(xué)習(xí)python3,下面這篇文章主要給大家介紹了關(guān)于Python3實(shí)戰(zhàn)爬蟲(chóng)之爬取京東圖書(shū)圖片的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-10-10Python獲取SQLite查詢(xún)結(jié)果表列名的方法
這篇文章主要介紹了Python獲取SQLite查詢(xún)結(jié)果表列名的方法,涉及Python連接及查詢(xún)SQLite數(shù)據(jù)庫(kù)的相關(guān)操作技巧,需要的朋友可以參考下2017-06-06