# 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() ``` ::: ## 大挑戰 - 試試看寫出第二隻貓吧!