2019 進修組 Pygame小組
===
> 王立洋 林冠宇
> 文件連結:
> https://hackmd.io/s/S1FN2UrsE
> github:
> https://github.com/osinoyan/2019-nctucscamp-pygame
> 簡報:
### PYGAME小遊戲 2.0 https://docs.google.com/presentation/d/1deqRTNzxyrYDBiQLceTbj4jkPcYoYASD6_MjHZE5csA/edit#slide=id.p
#### pygame old
https://docs.google.com/presentation/d/13sR05BAyob9vLKNGodk4USc-WyENBHcqWUvKk-mIqII/edit?usp=sharing
>20190625 Note
>Convert拿掉
>Center rect拿掉
>Canvas
>Color直接統一用pygame.color
>準備範例Code包
## Table of Contents
[TOC]
目標
---
- 帶大家寫出一個pygame小遊戲:
- ### ==小精靈==
- 一邊教一些pygame套件常用的模組,再利用教的東西,一邊帶大家寫遊戲
<!-- 摘要 -->
<!-- --- -->
<!-- - pygame 套件介紹+安裝
- pygame 基本遊戲架構 (sys.exit() 等等.. 讓遊戲至少可以安詳地開啟和關閉)
- pygame.init
- pygame.display.update
- pygame.quit
- pygame 顯示
- 設定視窗與圖層(convert)
- fill
- pygame.draw
- pygame.image
- pygame.font
- blit
- 顏色表示、座標軸表示
- pygame 事件
- pygame.key
- pygame.mouse
- pygame 動畫
- pygame.time
- pygame.sprite(角色類別)
- collision
- pygame.sprite.collide_rect
### advanced
- read/write file
- sound -->
## 統一參數命名
> 視窗:screen / 寬:SCREEN_WIDTH / 高:SCREEN_HEIGHT
> 圖片:picture ==因為pygame中有image這個class,怕搞混==
## 三小時 教課/練習時間安排
### SOCCERKICK
遊戲成品DEMO
顯示視窗
畫出圓形
動畫1:圓形等速移動
動畫2:重力模型
滑鼠輸入事件:踢球
隨機:球會亂噴
動畫3:碰壁反彈
加入遊戲開始畫面
顯示文字:分數計算
載入圖片
| 內容 | 教課 | 練習 |
| -------- | -------- | -------- |
| 遊戲成品DEMO | **5min** | **-** |
| 顯示視窗 | **5min** | **5min** |
| 畫出圓形 | **5min** | **5min** |
| 動畫1:等速移動 | **5min** | **5min** |
| 動畫2:重力模型 | **5min** | **5min** |
| 中場休息 | **10min** |
| 滑鼠輸入事件:踢球 | **10min** | **10min** |
| 隨機:球會亂噴 | **5min** | **5min** |
| 動畫3:碰壁反彈 | **5min** | **5min** |
| 中場休息 | **10min** |
| 加入遊戲開始畫面 | **10min** | **10min** |
| 顯示文字:分數計算 | **5min** | **5min** |
| 載入圖片 | **5min** | **5min** |
| 彈性FREE TIME | - | **35min** |
| 形狀 | 模組名稱 | 參數1 | 參數2 | 參數3 | 參數4 | 參數5 |
| -------- | -------- | -------- | -------- | -------- | -------- | -------- |
| 圓形 | `pygame.draw.circle` | 圖層 | 顏色 | 圓心(x, y)| 半徑 | - |
| 長方形 | `pygame.draw.rect` | 圖層 | 顏色 | [x, y, 寬, 高] | - | - |
| 線段 | `pygame.draw.line` | 圖層 | 顏色 | 起點(x, y) | 終點(x, y) | 線段粗度 |
<!--
| 內容 | 教課 | 練習 |
| -------- | -------- | -------- |
| 套件介紹 | **5min** | **-** |
| 基本遊戲架構 | **5min** | **5min** |
| 畫出圖形 | **5min** | **5min** |
| 顯示圖片 | **5min** | **5min** |
| 顯示文字 | **5min** | **-** |
| 中場休息 | **15min** |
| 事件 | **5min** | **-** |
| 鍵盤輸入 | **7.5min** | **10min** |
| 滑鼠輸入 | **7.5min** | **10min** |
| 動畫 | **10min** | **10min** |
| 角色類別 | **10min** | **10min** |
| 小精靈(小遊戲實作?) | **10min** | 運氣好的話還會剩 **35min** |
:::success
## 正片開始 (3hr)
:::
## 套件介紹+安裝
### 介紹
- Pygame是一群Python語言的模組,顧名思義便是讓Python方便製作遊戲
- 想要快速製作一些邏輯簡單的小遊戲,pygame 是非常不錯的選擇
### 安裝
```bash=
python -m pip install --upgrade pip
python -m pip install pygame
import pygame
```
## 基本遊戲架構
- 初始化
- `pygame.init()`
- 更新畫面
- `pygame.display.update()`
- 結束遊戲
- `pygame.quit()`
```python=
#匯入pygame
import pygame
#pygame初始化
pygame.init()
... #遊戲的初始化與背景設置放在這邊
#開始運行遊戲
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False #終止遊戲判斷
else
... #其他遊戲判斷與更新都放在這邊
#離開遊戲
pygame.quit()
```
## 顯示圖形/圖片/文字
```python=
#設定視窗大小與名字
SCREEN_WIDTH, SCREEN_HEIGHT = 800, 500 #(視窗寬, 視窗高) = (800, 500)
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption('xxxxxxx')
#新增一個圖層
canvas_1 = pygame.Surface(screen.get_size()).convert()
canvas_1.fill((255, 255, 255)) #將圖層背景設定為白色
```
### 畫出圖形
```python=
#畫出藍色長方形,左上角座標 = (70, 50),寬與高 = (40, 30)
pygame.draw.rect(canvas_1, (0, 0, 255), [70, 50, 40, 30])
#畫出黑色圓形,圓心座標 = (20, 30),半徑 = 10
pygame.draw.circle(canvas_1,(0, 0, 0) , (20, 30), 10)
#畫出紅色直線,線從座標(0, 0),畫到座標(800, 500),線粗 = 5
pygame.draw.line(canvas_1, (255, 0, 0), (0, 0), (800, 500), 5)
```
- 電腦顯示的座標軸:原點(0, 0)在左上角,x軸向右,y軸向下
- 顏色表示方法:(紅色 , 綠色 , 藍色),每個數值0~255
### 顯示圖片
```python=
#載入圖片
picture = pygame.image.load('picture.png').convert()
#調整圖片為寬與高 = (50, 50)
picture = pygame.transform.scale(picture, (50, 50)).convert()
#將圖片畫到圖層上,左上角座標(100, 80)
canvas_1.blit(picture, (100, 80))
```
- convert的作用
- 將一個文件名稱輸入load函數,它會輸出一個加載了圖像的Surface
- 加載完成之後,我們使用Surface中的convert。convert函數也會輸出一個該圖像的新Surface,但圖像被轉換為與我們的顯示(display)相同的像素點格式
- 由於圖像與屏幕有著同樣的格式,blit時會非常快。如果我們沒有進行轉換,blit函數會更慢,因為它運行時必須把一種格式的像素點轉換為另一種格式
### 顯示文字
```python=
#設定文字字型與大小
myfont = pygame.font.SysFont("Arial", 24)
# 定義顏色: 青色CYAN BLUE
CYAN_BLUE = (75, 139, 190)
# 設定文字內容 (內容, 是否消除鋸齒, 顏色)
text = myfont.render('How are you today?', True, CYAN_BLUE)
# 建立一個 'text' 尺寸大小的矩形,命名為 'text_rect'
# 並且設定該矩形的中心點為視窗的中心點
text_rect = text.get_rect(center = (SCREEN_WIDTH/2, SCREEN_HEIGHT/2))
# 將 'text' 文字元件,以text_rect的位置放置在視窗 'screen'
canvas_1.blit(text, text_rect)
```
- text_rect為何可以這樣用?
- rect本身就代表著一組寬與高,而text.get_rect會回傳一個rect,就可以直接使用在blit上了
- 我的長方形被圓形壓著啦!!!
- 那是因為先寫了draw.rect,下一行才寫draw.circle才會這樣
- draw或blit的順序決定了覆蓋順序
- 將圖層更新至視窗
`screen.blit(canvas_1, (0, 0))`
## 中場休息
## 事件
- pygame會接受使用者的各種操作 (例如按鍵盤,移動滑鼠等) ,以產生事件
- 事件隨時可能發生,而且量也可能會很大,pygame的做法是把一串的事件存放在一個陣列裡,再一個一個處理。
- 事件陣列
- `pygame.event.get()`
- 陣列中各個事件(event)有各種不同的形態(type),例如鍵盤按下或滑鼠移動等等,而根據型態的不同,事件帶有的參數就不同
### 鍵盤輸入
```python=
while running:
#開始偵測事件
for event in pygame.event.get():
#當偵測到按鍵按下
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
... #執行按向上鍵的功能
if event.key == pygame.K_a:
... #執行按A鍵的功能
#當偵測到按鍵放開
if event.type == pygame.KEYUP:
...
```
- 上面的方法是當按下或放開才會偵測,所以如果要讓角色持續移動,而不是按一下走一格的話,要使用下面的寫法
```python=
#偵測按鍵持續輸入
while running:
#獲得鍵盤輸入情況的一個陣列
KEY = pygame.key.get_pressed()
#如果偵測到向上鍵有按著
if KEY[pygame.K_DOWN]:
...
```
### 滑鼠輸入
```python=
while running:
#開始偵測事件
for event in pygame.event.get():
#當偵測到滑鼠按鍵按下
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
#執行按左鍵的功能(1: 左鍵 / 2: 中鍵 / 3: 右鍵 / 4: 滾輪向上 / 滾輪向下)
#獲得滑鼠按下時的座標(x, y)
Position = event.pos
#當偵測到滑鼠按鍵放開
if event.type == pygame.MOUSEBUTTONUP:
...
#當偵測到滑鼠移動
if event.type == pygame.MOUSEMOTION:
#滑鼠按鍵按下狀況(左鍵 , 中鍵 , 右鍵)
Button = event.buttons
#獲得滑鼠現在座標(x, y)
Position = event.pos
#獲得滑鼠移動距離
Distance = event.rel
```
- 同樣的,上面的寫法是當滑鼠有按下或移動才會偵測,如果要持續偵測 (例如讓角色跟著滑鼠移動) ,要使用下面的寫法
```python=
#偵測滑鼠按鍵持續按著
KEY_MOUSE = pygame.mouse.get_pressed()
#如果偵測到左鍵有按著
if KEY_MOUSE[0]:
...
#持續偵測滑鼠位置
Pos_MOUSE = pygame.mouse.get_pos()
#讓角色的位置持續等於滑鼠位置
player.pos = Pos_MOUSE
```
- 還記得一開始基本遊戲架構的`event.type == pygame.QUIT`嗎?其實它也是一種event,相當於我們關掉視窗的動作,所以如果沒有這個判斷,視窗是關不掉的喔!
## 動畫
- 使用一個迴圈,持續更新東西的位置或圖片,就會看起來像是在動了
- pygame.time
- `pygame.time.Clock()`
- 建立時間元件
- `tick(n)`
- 每秒執行n次
```python=
#建立時間元件
clock = pygame.time.Clock()
#長方形初始位置
Pos_X, Pos_Y = 30, 30
while running:
clock.tick(30) #每秒更新30次
Pos_X += 1 #更新長方形位置
#重新畫出長方形
pygame.draw.rect(canvas_1, (0, 0, 255), [Pos_X, Pos_Y, 40, 30])
screen.blit(canvas_1, (0, 0))
```
- 變成貪食蛇啦~
- 因為draw跟blit只會把新的圖畫上去,但並不會把舊的東西清掉
- 在draw之前加上`canvas_1.fill(原背景顏色)`就可以把舊的長方形清掉了
## 角色類別
- pygame.sprite
- 能創造多個相同的物件,除了複製多個物件,還可以進行動畫繪製及碰撞偵測等
```python=
角色群組名稱 = pygame.sprite.Group()
#加入角色物件
角色群組名稱.add(角色物件)
#繪製到畫布上
角色群組名稱.draw(圖層)
```
- collision
- pygame.sprite.collide_rect()
- 偵測矩形的碰撞
```python=
spirte_1 = MySprite("sprite_1.png",200,200,1)
sprite_2 = MySprite("sprite_2.png",50,50,1)
result = pygame.sprite.collide_rect(sprite_1,sprite_2)
if result:
print "Collision occurred"
```
## 自由時間/彈性進階教學
###### tags: `資工營` `進修組` `2019` `pygame`