# popcat
## 開始前的一些觀念
### What is Pygame?
- 跨平台Python模組,專為電子遊戲設計
### 對於遊戲視窗,我們到底在做什麼?
- 這是遊戲視窗的簡易結構
- window
- surface
image
font.render
- sound
## 基礎功能與操作
### 匯入pygame與初始化
- 匯入pygame函式庫
```python=
import pygame
```
- 初始化 pygame所有模組
```python=
pygame.init()
```
::: info
:information_source: **提醒**
`pygame.init()` 在此的作用是將函式庫中的所有模組初始化。
:::
* 關閉程式
```python=
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.quit()
```
:::warning
:warning: **注意**
忘記`pygame.quit()`就等被錯誤訊息噴吧!
:::
### 建立視窗
- 建立視窗
- 設定視窗大小
```python=
window = pygame.display.set_mode((window_width, window_height))
```
:::warning
:warning: **注意**
在這裡填的是**tuple**!!!
千萬不要只顧著填數字,會報錯喔!
:::
- 將視窗填色
- 填入色碼
```python=
window.fill(color)
window.fill(WHITE)
```
::: info
:information_source: **提醒**
色碼是由三組數字(0~255)組成的,分別代表紅綠藍三色比例。
為了提升可讀性,我們可以設定一些顏色的常數,如:
`WHITE = (0, 0, 0)`
:::
- 設定視窗標題
```python=
pygame.display.set_caption("POPCAT")
```
- 刷新畫面
```python=
pygame.display.update()
```
:::warning
:warning: **注意**
這很重要!! 主迴圈每次跑完都要刷新視窗!
想想看動畫是怎麼做的。
:::
### 建立畫布
- 建立畫布
```python=
surface = pygame.Surface((window_width, window_height))
surface = surface.convert()
surface.fill(color)
```
::: info
:information_source: **提醒**
`get_size()`取得視窗尺寸
`.convert()`建立副本,加快畫布在視窗顯示的速度。
:::
- 將畫布投影在視窗上
```python=
window.blit(cat_close_image, (0, 0))
window.blit(title_surface, (10, 10))
window.blit(score_surface, (20, 60))
```
:::warning
:warning: **注意**
一樣,`.blit(<surface>, <coordinate>)` 第二組參數是tuple!!!
:::
### 載入圖片
- 匯入圖片
```python=
cat_close_image = pygame.image.load("pic//cat_close.jpg")
cat_open_image = pygame.image.load("pic//cat_open.jpg")
```
:::info
:information_source: **提醒**
如果將絕對相對路徑以地址打比方
**絕對路徑**就像是唸出他家的完整地址。
**相對路徑**就像是說我跟他住同一條路,不過他住對面。
:::
::: warning
:warning: **注意**
圖片也是一種畫布, 也可以直接`.blit()`到畫面上。
:::
### 加入音效
- 匯入音效
```python=
pop_sound = pygame.mixer.Sound("sound//pop.mp3")
```
- 播放音效
```python=
pop_sound.play()
pygame.mixer.Sound.play(pop_sound)
```
### 加入文字
- 建立font物件
- 設定一些關於字型的事
```python=
font = pygame.font.SysFont(font_style, font_size)
font = pygame.font.SysFont(None, 48)
```
- 建立文字畫布
```python=
text_surface = font.render(text, antialias, color)
score_surface = font.render(str(score), True, BLACK)
title_surface = font.render("POPCAT", True, BLACK)
```
:::info
:information_source: **提醒**
antialias指的是反鋸齒,字體會看起來會更滑順。
:::
## 事件處理
### 什麼是事件?
- 事件是遊戲運行中會發生的事情
- 關閉視窗
- 按按鍵
- 用滑鼠
- 我們利用事件來處發程式
### Pygame內的事件處理
#### `pygame.event`
- `pygame.event.get()`是一個紀錄遊戲事件的佇列
- 我們要利用for迴圈去擷取遊戲事件
:::success
想像一下排隊,**佇列**就是排隊隊伍,先到的先出來。
:::
- `pygame.event.pump()`
- 跟`.get()`一樣,但更快
##### 視窗事件
- `pygame.QUIT`是當視窗被關閉的事件
##### 滑鼠事件
* `pygame.MOUSEBUTTONUP`是當滑鼠按鍵被放開的事件
* `pygame.MOUSEBUTTONDOWN`是當滑鼠按鍵被按下的事件
#### `pygame.key.get_pressed()`
- 更陽春一點的偵測
- 對於每次按鍵動作
## Popcat實作!
### 遊戲規則
- 點擊畫面中的貓咪
### 遊戲操作
- `滑鼠左鍵`:+1分
### 程式碼
:::spoiler 不要直接抄喔!!
```python=
## 匯入 pygame
import pygame
## 定義變數
running = True
score = 0
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
## 初始化模組
pygame.init()
## 建立視窗/初始化視窗
window = pygame.display.set_mode((525, 450))
window.fill(WHITE)
pygame.display.set_caption("POPCAT")
## 顯示記分板
font = pygame.font.SysFont(None, 48)
score_surface = font.render(str(score), True, BLACK)
title_surface = font.render("POPCAT", True, BLACK)
## 匯入貓貓圖片
cat_close_image = pygame.image.load("pic//cat_close.jpg").convert()
cat_open_image = pygame.image.load("pic//cat_open.jpg").convert()
## 匯入聲音
pop_sound = pygame.mixer.Sound("sound//pop.mp3")
## 初始化貓貓跟記分板
window.blit(cat_close_image, (0, 0))
window.blit(title_surface, (10, 10))
window.blit(score_surface, (20, 60))
## 主迴圈
while running:
## 遍歷事件佇列
for event in pygame.event.get():
## 事件:關閉視窗
if event.type == pygame.QUIT:
running = False
## 事件:點擊滑鼠
if event.type == pygame.MOUSEBUTTONDOWN:
window.fill(WHITE) # 注意!!!! 刷新畫面!!!! 你可以試試看不刷新會怎樣!
## 更新分數/產生新的記分板
score += 1
score_surface = font.render(str(score), True, BLACK)
## 播放聲音
pop_sound.play()
## 更新貓貓圖片
window.blit(cat_open_image, (0, 0))
## 事件:放開滑鼠
if event.type == pygame.MOUSEBUTTONUP:
window.blit(cat_close_image, (0, 0))
## 顯示記分板
window.blit(title_surface, (10, 10))
window.blit(score_surface, (20, 60))
## 更新畫面
pygame.display.update()
## 離開
pygame.quit()
```
:::
## 我們能不能做一點不一樣的?
### 要是今天我想寫兩隻貓咪上去?
- 最簡單的方式:直接複製第一隻貓咪
- 缺點:很煩、很亂
- 要是我今天要寫更多貓咪?
- 在這裡我們要使用物件:把貓咪設成一個物件
### 物件
- 一個物品,有它的屬性跟它會做的事
:::info
:information_source: **舉例**
- 鉛筆:
- 屬性:
- 長度
- 筆芯硬度
- 方法:
- 寫字
:::
:::success
根據我們剛剛所寫的東西,我們能寫出他的架構
- 貓貓
- 屬性:
- 貓貓的畫布
- 位置
- 圖片
- 聲音
- 方法:
- 初始化
- 播放聲音
- 顯示嘴巴關閉
- 顯示嘴巴打開
- 記分板
- 屬性:
- 字體
- 標題
- 分數
- 位置
- 方法:
- 加分
- 顯示
:::
### 有了物件以後
:::spoiler 加油!有點長!
```python=
## 匯入函式庫
import pygame
import random
running = True
NEARLY_WHITE = (248, 248, 248)
## 初始化模組
pygame.init()
## 貓咪類別
class Popcat(pygame.sprite.Sprite):
## 建構子:
### 參數:
#### path: 存圖片與聲音路徑的tuple
#### size: 圖片大小(用來建立畫布)
###
### 屬性:
#### surf: 顯示貓咪的畫布
#### image: 圖片們
#### sound: 聲音
#### rect: 矩形物件(可以存矩形座標)
def __init__(self, path: tuple, size: tuple):
super().__init__()
self.surf = pygame.Surface(size)
self.image = (pygame.image.load(path[0]), pygame.image.load(path[1]))
self.sound = pygame.mixer.Sound(path[2])
self.rect = self.surf.get_rect() ## 用途在於取得surf在視窗中的座標,用以定位
# 讓貓咪的畫布移動(貓咪的圖片也會跟著動)
def update(self, surface: pygame.Surface):
velocity = (random.randint(-100, 100), random.randint(-100, 100))
x, y = surface.get_size()
self.rect.x += velocity[0]
self.rect.y += velocity[1]
if self.rect.left > x or self.rect.right < 0 or self.rect.top > y or self.rect.bottom < 0:
self.rect.left = x / 2
self.rect.top = y / 2
# 播出聲音的方法
def pop(self):
pygame.mixer.Sound.play(self.sound)
# 張嘴
def mouth_open(self, surface: pygame.Surface):
self.surf.blit(self.image[1], (0, 0))
surface.blit(self.surf, self.rect)
# 閉嘴
def mouth_close(self, surface: pygame.Surface):
self.surf.blit(self.image[0], (0, 0))
surface.blit(self.surf, self.rect)
# 初始化顯示
def init(self, surface: pygame.Surface):
self.mouth_close(surface)
## 記分板類別
class Scoreboard:
## 建構子:
### 參數:
#### font_size: 字體大小
#### title: 標題字樣
#### pos: 位置tuple
###
### 屬性:
#### score: 分數
#### font: 字體物件
#### title: 標題字樣
#### pos: 顯示位置
def __init__(self, font_size: int, title: str, pos: tuple):
self.score = 0
self.font = pygame.font.SysFont(None, font_size)
self.title = title
self.pos = pos
## 顯示
def display(self, surface: pygame.Surface):
surface.blit(self.font.render(self.title, True, (0, 0, 0)), self.pos)
surface.blit(self.font.render(str(self.score), True, (0, 0, 0)), (self.pos[0] + 10, self.pos[1] + 50))
## 加分
def add(self):
self.score += 1
## 一個存有多個貓咪物件的list
cats = [Popcat(
(
"pic//cat_close.jpg",
"pic//cat_open.jpg",
"sound//pop.mp3"),
(525, 450)) for i in range(20)]
## 建立記分板物件
score = Scoreboard(
48,
"POPCAT",
(10, 10)
)
## 建立視窗
screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
screen.fill(NEARLY_WHITE)
pygame.display.set_caption("POPCAT")
## 初始化
score.display(screen)
for cat in cats:
cat.init(screen)
## 主迴圈
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
score.add()
score.display(screen)
screen.fill(NEARLY_WHITE)
for cat in cats:
cat.pop()
cat.mouth_open(screen)
cat.update(screen)
if event.type == pygame.MOUSEBUTTONUP:
for cat in cats:
cat.mouth_close(screen)
score.display(screen)
pygame.display.update()
pygame.quit()
```
:::
## 大挑戰
- 試試看寫出第二隻貓吧!