我們通過使用pygame模塊,創建一些帶有圖形和聲音的、更有趣的高級遊戲。
目錄
主要內容
以下是主要內容
- 安裝pygame
- pygame中的顏色和字體
- 鋸齒圖像和抗鋸齒圖像
- 屬性
- 數據類型pygame.font.Font、pygame.Surface、pygame.Rect和pygame.PixelArray
- 構造函數
- pygame的繪製函數
- Surface對象的blit()方法
- 事件
pygame
Pygame是一個利用SDL(Simple DirectMedia Layer)庫編寫的遊戲庫,SDL是一位叫做Sam Lantinga的大牛寫的,據說他爲了讓Loki(致力於向Linux上移植Windows的遊戲的一家大好人公司,可惜已經倒閉)更有效的工作,創造了這個東東。
SDL是用C寫的,不過它也可以使用C++進行開發,當然還有很多其它的語言,Pygame就是Python中使用它的一個庫。Pygame已經存在很多時間了,許多優秀的程序員加入其中,把Pygame做得越來越好。
pygame官網提供了不同系統下面安裝的相關命令,都是免費的。筆者選擇的是Windows。當然,在安裝pygame之前,你的Windows裏需要安裝好Python以及配置好環境變量。注意版本要對應上,筆者電腦裏的是64位的Python3.7。
Windows安裝pygame
因爲筆者之前有安裝過anaconda,所以這裏使用anaconda進行安裝。
- 進入https://anaconda.org/,在Search Anaconda Cloud中搜索pygame
2.回車進入頁面後,一般選擇下載最多的打開
3.複製命令行,進入CMD終端運行。
4.完成安裝。可以通過編譯環境檢測是否安裝成功,這裏以IDLE爲例,輸入命令import pygame,出現以下信息,則證明導入成功。
>>> import pygame
pygame 1.9.6
Hello from the pygame community. https://www.pygame.org/contribute.html
這裏僅以anaconda爲例,當然還有其他的安裝方法,pip也是可以的。
使用pygame
pygame有很多的模塊,下面是一些常用的模塊
name | function |
pygame.Color | color representations |
pygame.display | control the display window and screen |
pygame.draw | draw shapes |
pygame.event | interacting with events and queues |
pygame.font | loading and rendering fonts |
pygame.image | image transfer |
pygame.key | work with the keyboard |
pygame.locals | various constants |
pygame.mixer | load and play sounds |
pygame.mouse | pygame.mouse |
pygame.Rect | storing rectangular coordinates |
pygame.surface | representing images |
pygame.time | monitoring time |
pygame.mixer.music | control streamed audio |
有些模塊可能在某些平臺上不存在,你可以用None來測試一下。
import pygame
if pygame.Color is None:
print('This module is None')
else:
print('This module is in the module')
如果存在的話會有以下運行結果
pygame 1.9.6
Hello from the pygame community. https://www.pygame.org/contribute.html
This module is in the module
接下來我們將會用pygame寫第一個Hello World程序。
pygame Hello World
我們將分析pygame Hello World程序的代碼,看看它們做些什麼事情。
(一)導入模塊
import pygame
import sys
from pygame.locals import *
最後一行導入了pygame.locals模塊。這個模塊包含了許多將要用在pygame中的常量。
如果在程序中使用from sys import *語句,而不是使用import sys導入語句,那麼在代碼中要使用exit()調用該函數,而不是使用sys.exit()。但是大多數時候,使用完整的函數名稱會更好,因爲這樣可以知道該函數是在哪個模塊中。
(二)初始化pygame
# Set up pygame.
pygame.init()
在導入了pygame模塊後,並且在調用任何其他的pygame函數之前,所有pygame程序都必須先調用pygame.init()函數。
(三)設置pygame窗口
# Set up the window.
windowSurface = pygame.display.set_mode((500, 400), 0, 32)
pygame.display.set_caption('Hello world!')
通過調用pygame.display模塊中的set_mode()方法,創建了一個圖形化用戶界面(graphical user interface, GUI)。display模塊是pygame模塊之中的一個模塊。這些方法設置了一個窗口,供pygame在其中運行。窗口使用一個座標系統,但是該窗口的座標系統是以像素爲單位的。
像素是計算機屏幕上的最小的點。屏幕上的單個像素,可以以任何的顏色顯示。像素是指由圖像的小方格組成的,這些小方塊都有一個明確的位置和被分配的色彩數值,小方格顏色和位置就決定該圖像所呈現出來的樣子。
這裏我們使用一個元組,創建了一個500像素寬和400像素高的一個窗口。關於元組的知識,參見...???
pygame.display.set_mode()方法有三個參數,第一個參數就是包含兩個整數的一個元組,這兩個整數分別代表窗口的寬度和高度,以像素爲單位。第二個和第三個參數???
pygame模塊中的display模塊中的set_caption()方法用來設置窗口標題。
Surface對象
set_mode()函數返回了一個pygame.Surface對象。對象(object)是對擁有方法的數據類型的值的另外一種叫法。例如,字符串就是Python中的對象,因爲它們有數據(字符串本身)和方法(諸如lower()和split())。Surface對象表示這個窗口。
變量存儲對對象的引用,就像它們存儲了對列表和字典的引用一樣。
(四)設置顏色變量
像素有三種主要的:紅色、綠色和藍色。通過組合這三種顏色的不同數量(計算機屏幕就是這麼做的),你可以形成任何其他顏色。在pygame中,顏色由三個整數的元組表示。這些被稱爲RGB顏色值,我們將在程序中使用它們爲像素指定顏色。由於我們不希望每次在程序中使用特定顏色時都重寫一個三位數的元組,因此我們將創建常量來保存以元組表示的顏色命名的元組:
# Set up the colors.
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
元組中的第1個值表示顏色中有多少紅色。整數值0表示該顏色中沒有紅色,而225表示該顏色中紅色達到最大值。第二個值表示綠色,第三個值表示藍色。這三個整數值構成了一個RGB元組。例如:
(0,0,0)表示沒有紅色、綠色和藍色。最終的顏色是完全的黑色;
(255,255,255)表示紅色、綠色和藍色都達到最大值,最終得到的顏色就是白色。
常見的顏色及其RGB值如下表:
顏色 | RGB值 |
黑色 | (0,0,0) |
藍色 | (0,0,255) |
灰色 | (128,128,128) |
綠色 | (0,128,0) |
紫色 | (128,0,128) |
紅色 | (255,0,0) |
黃色 | (255,255,0) |
(五)將文本寫到pygame窗口上
將文本寫到一個窗口之上,和我們在基於文本的遊戲中只是使用print()函數有點不同。爲了將文本寫道窗口上,我們需要先做一些設置。
使用字體來樣式化文本
字體是以統一風格繪製的一整套的字母、數字、符號和字符。
在之前的遊戲中,我們只告訴Python打印文本。用於顯示文本的顏色、大小和字體,則完全取決於操作系統。Python程序不能修改字體。但是,pygame可以以計算機上的任何字體來繪製文本。
# Set up the fonts.
basicFont = pygame.font.SysFont(None, 48)
這裏使用pygame.font.SysFont()函數以創建一個pygame.font.Font對象。
這個函數包含兩個參數。第一個參數是字體的名稱,但是我們將傳遞None值以使用默認的系統字體。第二個參數是字體的大小(以點爲單位)。生成一幅文本的字母圖像,這叫做渲染(rendering)。
渲染一個Font對象
存儲在變量basicFont中的Font對象有一個叫做render()的方法。這個方法將返回一個Surface對象,文本就繪製於其上。render()的第一個參數是要繪製的文本的字符串。第二個參數指定是否想要抗鋸齒的一個Boolean值。爲文本抗鋸齒,會使其看上去略微平滑一些。
第3個參數和第4個參數都是RGB元組。第三個參數是將要用來渲染文本的顏色(在這個例子中是白色),並且第4個參數是文本後的背景顏色(藍色)。我們將這個Font對象賦值給變量text。
# Set up the text.
text = basicFont.render('Hello world!', True, WHITE, BLUE)
我們需要將設置好的Font對象放置到窗口上的一個位置之中。
使用Rect屬性設置文本位置
pygame.Rect數據類型(簡稱Rect)表示特定大小和位置的矩形區域。我們就是使用它來設置窗口中的對象的位置。
調用函數pygame.Rect()來創建一個新的Rect對象。注意,pygame.Rect()函數和pygame.Rect數據類型的名稱相同。與其數據類型的名稱相同並且創建其數據類型的對象或值的函數,叫做構造函數(constructor functions)。
pygame.Rect()函數的參數是表示左上角的X座標和Y座標的整數,後邊跟隨着寬度和高度,都是以像素爲單位。帶有參數的這個函數看上去是這樣的:pygame.Rect(left, top, width, height)。當我們創建了Font對象,就已經爲其生成了一個Rect對象,因此,我們現在要做的就是訪問它。我們在text上使用get_rect()方法,將該Rect賦值給textRect:
textRect = text.get_rect()
textRect.centerx = windowSurface.get_rect().centerx
textRect.centery = windowSurface.get_rect().centery
就像方法是與對象相關的函數一樣,屬性(attribute)是與對象相關的變量。Rect數據類型有許多屬性,用來描述它所表示的矩形。爲了設置textRect在窗口上的位置,我們需要將其中心的x值和y值,賦值爲窗口上的座標像素。由於每個Rect對象已經有了存儲Rect的中心的x座標和y座標的屬性,分別名爲centerx和centery,我們所需要做的就是賦值這些座標值。
我們需要將textRect放置到窗口的中央,因此,我們需要獲取windowsSurfaceRect,獲取其centerx和centery屬性,然後將其賦值給textRect的centerx和centery屬性。
Rect對象的好處是,如果修改了這些屬性中的任意一個,所有其他屬性也將自動修改。
在pygame模塊中,是font和surface模塊,這些模塊中,是Font和Surface數據類型。pygame程序員用小寫字母大頭表示模塊,用大寫字母開頭表示數據類型,以便於區分數據類型和模塊。注意,Font對象(存儲在text變量中)和Surface對象(存儲在WindowsSurface變量中)都有一個名爲get_rect()的方法。從技術上講,這是兩種不同的方法,但是pygame的程序員給它們起了相同的名字,因爲它們都做了相同的事情,並且分別返回表示Font對象或Surface對象的大小和位置的Rect對象,所以pygame程序員給予他們相同的名稱。
(六)用一種顏色填充一個Surface對象
對於我們的程序來說,我們想要用白色來填充存儲在windowsSurface變量中的整個Surface對象。fill()函數將會使用傳遞給參數的顏色來填充整個Surface對象。
# Draw the white background onto the surface.
windowSurface.fill(WHITE)
注意,在pygame中,當調用fill()方法或其他任何繪製函數時,屏幕上的窗口都不會改變。相反,這些函數將會改變Surface對象,但是在調用pygame.display.update()函數之前,不會把新的Surface對象繪製到屏幕上。這是因爲,在計算機內存中修改Surface對象要比在屏幕上修改圖像塊。當所有繪製函數完成了對Surface對象的繪製之後,再在屏幕上繪製,這樣要高效的很多。
(七)pygame的繪製函數
以上內容實現了使用一種顏色填充一個pygame窗口並添加文本,但是pygame還擁有讓你能夠繪製形狀和線條的函數。每一種形狀都有其自己的函數,並且可以將這些形狀組合到不同的圖片中,以用於圖形化的遊戲。這裏主要用到pygame模塊中的draw模塊。
繪製一個多邊形
pygame.draw.polygon()函數可以繪製你所指定的任意多邊形,該函數的參數依次爲:polygon(surface, color, points, width=0)
- 要在其上繪製多邊形的Surface對象;
- 多邊形的顏色;
- 由要依次繪製的點的XY座標的元組所構成的一個元組。最後一個元組將自動連接到第一個元組以完成該形狀;
- 可選項,表示多邊形邊線條的寬度的整數值。沒有這個選項的話,多邊形將會填充。
# Draw a green polygon onto the surface.
pygame.draw.polygon(windowSurface, GREEN, ((146, 0), (291, 106),
(236, 277), (56, 277), (0, 106)))
以上代碼在Surface對象上繪製了一個綠色的五角星。
繪製直線
pygame.draw.line()函數只是從屏幕上的一點到另一點繪製一條直線。pygame.draw.line()參數依次是:
line(surface, color, start_pos, end_pos, width=1)
- 要在其上繪製直線的Surface對象;
- 直線的顏色;
- 包含直線的一端的XY座標的兩個整數的一個元組;
- 包含直線的另一端的XY座標的兩個整數的一個元組;
- 可選項,表示線條寬度的整數值,默認是1.
# Draw some blue lines onto the surface.
pygame.draw.line(windowSurface, BLUE, (60, 60), (120, 60), 4)
pygame.draw.line(windowSurface, BLUE, (120, 60), (60, 120))
pygame.draw.line(windowSurface, BLUE, (60, 120), (120, 120), 4)
以上代碼調用了三次pygame.draw.line()函數,在Surface對象上繪製一個藍色的“Z”。
繪製圓形
pygame.draw.circle()函數在Surface對象上繪製圓形。其參數如下:circle(surface, color, center, radius, width=0)
- 要在其上繪製圓的Surface對象;
- 圓的顏色;
- 表示圓心的XY座標的兩個整數的一個元組;
- 表示圓的半徑(也就是大小)的整數值;
- 可選項,表示線條的寬度的一個整數值。寬度值爲0表示填充圓。
# Draw a blue circle onto the surface.
pygame.draw.circle(windowSurface, BLUE, (300, 50), 20, 0)
以上代碼繪製了一個藍色的圓。
繪製橢圓形
pygame.draw.elipse()函數與pygame.draw.circle()函數類似,但是它繪製一個橢圓形,這就像是一個壓扁了的圓形。pygame.draw.elipse()函數依次如下:ellipse(surface, color, rect, width=0)
- 要在其上繪製橢圓的Surface對象;
- 橢圓的顏色;
- 傳遞分別表示橢圓的左上角的X和Y座標以及橢圓的寬和高的4個整數的一個元組;
- 可選項,表示寬度的整數值。寬度值爲0,表示填充橢圓。
# Draw a red ellipse onto the surface.
pygame.draw.ellipse(windowSurface, RED, (300, 250, 40, 80), 1)
繪製矩形
pygame.draw.rect()函數將繪製一個矩形。pygame.draw.rect()函數的參數依次如下:rect(surface, color, rect, width=0)
- 要在其上繪製矩形Surface的對象;
- 矩形的顏色;
- 第三個參數是包含了表示矩形的左上角的X和Y座標,以及矩形的寬和高的4個整數的一個元組。也可以給第3個參數傳遞一個Rect對象,而不是傳遞4個整數的一個元組。
# Draw the text's background rectangle onto the surface.
pygame.draw.rect(windowSurface, RED, (textRect.left - 20,
textRect.top - 20, textRect.width + 40, textRect.height + 40))
這裏要想起來我們之前創建的一個textRect對象。
給像素着色
pygame.PixelArray對象(簡稱PixelArray對象)。PixelArray對象是顏色元組的一個列表,這些顏色元組表示傳遞給它的Surface對象。PixelArray對象使得我們能夠進行更高的像素級別的控制因此,如果需要在屏幕上繪製非常詳細或定製化圖像,而不只是較大的圖形的時候,PixelArray對象是很好的選擇。
我們將使用PixelArray把windowsSurface上的一個像素變爲黑色。當你運行pygame的Hello World程序的時候,在窗口的右下角可以看到這個像素。
把windowsSurface作爲參數傳遞給pygame.PiexlArray()函數調用,所以下面把BLACK分配給pixArray[480][380],將會把座標爲(480,380)的像素改變爲一個黑色像素。pygame將把這個改變自動更新到windowsSurface對象。
PixelArray對象中的第1個索引是X座標。第2個索引是Y座標。PixelArray對象使得在一個PixelArray對象上單獨像素設置爲特定顏色變得很容易。
# Get a pixel array of the surface.
pixArray = pygame.PixelArray(windowSurface)
pixArray[480][380] = BLACK
del pixArray
注意,從一個Surface對象創建PixelArray對象,將會鎖定這個Surface對象。鎖定意味該Surface對象不能調用blit()函數。要解鎖這個Surface對象,必須使用del操作符刪除PixelArray對象。
如果忘記刪除PixelArray對象,就會得到一條錯誤信息:error: Surfaces must not be locked during blit
(八)Surface對象的blit()方法
bllit()方法將一個Surface對象的內容繪製到另一個Surface對象之上。render()方法所創建的所有文本對象,都存在於自己的Surface對象之上。pygame的繪製方法都能夠指定要在其上繪製一個形狀或線條的Surface對象,但我們的文本存儲在text變量中而不是繪製到windowsSurface上。爲了將text繪製到我們想要讓其出現的Surface上,必須使用blit()方法:
# Draw the text onto the surface.
windowSurface.blit(text, textRect)
這裏將text變量中的'Hello World!'Surface對象繪製到了windowsSurface變量中的Surface對象之上。blit()的第2個參數指定了應該將text surface繪製於windowsSurface上的何處。前面通過調用text.get_rect()而獲取的Rect對象,將作爲這個參數傳遞。
(九)將Surface對象繪製到屏幕上
在調用pygame.display.update()函數之前,並不會真的將任何內容繪製到屏幕上,該函數顯示更新後的Surface對象:
# Draw the window onto the screen.
pygame.display.update()
爲了節省內存,我們並不想要在每次調用了繪圖函數之後,都更新到屏幕上,只有在所有繪製函數都調用完後,纔想要一次性更新屏幕。
(十)事件和遊戲循環
在前面的遊戲中,所有程序都會立即打印每一項內容,知道它們遇到一個input()函數調用。此時,程序會停止,等待用戶輸入一些內容並按下回車鍵。但是,pygame程序不斷地運行叫做遊戲循環(game loop)地一個循環。在這個程序中,遊戲循環中地所有代碼行每秒鐘都會執行100次左右。
遊戲循環是不斷地查看新事件、更新窗口的狀態並在屏幕上繪製窗口的一個循環。任何時候,當用戶按下一個鍵、點擊或移動鼠標或使得其他一些事件發生的時候,pygame都會創建該對象。事件(event)是pygame.event.Event數據類型的對象。
# Run the game loop.
while True:
這裏把while語句的條件設置爲True,所以它會永遠循環。退出循環的唯一方式是,如果有一個事件導致了程序終止。
獲取事件對象
調用pygame.event.get()函數檢索自從上次調用pygame.event.get()後所生成的任何新的pygame.event.Event對象(簡稱爲Event對象)。這些事件會以Event對象的一個列表的形式返回。所有Event對象都有一個叫做type的屬性,它告訴我們事件是什麼類型。在這裏我們只需要使用QUIT類型的事件,當用戶終止程序的時候,該事件會告訴我們。
for event in pygame.event.get():
if event.type == QUIT:
這裏我們使用了一個for循環,遍歷了pygame.event.get()所返回的列表中的每一個Event對象。如果事件的type屬性等於常量QUIT(它位於我們在程序開始處所導入的pygame.locals模塊之中),那麼我們就知道產生了QUIT事件。
當用戶關閉程序的窗口或者當計算機關閉並嘗試終止所有運行的程序的時候,pygame模塊會產生QUIT事件。接下來,當檢測到QUIT事件的時候,我們將告訴程序做什麼。
退出程序
如果已經產生了QUIT事件,程序就應該調用pygame.quit()和sys.exit()。
pygame.quit()
sys.exit()
pygame.quit()函數是和init()相對應的一種函數。在退出程序之前,需要調用它。如果忘記了,可能導致IDLE在程序結束之後掛起。
源代碼:
import pygame
import sys
from pygame.locals import *
# Set up pygame.
pygame.init()
# Set up the window.
windowSurface = pygame.display.set_mode((500, 400), 0, 32)
pygame.display.set_caption('Hello world!')
# Set up the colors.
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
# Set up the fonts.
basicFont = pygame.font.SysFont(None, 48)
# Set up the text.
text = basicFont.render('Hello world!', True, WHITE, BLUE)
textRect = text.get_rect()
textRect.centerx = windowSurface.get_rect().centerx
textRect.centery = windowSurface.get_rect().centery
# Draw the white background onto the surface.
windowSurface.fill(WHITE)
# Draw a green polygon onto the surface.
pygame.draw.polygon(windowSurface, GREEN, ((146, 0), (291, 106),
(236, 277), (56, 277), (0, 106)))
# Draw some blue lines onto the surface.
pygame.draw.line(windowSurface, BLUE, (60, 60), (120, 60), 4)
pygame.draw.line(windowSurface, BLUE, (120, 60), (60, 120))
pygame.draw.line(windowSurface, BLUE, (60, 120), (120, 120), 4)
# Draw a blue circle onto the surface.
pygame.draw.circle(windowSurface, BLUE, (300, 50), 20, 0)
# Draw a red ellipse onto the surface.
pygame.draw.ellipse(windowSurface, RED, (300, 250, 40, 80), 1)
# Draw the text's background rectangle onto the surface.
pygame.draw.rect(windowSurface, RED, (textRect.left - 20,
textRect.top - 20, textRect.width + 40, textRect.height + 40))
# Get a pixel array of the surface.
pixArray = pygame.PixelArray(windowSurface)
pixArray[480][380] = BLACK
del pixArray
# Draw the text onto the surface.
windowSurface.blit(text, textRect)
# Draw the window onto the screen.
pygame.display.update()
# Run the game loop.
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
運行結果:
參考: