--- 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)