# Python Pygame 語法介紹(1) ## 簡介 **Pygame**是**Python**的一個遊戲開發函式庫,提供了簡單的介面來處理視窗管理、圖形渲染、事件處理及音效播放等功能 ## 安裝 Pygame 在開始使用**Pygame**前,我們得先安裝他,在終端機輸入以下指令來安裝 ``` pip install pygame ``` >若安裝失敗請確認`pip`是否有正常執行 ## Pygame 語法 ### 導入和初始化 在程式的一開始,我們需要導入並初始化`pygame`,否則系統可能會報錯 ```python #導入pygame import pygame #初始化pygame pygame.init() ``` ### 建立與管理視窗 在`pygame`中,主要由`pygame.display`負責管理視窗的顯示,提供了許多方法來處理畫面更新、視窗控制、全螢幕模式等。以下是相關指令的介紹 - `pygame.display.init()`:初始化`pygame.display`(通常`pygame.init()`會自動執行) - `pygame.display.get_init`:檢查`pygame.display`是否已初始化,回傳`True`或`False` - `pygame.display.set_mode(size, flags=0, depth=0, display=0, vsync=0)`:設定視窗的大小和類型 - `size`:必填,設定視窗的大小 - 格式: `(寬度, 高度)`(單位:像素) - 範例:`pygame.display.set_mode((800, 600))` >→ 創建一個 800×600 的視窗 - `flags`:選填,控制視窗模式,且可同時存在多個`flags` - 預設值`0`:普通視窗 - 可用的`flags`: |`flags`|功能| |-------------------|---------------------| |`pygame.FULLSCREEN`|全螢幕模式 | |`pygame.RESIZABLE` |允許使用者調整視窗大小 | |`pygame.NOFRAME` |無邊框視窗 | |`pygame.SCALED` |自動縮放畫面以適應視窗大小| |`pygame.HWSURFACE` |使用硬體加速(僅限 `pygame.FULLSCREEN`可用)| |`pygame.DOUBLEBUF` |啟用雙緩衝(建議與`pygame.HWSURFACE`或`pygame.OPENGL`一起使用)| |`pygame.OPENGL` |使用 OpenGL 渲染 | >[!Note]名詞補充 >雙緩衝 (Double Buffering) 是一種圖形渲染技術,目的是減少畫面閃爍並提升視覺流暢度 >OpenGL(Open Graphics Library)渲染是指使用 OpenGL API 來將 3D 模型與場景轉換成影像並顯示在螢幕上的過程 - `depth`:選填,設定色彩深度(單位:bit) - 預設值`0`: 讓系統自動選擇最佳的色彩深度 - `display`:選填,指定使用哪一個顯示器(僅在**多螢幕系統**上適用) - 預設值`0`:使用主顯示器 - `vsync`:選填,啟用或停用**垂直同步(VSync)** - 可用的值: - `0`:預設,不使用垂直同步 - `1`:啟用垂直同步 - `-1`:讓系統自動決定是否啟用(部分系統和驅動支援) >[!Important]注意 >在設定參數時許注意格式統一,否則系統將會報錯 >以下為錯誤示範 >``` >pygame.display.set_mode(size=(800,800), pygame.FULLSCREEN) >``` > >另外,也要注意各個參數之間的搭配,否則可能會引起系統故障 - `pygame.display.quit()`:關閉視窗(不等於關閉`pygame`) - `pygame.display.flip()`:更新整個視窗 - `pygame.display.update(rect_list)`:更新視窗內部份區域 - `rect_list`: - `rect_list=None`:預設值,若**不提供`rect_list`參數**,則效果與`pygame.display.flip()`相同,會更新整個畫面 - `rect_list`是一個`Rect`物件列表:當`rect_list`是一個 list(包含多個`pygame.Rect`物件)時,只會更新這些區域,提升效能 - `pygame.display.set_caption(title)`:設定視窗名稱 - `pygame.display.get_caption()`:取得視窗名稱,回傳`(title, icontitle)` - `pygame.display.set_icon(icon_surface)`:設定視窗圖示,接受一個`pygame.Surface`物件或圖片作為圖示,步驟如下 1. 準備圖示圖片:首先,你需要準備一張 小尺寸的圖示圖片(如 icon.png),建議尺寸為 32×32 或 64×64 像素,並且圖片格式為 PNG(建議使用透明背景) 2. 載入圖片並設定圖示: ```python import pygame pygame.init() screen = pygame.display.set_mode((800, 600)) # 載入圖示,確保 icon.png 放在程式的相同目錄下 # 也可以直接給予路徑 icon = pygame.image.load("icon.png") # 設定視窗圖示 pygame.display.set_icon(icon) ``` >[!Note]補充 >如果你的圖示太大(例如 256x256),Pygame不會自動縮小,可能會出現問題,這時可以使用`pygame.transform.scale()`來縮小圖示 >```python >icon = pygame.image.load("icon.png") >#調整為 32×32 >icon = pygame.transform.scale(icon, (32, 32)) >pygame.display.set_icon(icon) >``` >這樣可以確保你的圖示適合視窗 - `pygame.display.get_surface()`:取得目前視窗本身的`Surface`(用於直接繪製) >[!Note]視窗本身的`Surface`是什麼? >Pygame的**視窗本身就是一個`Surface`物件**,它來自`pygame.display.set_mode()` >當你執行: >```python >screen = pygame.display.set_mode((800, 600)) >``` >這行程式碼會: >1. 建立一個 800×600 的Pygame視窗 >2. 回傳一個`Surface`物件,存入變數`screen` >3. `screen`就是這個**視窗的`Surface`**,你可以在上面畫東西 > >這個`Surface`代表遊戲視窗上的所有像素。你可以把它想像成一張**可以繪製的畫布**,所有的圖案、圖片和文字都必須畫在這個`Surface`上,最後再透過`pygame.display.update()`或 `pygame.display.flip()`顯示到螢幕上 > >當你使用`pygame.display.get_surface()`,你取得的是**視窗本身的`Surface`物件**,這表示: >1. 你可以直接在視窗上畫圖,無需建立新的`Surface` >2. 你畫的內容會直接影響視窗內容(但仍需更新視窗才能顯示) - `pygame.display.iconify()`:最小化視窗 - `pygame.display.toggle_fullscreen()`:切換全螢幕模式,主要適用於 Linux(X11 視窗系統) >[!Caution]警告 >在 Windows 和 macOS 上可能無法正常運作,甚至會導致視窗異常,建議改用`pygame.display.set_mode()`來切換全螢幕模式 :::success - `.fill(color)`:將`Surface`物件填滿顏色,在前面我們有提到視窗本身就是一個`Surface`物件,所以我們可以用`.fill()`來改變視窗的顏色 - 範例: ```python screen = pygame.display.set_mode((500, 300)) screen.fill((0, 0, 0))# 黑色背景 ``` ::: ### 繪製圖形 在`pygame`中,主要由`pygame.draw`來負責繪製圖形,提供了一系列用於繪製基本圖形的函式 >[!Note]螢幕座標系統 >在開始介紹函式前我們要先來說明一下螢幕座標系統 >螢幕座標系統是一種 二維 (2D) 坐標系統,用來定位電腦螢幕上的像素 (pixel) 位置 > >螢幕座標的基本特性: >- 原點 (0,0) 在左上角 > 與數學坐標系統(原點通常在中心)不同,螢幕座標系統的原點通常定義在**螢幕的左上角** >- X軸向右遞增 > 從左往右,X值增加 >- Y軸向下遞增 > 從上往下,Y值增加,這與數學座標系統相反,數學座標系統通常是Y軸向上遞增 > >示意圖 >|0,0|1,0|2,0| >|---|---|---| >|0,1|1,1|2,1| >|0,2|1,2|2,2| >[!Tip]`pygame.draw`繪圖函式的特性 >這些函式的第一個參數通常是`surface`,表示要繪製的目標畫布,顏色則使用`(R, G, B)`或`(R, G, B, A)`格式,`A`為透明度,另外,**所有繪圖函式都會回傳`pygame.Rect`物件**,代表繪製區域的範圍,可配合`pygame.display.update()`使用 - `pygame.draw.line(surface, color, start_pos, end_pos, width=1)`:畫一條線 - `start_pos`:線段的起點座標`(x, y)` - `end_pos`:線段的終點座標`(x, y)` - `width`:線條寬度,預設`1` - `pygame.draw.lines(surface, color, closed, point_list, width=1)`:畫多條相連的線 - `closed`:若為`True`,則最後一點會連回第一點形成封閉區域 - `point_list`:點座標的列表`[(x1, y1), (x2, y2), ...]` - `width`:線條寬度 - `pygame.draw.aaline(surface, color, start_pos, end_pos, blend=1)`:畫一條抗鋸齒的線(較平滑) - `blend`:若為`1`,則啟用抗鋸齒效果 - `pygame.draw.aalines(surface, color, closed, point_list, blend=1)`:畫多條相連的抗鋸齒線 >[!Note]什麼是抗鋸齒? >抗鋸齒(Anti-Aliasing,AA)是一種圖形技術,主要用來減少電腦圖像中因解析度限制而產生的鋸齒狀(Jagged Edges)效果,使邊緣看起來更平滑。這種鋸齒現象通常出現在低解析度或當對角線、曲線無法完美對應像素點時,導致邊緣看起來不自然或鋸齒化 > >以下是幾種常見的抗鋸齒技術: >- 多重取樣抗鋸齒(MSAA, Multi-Sample Anti-Aliasing) > - 透過對場景的多個樣本進行計算,並根據像素的顏色加權平均,使邊緣平滑 > - 只對幾何邊緣進行抗鋸齒,因此效能較好,但對透明紋理無效 >- 快速近似抗鋸齒(FXAA, Fast Approximate Anti-Aliasing) > - 透過後處理(Post-Processing)的方法模糊鋸齒邊緣,使畫面變得平滑 > - 效能較好,適合低端硬體,但畫面可能會變模糊 >- 超級採樣抗鋸齒(SSAA, Super-Sampling Anti-Aliasing) > - 先以高解析度渲染畫面,然後縮小至目標解析度,以獲得更平滑的圖像 > - 效果較佳,但效能消耗較高 >- 時間性抗鋸齒(TAA, Temporal Anti-Aliasing) > - 使用多幀資訊來平滑動態場景中的鋸齒,能減少閃爍問題 > - 效果較佳,但可能會產生畫面模糊或鬼影(Ghosting)現象 >- 深度學習抗鋸齒(DLAA, Deep Learning Anti-Aliasing) > - 由 NVIDIA 開發,人工智慧加持的反鋸齒技術,讓影像品質更臻完美。DLAA 採用專為 DLSS 開發的超解析技術,以原生解析度締造更栩栩如生的高品質影像 > - 透過機器學習預測高解析度畫面,達到較好的效果,效能影響較小,但犧牲幀率來提升來提高圖像質量 - `pygame.draw.rect(surface, color, rect, width=0, border_radius=0, border_top_left_radius=-1, border_top_right_radius=-1, border_bottom_left_radius=-1, border_bottom_right_radius=-1)`:繪製矩形 - `rect`:矩形的 Rect 物件`(x, y, width, height)` - `width`:選填,邊框粗細 - `width=0`:填滿矩形(畫出實心矩形) - `width>0`:只畫邊框,值決定邊框的厚度(像素) - `width`大於矩形寬度或高度時:會變成實心矩形 - `border_radius`:選填,預設為`0`,控制圓角半徑 - `border_*_radius`:選填,預設為`-1`,分別控制四個角的圓角半徑 >[!Note]圓角半徑(border radius)是什麼? >圓角半徑(border_radius)指的是矩形四個角的圓滑程度,即矩形的角如何從直角變成圓弧。當 border_radius 設為 0(預設值)時,矩形的角是標準的 90° 直角;當 border_radius 設為正數時,矩形的角就會變圓 >[!Note]`border_radius`的效果 >數值`0`(預設) → 標準直角矩形 >數值`>0` → 圓角矩形,數值越大,圓角越圓 >數值`> min(width, height) / 2` → 會變成橢圓或半圓 >數值`-1`→ 無效(不會影響矩形) - `pygame.draw.circle(surface, color, center, radius, width=0)`:畫一個圓 - `center`:圓心座標`(x, y)` - `radius`:圓半徑 - `pygame.draw.ellipse(surface, color, rect, width=0)`:畫一個橢圓(基於矩形範圍) - `rect`:決定橢圓形狀的矩形區域 - `pygame.draw.arc(surface, color, rect, start_angle, end_angle, width=1)` :畫一條弧線(基於矩形範圍) - `start_angle`、`end_angle`:弧線的起始與結束角度,單位為弧度 >[!Note]弧度 >在`pygame.draw.arc()`中,起始角度(start_angle)和結束角度(end_angle)的單位是弧度(radian),而不是一般常見的度數(degree)。這可能會讓人感到困惑,所以我們來簡單解釋一下 > >#### 什麼是弧度? > >弧度(radian)是角度的另一種測量方式: >- 360° = 2𝜋 弧度 >- 180° = 𝜋弧度 >- 90° = $\frac{𝜋}{2}$弧度 >- 45° = $\frac{𝜋}{4}$弧度 > >換算公式: >$$弧度=度數×\frac{𝜋}{180}$$ > >Python內建的`math.radians(degrees)`可以幫助我們轉換,例如: >```python >import math >#轉換 90° 為弧度,結果是 𝜋/2 >print(math.radians(90)) # 轉換 90° 為弧度,結果是 𝜋/2 >#輸出 1.5707963267948966 >``` > >畫一條從 0° 到 180°(半圓)的弧線 >```python >pygame.draw.arc(screen, RED, rect, math.radians(0), math.radians(180), width=3) >``` > >#### 角度方向 > >在`pygame.draw.arc()`中,角度是按照**數學標準方向**計算的: >- 0 弧度(0°) → 從右側開始 >- π/2 弧度(90°) → 上方 >- π 弧度(180°) → 左側 >- 3π/2 弧度(270°) → 下方 >由上可知,**`pygame.draw.arc()`的繪製方向是「逆時針」的**,如果你想畫出從 90°(上方)到 270°(下方)的半圓弧,應該這樣設定: >```python >pygame.draw.arc(screen, RED, rect, math.radians(90), math.radians(270), width=3) >``` >>高二上數學 - `pygame.draw.polygon(surface, color, point_list, width=0)`:畫一個多邊形 - `point_list`:頂點座標的列表`[(x1, y1), (x2, y2), ...]` >[!Tip]小技巧 >看到現在不知道大家有沒有發現,不論是畫哪種形狀,開頭都會先用到`pygame.draw`,然後再接要畫的形狀函數,而如果是使用VScode來寫程式的話,系統都會自動提示你有那些函數可接,所以在記指令時我們只要記得每個函數大概的意思就可以了,剩下的系統會提示你 >![螢幕擷取畫面 2025-02-08 143523](https://hackmd.io/_uploads/S1g2npVYke.png) >我們剛才說的九種函數都在這裡 >![螢幕擷取畫面 2025-02-08 143757](https://hackmd.io/_uploads/Bklhn6Etkl.png) >參數忘了也沒關係,系統會提示 ### 文字處理 在`pygame`中,主要由`pygame.font`來處理文字相關的操作,主要有載入字型、設定大小、渲染文字等功能 - `pygame.font.init()`:初始化`pygame.font`(通常`pygame.init()`會自動執行) - `pygame.font.get_init()`:檢查`pygame.font`是否已初始化,回傳 `True`或`False` - `pygame.font.quit()`:關閉`pygame.font` - `pygame.font.get_default_font()`:取得`pygame`預設字型的名稱,通常是`"freesansbold.ttf"` - `pygame.font.get_fonts()`:回傳系統上可用的字型名稱清單 - `pygame.font.match_font(name, bold=False, italic=False)`:查找字型是否存在,若存在則回傳路徑,若找不到則回傳`None` - `name`:字型名稱 - `bold`:選填,指定是否查找**加粗**(**bold**)版本的字型 - `italic`:選填,指定是否查找**斜體**(**italic**)版本的字型 - `pygame.font.SysFont(name, size, bold=False, italic=False)`:載入一個字型並創建字型物件 - `name`:字型名稱,若設為`None`則使用預設字型 - `size`:字型大小 - `bold`:選填,粗體 - `italic`:選填,斜體 - `pygame.font.Font(file, size)`:載入一個字型檔案並創建字型物件 - `file`:檔案路徑,若設為`None`則使用預設 - `size`:字型大小 >[!Important]字型物件 >當你創建好一個字型物件後,記得要將他保存起來,也方便你接下來的操作,例如: >```python >font = pygame.font.SysFont("microsoftjhenghei", 100) >``` - `text_surface = font.render(text, antialias, color, background=None)`:將文字渲染為`Surface`物件 - `text`:要輸出的文字 - `antialias`:是否開啟抗鋸齒,`True`是開啟,`False`是不開啟 - `color`:字體顏色 - `background`:選填,背景顏色,若為`None`則透明 - `text_rect = text_surface.get_rect(center=(x, y))`:取得文字的矩形區域,回傳為`Rect`物件 - 可以在`get_rect()`裡加參數來**設定圖片的初始位置,在定位的同時移動到指定位置**,以下是可用參數 |參數|作用|參數|作用| |-------------|----------------|-----------|------------------| |`topleft` |設定矩形的左上角座標|`midtop` |設定矩形的頂部中央座標| |`topright` |設定矩形的右上角座標|`midbottom`|設定矩形的底部中央座標| |`bottomleft` |設定矩形的左下角座標|`midleft` |設定矩形的左側中央座標| |`bottomright`|設定矩形的右下角座標|`midright` |設定矩形的右側中央座標| |`center` |設定矩形的中心點座標| - `screen.blit(text_surface, text_rect)`:將文字渲染到視窗上,仍須更新視窗才會顯示,另外`text_rect`也可以換成`(x, y)`座標 >[!Tip]完整流程 >```python >font = pygame.font.SysFont("microsoftjhenghei", 100) >text_surface = font.render("文字", True, (0, 255, 255)) >text_rect = text_surface.get_rect(center=(350, 350)) >screen.blit(text_surface, text_rect) >pygame.display.flip() >``` - `font.size(text)`:測量指定文字的寬度和高度,回傳`(width, height)` >[!Important]以下指令適用於`Surface`物件 - `set_bold(value)`:設定是否加粗,`value=True/False` - `get_bold()`:檢查字型是否加粗,回傳`True`或`False` - `set_italic(value)`:設定是否斜體 - `get_italic()`:檢查字型是否斜體 - `set_underline(value)`:設定是否有底線 - `get_underline()`:檢查字型是否有底線 - `metrics(text)`:取得指定文字的字元度量資訊,回傳列表`(min_x, max_x, min_y, max_y, advance_x)`,若某個字元在字型中找不到將回傳`None` - |參數|意義| |---|---| |`min_x`|字元左側邊界的 X 座標| |`max_x`|字元右側邊界的 X 座標| |`min_y`|字元底部邊界的 Y 座標| |`max_y`|字元頂部邊界的 Y 座標| |`advance_x`|下一個字元的起始位置(表示字元的寬度加上間距)| >[!Note]常用的字型名稱 >- `microsoftjhenghei`:微軟正黑體,推薦使用 >- `microsoftyahei`:微軟雅黑體 >- `mingliu`:細明體,傳統風格 >- `pmingliu`:新細明體,繁體中文 >- `simsun`:中易宋體,簡體中文 >- `simhei`:中易黑體,簡體中文 >- `dengxian`:等線體 >- `fangsong`:仿宋體,古典風格 >- `kaiti`:楷體,手寫風格 ### 圖像處理 在`pygame`中,主要由`pygame.image`來負責圖像方面的操作,提供了圖片縮放、色彩透明度以及像素碰撞等功能 - `pygame.image.load("path/to/image.png")`:載入圖片 - `"path/to/image.png"`:檔案路徑,要記得`\`要換成`\\`或`/` - 支援格式:PNG、JPG、GIF(靜態)、BMP、TGA - 如果圖片格式不支援,會拋出`pygame.error: Unsupported image format`錯誤 >[!Note]可以使用`convert()`或`convert_alpha()`來提高效能 >在`pygame`中,使用`.convert()`或`.convert_alpha()`**提高效能的主要原因**在於**顯存格式的匹配**。以下是它們如何提升效能的細節: >- 內部格式轉換 > 當你用`pygame.image.load()`載入圖片時,圖片的格式可能與螢幕的顯存格式不同,這會導致`blit()`(繪製圖片)時,每次都需要額外的計算來匹配格式,從而降低效能 > - `.convert()`:將圖片的像素格式轉換為與顯示畫面(Surface)的格式相同,這樣`blit()`可以直接複製記憶體區塊,而不需要即時轉換格式,因此顯示速度大幅提升 > - `.convert_alpha()`:同樣會轉換格式,但會保留 Alpha(透明通道)資訊,適用於 PNG 這類有透明部分的圖片 >- 減少 CPU/GPU 運算開銷 > - **如果不使用**`.convert()`,每次`blit()`都要即時進行格式轉換,這會增加 CPU/GPU 的運算負擔,導致遊戲掉幀或卡頓 > - 使用`.convert()`後,圖片格式與顯示畫面相匹配,繪製時可以直接使用顯存中的數據,減少額外的運算開銷,提高`blit()`的執行效率 >```python >image = pygame.image.load("path/to/image.png").convert() # 移除透明度,加快顯示 >image_alpha = pygame.image.load("path/to/image.png").convert_alpha() # 保留透明度 >``` - `pygame.transform.scale(image, (new_width, new_height))`:前面提到過的圖片縮放 - `pygame.transform.smoothscale(image, (new_width, new_height))`:**平滑縮放**(適用於 PNG、JPG),**減少鋸齒化,但效能消耗較高** - `pygame.transform.rotate(image, angle)`:旋轉圖片,但旋轉後圖片大小可能會改變 - `angle`:角度(順時針旋轉) - `pygame.transform.rotozoom(image, angle, scale)`:旋轉並縮放 - `scale`:縮放倍率,`1.0`表示不變 - `pygame.transform.flip(image, flip_x, flip_y)`:水平和垂直翻轉 - `flip_x`:水平翻轉(`True`/`False`) - `flip_y`:垂直翻轉(`True`/`False`) :::success - `image.set_alpha(alpha_value)`:改變透明度,適用於`.convert_alpha()` 圖片 - `alpha_value`:`0~255`,`0`表示完全透明,`255`表示完全不透明 - `image.set_colorkey((R, G, B))`:使某個顏色變為透明 - `image.get_size()`:取得圖片尺寸,回傳為`(w, h)` - `image.get_rect()`:取得圖片的矩形區域,回傳為`Rect`物件,與前面提到過的`text_surface.get_rect()`類似 - `screen.blit(image, (x, y))`:在`(x, y)`位置繪製圖片,仍須更新視窗才會顯示,與前面提到的`screen.blit(text_surface, text_rect)`類似 ::: ### 結束Pygame 當程式結束時,應確保Pygame正確關閉 - `pygame.quit()`:關閉Pygame ## [下一篇](https://hackmd.io/@Huanyu763/PythonPygame事件處理) ## [回到主頁](https://hackmd.io/@Huanyu763/home)