天天看點

Pygame 入門基本指南

最近正在利用 Python 制作一個小遊戲,但對于 Pygame 不熟悉,故在學習的過程記錄相關知識點

Pygame 中文文檔下載下傳:Here

Pygame 2.0 (中文) 線上:Here 感謝 龍桑 提供

Pygame第1-1課:入門

什麼是Pygame?

Pygame是一個“遊戲開發庫” - 一組幫助程式員制作遊戲的代碼庫。包含:

  • 圖形和動畫
  • 聲音(包括音樂)
  • 控制(鍵盤,滑鼠,遊戲搖桿等)

Pygame安裝

  • pip

    安裝

    pip install pygame

    如果下載下傳緩慢或者下載下傳失敗了的話建議切換pip源到國内鏡像,如何更換?
  • PyCharm 安裝

    第一步:打開Pycharm

    第二步:點File ->Default Settings->Project Interpreter->點加号

    第三步: 搜尋Pygame->Install Package

Pygame遊戲結構架構

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個部分:紅色,綠色和藍色。每個部分點亮多少會決定像素的顔色,如下所示:

Pygame 入門基本指南

三原色中的每一個可以具有介于0和255之間的值,是以對于三種基色中的每一種,存在256種不同的可能性。以下是一些三種顔色的組合示例:

Pygame 入門基本指南

在程式的頂部定義一些顔色:

# 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個選擇:

  1. 自己制作
  2. 找一位美工為你制作
  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()