---
title: Pygame基本介紹
tags: pygame_sirla, python
---
> [TOC]
>
# Pygame基本介紹
:::info
上課日期: 2019/07/04
講者: 土豆
課程錄影: [SIRLA2019暑期特訓(1)-pygame基本介紹](https://www.youtube.com/watch?v=6aDGAdg2-O8&t=766s)
:::
## 教學目標
1. 了解遊戲程式邏輯
* 躲石頭小遊戲
* 太空戰機
2. 增進物件導向程式設計能力
3. 練習閱讀文件
4. 練習閱讀程式碼
5. 從教學內容延伸,開發出完整的遊戲
## 何謂遊戲
* 輸出
* 聲音
* 影像
* 輸入
* 鍵盤
* 滑鼠
* 搖桿
* 邏輯
* 座標在哪,怎麼移動座標
* 碰撞判斷
* 數值計算
* 事件判斷
## Pygame介紹
> Pygame官網: https://www.pygame.org
> PyPI(Python Package Index): https://pypi.org/project/pygame/
Pygame is a Python wrapper module for the SDL multimedia library(Pygame是將SDL多媒體函式庫幫裝起來讓你易於使用的模組)
* SDL: Simple DirectMedia Layer,以C語言撰寫
* multimedia: 多媒體,所以Pygame不只可以用來做遊戲,跟多媒體有關的東西它都可以做
那麼Pygame可以做到怎麼樣子的遊戲呢?
大概是這樣子
{%youtube 8UnvMe1Neok%}
看起來不怎麼Fancy,但你要想,這是一行一行用程式碼刻出來的
一般在做稍微大型的遊戲的時候其實不會用這麼底層的工具去做,都是使用所謂的遊戲引擎,如Unity、Unreal Engine、Godot等等
但這邊為了讓各位清楚了解遊戲最基礎的部分,以及練習寫程式的邏輯,所以我們使用Pygame來進行教學
## 遊戲程式結構介紹
* 遊戲開始畫面
* 遊戲主迴圈(無限循環直到結束條件發生)
* 確認使用者輸入
* 更新座標
* 碰撞判斷
* 繪製圖像
* 播放音效
* 遊戲結束畫面
## Pygame基本架構
記得先安裝pygame的package
`pip install pygame`
* 初始化視窗
```python=
import pygame
pygame.init()
DISPLAY_WIDTH = 800
DISPLAY_HEIGHT = 600
gameDisplay = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT))
pygame.display.set_caption("Hello World")
# ...下面還有喔...
```
* 定義顏色
```python=
# ...接著上面的寫...
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
# ...下面還有喔...
```
* 新增clock物件
```python=
# ...接著上面的寫...
clock = pygame.time.Clock()
# ...下面還有喔...
```
* 遊戲主迴圈
這邊使用了事件判定,如果使用者按下叉叉,playing變成False,迴圈就會結束,關於事件,之後會再詳細說明
```python=
# ...接著上面的寫...
playing = True
while playing:
for event in pygame.event.get():
if event.type == pygame.QUIT:
playing = False
clock.tick(30)
# ...下面還有喔...
```
* 結束遊戲: 一旦跳出迴圈便運行結束指令
```python=
# ...
pygame.quit()
quit()
```
## 繪製長方形、圓形、線條
我們使用draw這個module來進行圖形繪製
> 文件: https://www.pygame.org/docs/ref/draw.html
* 繪製長方形
先來看看文件怎麼說
```python=
rect(Surface, color, Rect, width=0)
```
* Surface: 要畫在哪張畫布上
* color: 顏色,用一個Tuple表示
* Rect: 邊框物件,可使用pygame.Rect()進行初始化,需傳入四個參數,top、left、width、height
* width: 邊框,預設為0
接著實際畫出正方形,左上角位於100, 100,長寬皆為50
```python=
# 將此行加入遊戲主迴圈
pygame.draw.rect(gameDisplay, RED, pygame.Rect(100, 100, 50, 50))
```
* 繪製圓形
```python=
circle(Surface, color, pos, radius, width=0)
```
* pos: **圓心**的x, y座標,一個Tuple
* radius: 半徑
畫出圓心在200, 100,半徑為25的圓形
```python=
# 將此行加入遊戲主迴圈
pygame.draw.circle(gameDisplay, GREEN, (200, 100), 25)
```
* 繪製線條
```python=
line(Surface, color, start_pos, end_pos, width=1)
```
* start_pos: 線開始的點
* end_pos: 線結束的點
畫兩條線,長300,中間間隔50,第二條線粗度為5
```python=
# 將這兩行加入遊戲主迴圈
pygame.draw.line(gameDisplay, BLUE, (300, 100), (300, 400))
pygame.draw.line(gameDisplay, BLUE, (350, 100), (350, 400), 5)
```
* 畫完後執行pygame.display.update(),才會顯示在畫布上
* 這個部分全部完成後的程式碼
```python=
import pygame
pygame.init()
DISPLAY_WIDTH = 800
DISPLAY_HEIGHT = 600
gameDisplay = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT))
pygame.display.set_caption("Draw Shapes")
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
clock = pygame.time.Clock()
playing = True
while playing:
for event in pygame.event.get():
if event.type == pygame.QUIT:
playing = False
pygame.draw.rect(gameDisplay, RED, pygame.Rect(100, 100, 40, 40))
pygame.draw.circle(gameDisplay, GREEN, (200, 100), 25)
pygame.draw.line(gameDisplay, BLUE, (300, 100), (300, 400))
pygame.draw.line(gameDisplay, BLUE, (350, 100), (350, 400), 5)
pygame.display.update()
clock.tick(30)
pygame.quit()
quit()
```
### 練習題: 給各位半小時,請畫出一隻小叮噹
範例如圖,沒別的方法,就是慢慢刻
![](https://i.imgur.com/vANBC0P.png)
## 圓球掉落
* 原理
每跑一次迴圈,我們就更新一次圓球的y座標,讓它數值稍微變大一點,然後再將圓球畫出
先設定好視窗、標題、顏色、clock,並初始化球的x, y座標
```python=
import pygame
pygame.init()
DISPLAY_WIDTH = 800
DISPLAY_HEIGHT = 600
gameDisplay = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT))
pygame.display.set_caption("Ball Drop")
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
ball_x = 400
ball_y = 0
clock = pygame.time.Clock()
# ...
```
接著來寫主迴圈,每一次迴圈都將ball_y的值加上5之後才畫出
```python=
# ...接著上面的寫喔...
playing = True
while playing:
for event in pygame.event.get():
if event.type == pygame.QUIT:
playing = False
ball_y += 5
pygame.draw.circle(gameDisplay, GREEN, (ball_x, ball_y), 25)
pygame.display.update()
clock.tick(30)
pygame.quit()
quit()
```
這時候會發生很奇怪的現象,球不是在掉落,而是直接畫成一條線了
![](https://i.imgur.com/7fvEplr.png)
這是因為我們沒有先將背景重置,就直接畫出球,所以畫面上會殘留著上次的圓形,要重置背景的話要使用display物件的fill()方法,在每次跑畫出新的圓形之前,先將背景填滿某個顏色,改完後如下
```python=
playing = True
while playing:
for event in pygame.event.get():
if event.type == pygame.QUIT:
playing = False
ball_y += 5
gameDisplay.fill(WHITE) # 使用fill()將畫面填入白色,以清空畫面
pygame.draw.circle(gameDisplay, GREEN, (ball_x, ball_y), 25)
pygame.display.update()
clock.tick(30)
pygame.quit()
```
* 這部分完整程式碼
```python=
import pygame
pygame.init()
DISPLAY_WIDTH = 800
DISPLAY_HEIGHT = 600
gameDisplay = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT))
pygame.display.set_caption("Ball Drop")
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
ball_x = 400
ball_y = 0
clock = pygame.time.Clock()
playing = True
while playing:
for event in pygame.event.get():
if event.type == pygame.QUIT:
playing = False
ball_y += 5
gameDisplay.fill(WHITE)
pygame.draw.circle(gameDisplay, GREEN, (ball_x, ball_y), 25)
pygame.display.update()
clock.tick(30)
pygame.quit()
quit()
```
## 圓球反彈
接下來沿用剛剛的程式碼,但是這次我們希望球碰到上邊框或是下邊框時會回彈,形成來回彈動的效果。
首先,宣告一個叫做direction的變數,初始化為1
```python=
import pygame
pygame.init()
# ...上面都不變喔...
ball_x = 400
ball_y = 0
direction = 1 # 新增direction變數
# ...下面還有...
```
接著,更新座標時我們要乘以一個direction變數,如果direction為1,那麼y值就會增加,也就是往下移動,反之,direction為-1則y值減少,也就是往上移動
```python=
# ...
while playing:
# ...
ball_y += 10 * direction # 更新y座標前先乘以direction
# ...
# ...
```
每次更新完座標後,確認一下目前座標,如果小於等於零(超出上邊界),或是大於等於600(超出下邊界),就改變direction的值,讓它往反方向跑
```python=
# ...
while playing:
# ...
ball_y += 10 * direction
# ...新增這段...
if ball_y <= 0:
direction = 1
elif ball_y >= 600:
direction = -1
# ...新增這段...
# ...
# ...
```
這時會發現一個問題,球並不是在一碰到邊邊的時候就回彈,而是中心點的位置碰到時才會回彈
![](https://i.imgur.com/C6Uy14P.png)
這是因為我們在判斷有沒有碰到邊界時,是使用圓心作為座標基準點,所以當圓心超出範圍時才會回彈。
要解決這個問題,判斷有沒有超出邊界時我們需要考慮圓的半徑值,假設減去半徑之後小於等於0,或是加上半徑之後大於等於600,則改變方向
```python=
# ...
while playing:
# ...
ball_y += 10 * direction
if ball_y - 25 <= 0:
direction = 1
elif ball_y + 25 >= 600:
direction = -1
# ...
# ...
```
這樣就完成球反彈的效果了,完整程式碼如下
```python=
import pygame
pygame.init()
DISPLAY_WIDTH = 800
DISPLAY_HEIGHT = 600
gameDisplay = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT))
pygame.display.set_caption("Ball Rebound")
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
ball_x = 400
ball_y = 0
direction = 1
clock = pygame.time.Clock()
playing = True
while playing:
for event in pygame.event.get():
if event.type == pygame.QUIT:
playing = False
ball_y += 10 * direction
if ball_y - 25 <= 0:
direction = 1
elif ball_y + 25 >= 600:
direction = -1
gameDisplay.fill(WHITE)
pygame.draw.circle(gameDisplay, GREEN, (ball_x, ball_y), 25)
pygame.display.update()
clock.tick(30)
pygame.quit()
quit()
```
## 作業 - 模仿DVD螢幕保護動畫
:::info
指派日期: 7/4
繳交日期: 7/11 晚上八點前
:::
這個作業要做一個模仿以前看DVD時的螢幕保護動畫,如果你不是我那個年代的人的話,可以參考這個影片
{%youtube CDHI8yO6etE %}
作業要求如下
1. 要有一個**橢圓形**(裡面不用有字,字做得出來請你吃東西),可以參考[文件](https://www.pygame.org/docs/ref/draw.html)看看怎麼畫出橢圓形,大小隨意,適中即可
2. 這個橢圓形要能夠往八個方向移動(上、右上、右、右下、下、左下、左、左上)
3. 一開始從畫面上半部出發,往任意方向移動
4. 碰到邊界時要進行反彈,如影片那樣
5. Bonus: 如果能做到每次反彈都變更顏色,合宿時我請你25塊內的飲料(如果是夜貓只有10塊錢麥香)
成品如下
![](https://i.imgur.com/4XYiuen.gif)