Pygame 入門基本指南
最近正在利用 Python 製作一個小遊戲,但對於 Pygame 不熟悉,故在學習的過程記錄相關知識點
Pygame 中文文件下載:Here
Pygame第1-1課:入門
什麼是Pygame?
Pygame是一個“遊戲開發庫” - 一組幫助程式設計師製作遊戲的程式碼庫。包含:
- 圖形和動畫
- 聲音(包括音樂)
- 控制(鍵盤,滑鼠,遊戲手柄等)
Pygame安裝
-
pip
安裝pip install pygame
如果下載緩慢或者下載失敗了的話建議切換pip源到國內映象,如何更換?
-
PyCharm 安裝
第一步:開啟Pycharm
第二步:點File ->Default Settings->Project Interpreter->點加號
第三步: 搜尋Pygame->Install Package
Pygame遊戲結構框架
每個遊戲的核心都是一個迴圈,將其稱為“遊戲迴圈”。這個迴圈一直在不斷執行,一遍又一遍地完成遊戲工作所需的所有事情。每次迴圈顯示一次遊戲當前畫面,稱為幀。
Pygame遊戲迴圈,主要處理3件事情
1.處理外部輸入(滑鼠點選或鍵盤按下事件)
這意味著遊戲在進行的同時,需要響應與處理使用者的操作---這些可能是鍵盤上的鍵被按下,或滑鼠被點選等事件。
2.更新遊戲物件位置或狀態
如果飛機物件在空中飛行,收到重力作用,自身的位置需要改變。如果兩個物件相互碰撞,則需要爆炸。
3.渲染
此步驟中,在螢幕上重新繪製所有更新位置後的所有遊戲物件。
Pygame時鐘
遊戲迴圈的另一個重要方面是控制整個迴圈的執行速度。遊戲中有個術語叫FPS(Frames Per Second),它代表每秒幀數,也叫幀率。這意味著遊戲迴圈每秒應發生多少次。這很重要,因為我們不希望遊戲執行得太快或太慢。也不希望它在不同的計算機上以不同的速度執行 。
構建Pygame遊戲程式骨架
現在,製作一個簡單的pygame程式,功能是除了開啟一個視窗並運行遊戲迴圈之外什麼都不做。
在程式的開始部分,我們匯入需要的庫併為遊戲設定一些變數:
import pygame import random WIDTH = 360 # 遊戲視窗的寬度 HEIGHT = 480 # 遊戲視窗的高度 FPS = 30 # 幀率
接下來,建立遊戲視窗:
# initialize pygame and create window
pygame.init()
pygame.mixer.init() #聲音初始化
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("My Game")#設定遊戲視窗標題欄文字
clock = pygame.time.Clock()
pygame.init()
是啟動pygame,並“初始化”它的命令。 screen
指的是遊戲螢幕,按照在配置常量中設定的視窗大小建立它。最後,建立了一個,clock
時鐘物件,以便能夠確保遊戲以想要的FPS執行。
讓遊戲迴圈:
# Game Loop
running = True
while running:
# Process input (events)
# Update
# # Draw / Render
這是遊戲迴圈,它是由變數running
控制的迴圈。如果希望遊戲結束,只需要設定running
為False
,迴圈就會結束。接下來用一些基本程式碼填寫每個部分。
渲染/繪製部分
我們將從Draw部分開始。目前還沒有任何遊戲物件,用純色填充螢幕。
影象由畫素組成,這些畫素有3個部分:紅色,綠色和藍色。每個部分點亮多少會決定畫素的顏色,如下所示:
三原色中的每一個可以具有介於0和255之間的值,因此對於三種基色中的每一種,存在256種不同的可能性。以下是一些三種顏色的組合示例:
在程式的頂部定義一些顏色:
# Colors (R, G, B)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
在繪圖部分用黑色填滿螢幕。
# Draw / render
screen.fill(BLACK)
這樣還不夠。更改螢幕上的畫素意味著告訴視訊卡告訴顯示器更改實際畫素。從計算機的角度來看,這是一個非常非常緩慢的過程。因此,如果你在螢幕上繪製了很多東西,那麼繪製它們可能需要很長時間。處理能力差的計算機表現為遊戲執行卡頓。
我們可以通過使用稱為雙緩衝的技術,以巧妙的方式解決這個問題。
想象一下,有一個雙面白板,可以翻轉顯示一側或另一側。前面是顯示器(玩家看到的螢幕),而背面是隱藏的,只有計算機可以“看到”它。每一幀,都在背面(記憶體中)繪製所有圖畫 - 每個角色,每個子彈,每個閃耀的光線等等。然後,當完成後,將白板翻轉並顯示。這意味著每幀只是執行一次白板翻轉的過程。
所有這些都在pygame中自動發生。完成繪圖後,只需要告訴它翻轉白板。命令為flip()
:
# Draw / render
screen.fill(BLACK)
# *after* drawing everything, flip the display
pygame.display.flip()
處理遊戲過程中發生的事件
如果現在嘗試執行該程式,我們會發現遇到了問題:無法關閉視窗!單擊螢幕右上角中的“X”按鈕不起作用。那是因為當用戶點選關閉按鈕時產生了一個事件,我們需要程式監聽該事件,並做出相應處理---退出遊戲。
Pygame內部儲存自上一幀以來發生的所有事件。可以通過下面的程式碼檢查發生了哪些事件
for event in pygame.event.get():
# check for closing window
if event.type == pygame.QUIT:
running = False
Pygame有很多事件。 pygame.QUIT
是單擊“X”按鈕時發生的事件。程式檢查到該事件發生後,將running變數設定成False,從而推出遊戲迴圈,結束遊戲。
輸出文字
pygame支援使用pygame.font物件將文字列印到視窗上。要列印文字的話,首先需要建立一個字型物件,Font的第一個引數為None是告訴pygame獲得系統預設字型,也可以是具體的字型名稱。Font的第二個引數指明字型大小。
myfont = pygame.font.Font(None,60)
文字繪製過程比較耗費時間,常用的做法是先在記憶體中建立文字影象,然後將文本當作一個影象來渲染。
textImage = myfont.render("pygame", True, WHITE)
textImage 物件可以使用screen.blit()來繪製。上面程式碼中的render函式第一個引數是要繪製的文字,第二個引數是啟用抗鋸齒能力,第三個引數是文字的顏色。
下面的程式碼,將剛剛產生文字影象繪製到螢幕的座標(10,100)處。
screen.fill(BLACK)
screen.blit(textImage, (10,100))
pygame.display.flip()
控制FPS
雖然現在還沒有任何東西要放在“更新”部分,但仍然需要設定FPS
來控制遊戲執行速度。可以這樣做:
while running:
# keep loop running at the right speed
clock.tick(FPS)
該tick()
命令告訴pygame一秒迴圈多少次。如果設定FPS
為20,這意味著我們命令遊戲的每個迴圈持續1 / 20(0.05)秒。如果迴圈程式碼(更新,繪圖等)只需要0.03秒,那麼pygame將等待0.02秒。以上是計算機處理比較快的情況。如果電腦比較差,執行緩慢,一秒鐘未必能執行20次迴圈--- 那麼clock.tick(20)就成為一個指導意見。
在螢幕上顯示出FPS
首先,在while迴圈之前定義2個變數
count = 0
start = time.time()
每次迴圈計算出當前的FPS並顯示
計算方法為:迴圈開始前獲得當前系統時間,在每次迴圈中,累加迴圈次數count;同時在每次迴圈時,獲得當前系統時間,那麼從開始迴圈到目前為止流逝的時間(時間差)為:now-start。再用count除以這個時間差,即為FPS(每秒迴圈了多少次)。程式碼如下:
# Update
count+=1
now = time.time()
fps = count/(now-start)
fpsImage = myfont.render(str(fps), True, WHITE)
# Draw / render
screen.fill(BLACK)
screen.blit(fpsImage, (10, 100))
# *after* drawing everything, flip the display
pygame.display.flip()
我們可以修改FPS的值,看看是否介面上輸出的值會跟著變化
FPS = 10
FPS = 100
FPS = 1000
FPS = 10000
我的電腦,將FPS設定成10000時,發現螢幕上列印的是2000,說明,我的電腦配置最多可以支援到1秒中執行迴圈中的程式碼2000次。
最後,為了確保當遊戲迴圈結束時,遊戲視窗正確退出。我們通過將程式碼pygame.quit()
的放到最後一行來實現這一點。
整合到一起
最終程式碼如下所示:
# Pygame template - skeleton for a new pygame project
import time
import pygame
import random
WIDTH = 360
HEIGHT = 480
FPS = 30
# define colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
# initialize pygame and create window
pygame.init()
pygame.mixer.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("My Game")
clock = pygame.time.Clock()
myfont = pygame.font.Font(None,60)
textImage = myfont.render("pygame", True, WHITE)
# Game loop
running = True
count = 0
start = time.time()
while running:
# keep loop running at the right speed
clock.tick(FPS)
# Process input (events)
for event in pygame.event.get():
# check for closing window
if event.type == pygame.QUIT:
running = False
# Update
count+=1
now = time.time()
fps = count/(now-start)
fpsImage = myfont.render(str(fps), True, WHITE)
# Draw / render
screen.fill(BLACK)
screen.blit(fpsImage, (10, 100))
# *after* drawing everything, flip the display
pygame.display.flip()
pygame.quit()
Pygame第1-2課:使用精靈
什麼是精靈?
當您玩任何2D遊戲時,您在螢幕上看到的所有物件都是精靈。精靈可以是動畫的,它們可以由玩家控制,甚至可以互相互動。
將在遊戲迴圈的UPDATE和DRAW部分更新和繪製精靈。你可以想象,如果你的遊戲中有大量的精靈,那麼遊戲迴圈的這些部分可能會變得非常冗長和複雜。幸運的是,Pygame有一個很好的解決方案:精靈組。
精靈組只是精靈的集合,您可以同時對所有精靈進行操作。讓建立一個sprite組來儲存遊戲中的所有精靈:
clock = pygame.time.Clock()
all_sprites = pygame.sprite.Group()
現在可以通過在迴圈中新增以下內容來利用該組:
# Update
all_sprites.update()
# Draw / render
screen.fill(BLACK)
all_sprites.draw(screen)
現在,對於建立的每個精靈,只需確保將它新增到all_sprites
組中,它將自動在螢幕上繪製,並在每次迴圈時更新。
建立一個精靈
現在準備好製作第一個精靈了。在Pygame中,精靈是物件。
首先定義新精靈:
class Player(pygame.sprite.Sprite):
class
告訴Python正在定義一個新物件,它將成為玩家精靈,它的父型別是pygame.sprite.Sprite
,這意味著它將基於Pygame的預定義Sprite
類。
在class Player
定義中需要的第一個函式是特殊__init__()
函式,它定義了在建立此型別的新物件時,需要初始化的一些屬性。每個Pygame精靈都必須擁有兩個屬性: image
和 rect
:
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((50, 50))
self.image.fill(GREEN)
self.rect = self.image.get_rect()
第一行pygame.sprite.Sprite.__init__(self)
是Pygame所必需的 - 它執行內建Sprite
類初始化程式。接下來,定義image
屬性 - 在這種情況下,只是建立一個簡單的50 x 50正方形並用顏色填充它GREEN
。稍後將學習如何讓精靈image
變得更加漂亮,比如角色或宇宙飛船,但現在一個穩固的方塊已經足夠好了。
接下來,必須定義精靈rect
,它是“矩形”的縮寫。在Pygame中使用矩形來跟蹤物件的座標。該get_rect()
命令只是檢視image
並計算將它包圍它的矩形。
可以使用rect
它將精靈放在想要的任何地方在螢幕上。讓從中心開始:
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((50, 50))
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.center = (WIDTH / 2, HEIGHT / 2)
現在已經定義了Player精靈,需要通過建立一個Player類的例項來“生成”(意思是建立)它。還需要確保將精靈新增到all_sprites
組中:
all_sprites = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
現在,如果你執行你的程式,你會看到螢幕中央的綠色方塊。來吧,增加WIDTH
和HEIGHT
設定,這樣你將有足夠的視窗空間,使得精靈在接下來的步驟中移動。
精靈運動
請記住,在遊戲迴圈中,有all_sprites.update()
。這意味著對於組中的每個sprite,Pygame將查詢一個update()
函式並執行它。所以為了讓精靈移動,只需要定義它的更新規則(定義update函式):
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((50, 50))
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.center = (WIDTH / 2, HEIGHT / 2)
def update(self):
self.rect.x += 5
這意味著每次迴圈時,都會將精靈的x座標增加5個畫素。繼續執行它,你會看到精靈向螢幕右側移動,但是會移出視窗:
讓通過使精靈環繞來解決這個問題 - 只要它到達螢幕的右側,就會將它移到左側。看一下矩形rect的結構:
因此,如果rect
螢幕的左邊緣離開螢幕,會將右邊緣設定為0:
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((50, 50))
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.center = (WIDTH / 2, HEIGHT / 2)
def update(self):
self.rect.x += 5
if self.rect.left > WIDTH:
self.rect.right = 0
現在可以看到精靈會一直在螢幕上滾動:
整合到一起
# Pygame sprite Example
import pygame
import random
WIDTH = 800
HEIGHT = 600
FPS = 30
# define colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
class Player(pygame.sprite.Sprite):
# sprite for the Player
def __init__(self):
# this line is required to properly create the sprite
pygame.sprite.Sprite.__init__(self)
# create a plain rectangle for the sprite image
self.image = pygame.Surface((50, 50))
self.image.fill(GREEN)
# find the rectangle that encloses the image
self.rect = self.image.get_rect()
# center the sprite on the screen
self.rect.center = (WIDTH / 2, HEIGHT / 2)
def update(self):
# any code here will happen every time the game loop updates
self.rect.x += 5
if self.rect.left > WIDTH:
self.rect.right = 0
# initialize pygame and create window
pygame.init()
pygame.mixer.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Sprite Example")
clock = pygame.time.Clock()
all_sprites = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
# Game loop
running = True
while running:
# keep loop running at the right speed
clock.tick(FPS)
# Process input (events)
for event in pygame.event.get():
# check for closing window
if event.type == pygame.QUIT:
running = False
# Update
all_sprites.update()
# Draw / render
screen.fill(BLACK)
all_sprites.draw(screen)
# *after* drawing everything, flip the display
pygame.display.flip()
pygame.quit()
Pygame第1-3課:圖片精靈
轉向圖形精靈
彩色矩形很好 - 它們是一個好的開始,並確保你的遊戲基本工作,但遲早你會想要為你的精靈使用一個很酷的宇宙飛船影象或角色。這引出了第一個問題:在哪裡獲得遊戲資源。
獲得圖片資源
當你需要為你的遊戲新增圖片資源時,你有3個選擇:
- 自己製作
- 找一位美工為你製作
- 使用網際網路上已有的圖片資源
在本課中,將使用影象“p1_jump.png”:
管理遊戲資源
首先,需要一個資料夾img來儲存遊戲資源,然後將影象放入其中。
要在遊戲中使用此影象,需要讓Pygame載入圖片檔案,這意味著需要程式知道檔案的位置。根據使用的計算機型別,這可能會有所不同,希望能夠在任何計算機上執行程式,因此需要匯入一個名為os
的Python庫。
import pygame
import random
import os
# set up asset folders
game_folder = os.path.dirname(__file__)
特殊的Python變數__file__
指的是當前程式碼檔案所在的資料夾,函式os.path.dirname會獲得該資料夾的路徑。例如
/Users/chris/Documents/gamedev/tutorials/1-3 sprite example.py
如果使用的是Windows,路徑可能如下所示:
C:\Users\chris\Documents\python\game.py
不同的作業系統使用不同的方式來描述計算機上的位置。通過使用os.path
命令,可以讓計算機找出正確的路徑。
import pygame
import random
import os
# set up asset folders
game_folder = os.path.dirname(__file__)
img_folder = os.path.join(game_folder, 'img')
player_img = pygame.image.load(os.path.join(img_folder, 'p1_jump.png')).convert()
現在已經載入了影象,pygame.image.load()
並且已經確保使用convert()
,這將通過將影象轉換為在螢幕上繪製更快的格式來加速Pygame的繪製。現在準備用精美的玩家圖片形象替換精靈中的普通綠色方塊:
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = player_img
self.rect = self.image.get_rect()
self.rect.center = (WIDTH / 2, HEIGHT / 2)
請注意,已經刪除了命令self.image.fill(GREEN)
- 不再需要填充純色。
現在,如果你執行該程式,你應該看到一個漂亮的小卡通外星人在螢幕上執行。但是遇到了一個問題:
目前無法看到,因為背景目前是黑色的。
用screen.fill(BLUE)
將背景改為藍色。現在可以看到問題:
當您在計算機上有影象檔案時,該檔案始終是矩形畫素網格。無論你繪製什麼樣的形狀,仍然會有畫素邊框填充影象的“背景”。需要做的是告訴Pygame忽略不關心的影象中的畫素。在此影象中,這些畫素恰好是黑色,因此可以新增以下內容:
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = player_img
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
self.rect.center = (WIDTH / 2, HEIGHT / 2)
set_colorkey()
只是告訴Pygame,當繪製圖像時,要忽略指定顏色的任何畫素。現在影象看起來好多了:
整合到一起
# Pygame sprite Example
import pygame
import random
import os
WIDTH = 800
HEIGHT = 600
FPS = 30
# define colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
# set up assets folders
# Windows: "C:\Users\chris\Documents\img"
# Mac: "/Users/chris/Documents/img"
game_folder = os.path.dirname(__file__)
img_folder = os.path.join(game_folder, "img")
class Player(pygame.sprite.Sprite):
# sprite for the Player
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(os.path.join(img_folder, "p1_jump.png")).convert()
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
self.rect.center = (WIDTH / 2, HEIGHT / 2)
self.y_speed = 5
def update(self):
self.rect.x += 5
self.rect.y += self.y_speed
if self.rect.bottom > HEIGHT - 200:
self.y_speed = -5
if self.rect.top < 200:
self.y_speed = 5
if self.rect.left > WIDTH:
self.rect.right = 0
# initialize pygame and create window
pygame.init()
pygame.mixer.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("My Game")
clock = pygame.time.Clock()
all_sprites = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
# Game loop
running = True
while running:
# keep loop running at the right speed
clock.tick(FPS)
# Process input (events)
for event in pygame.event.get():
# check for closing window
if event.type == pygame.QUIT:
running = False
# Update
all_sprites.update()
# Draw / render
screen.fill(BLUE)
all_sprites.draw(screen)
# *after* drawing everything, flip the display
pygame.display.flip()
pygame.quit()
The desire of his soul is the prophecy of his fate
你靈魂的慾望,是你命運的先知。