iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 29
1
自我挑戰組

Python 30天學習日誌系列 第 29

Day29-PyGame動畫處理

  • 分享至 

  • xImage
  •  

動畫處理的基本架構

動畫是遊戲不可或缺的要素,要讓遊戲角色動起來,遊戲才擁有生命。PyGame套件透過不斷重新繪製繪圖視窗,來呈現物體動起來的感覺。可以回想一下關閉視窗的程式碼,那裡是用無窮迴圈去做的,那麼動畫的基本架構也能被放在那裡,如下:

import pygame as pg
pg.init()

#設定視窗
width, height = 640, 480                      
screen = pg.display.set_mode((width, height))   
pg.display.set_caption("Sean's game")         
bg = pg.Surface(screen.get_size())
bg = bg.convert()
bg.fill((255,255,255))


clock = pg.time.Clock()   #建立時間元件

#關閉程式的程式碼
running = True
while running:
    clock.tick(30)        #每秒執行30次
    for event in pg.event.get():
        if event.type == pg.QUIT:
            running = False
    screen.blit(bg, (0,0))  #重繪視窗
    pg.display.update()     #更新視窗
    
pg.quit()  

這邊建立clock元件,利用它tick()方法來設定每秒重繪次數。當然數值越大,動畫越流暢,但CPU的負擔可就越重喔。那來試試看讓一顆球動起來吧!

水平移動

import pygame as pg
pg.init()

#設定視窗背景
width, height = 640, 480                      
screen = pg.display.set_mode((width, height))   
pg.display.set_caption("Sean's game")         
bg = pg.Surface(screen.get_size())
bg = bg.convert()
bg.fill((255,255,255))

#藍球建立
ball = pg.Surface((70,70))     #建立球矩形繪圖區
ball.fill((255,255,255))       #矩形區塊背景為白色
pg.draw.circle(ball, (0,0,255), (35,35), 35, 0)  #畫藍色球
rect = ball.get_rect()         #取得球矩形區塊
rect.center = (320,240)        #球起始位置
x, y = rect.topleft            #球左上角坐標
speed = 3                      #球運動速度
clock = pg.time.Clock()        #建立時間元件

#關閉程式的程式碼
running = True
while running:
    clock.tick(30)        #每秒執行30次
    for event in pg.event.get():
        if event.type == pg.QUIT:
            running = False
    
    x += speed                 #改變水平位置
    rect.center = (x,y)        #坐標差異讓它移動
    if(rect.left <= 0 or rect.right >= screen.get_width()):   #到達左右邊界
        speed *= -1            #正負值交換

            
    screen.blit(bg, (0,0))
    screen.blit(ball, rect.topleft)  #繪製藍球
    pg.display.update()     #更新視窗
    
pg.quit()

隨機移動

因為關係到隨機跟反彈角度問題,要記得匯入random跟math模組,拿上方例子在稍微做些修改就可以了:

import pygame as pg, random, math
pg.init()

#設定視窗背景
...(略

#藍球建立
ball = pg.Surface((70,70))
ball.fill((255,255,255))
pg.draw.circle(ball, (0,0,255), (35,35), 35, 0)
rect = ball.get_rect()
rect.center = (random.randint(100,250),random.randint(150,250))       #球隨機起始位置
x, y = rect.topleft                #球左上角坐標
direction = random.randint(20,70)  #起始角度
radian = math.radians(direction )  #轉為弳度
dx = 5 * math.cos(radian)          #球水平運動速度
dy = -5 * math.sin(radian)         #球垂直運動速度

clock = pg.time.Clock()
#關閉程式的程式碼
running = True
while running:
    clock.tick(30)        #每秒執行30次
    for event in pg.event.get():
        if event.type == pg.QUIT:
            running = False
    
    x += dx  #改變水平位置
    y += dy  #改變垂直位置
    rect.center = (x,y)
    if(rect.left <= 0 or rect.right >= screen.get_width()):       #到達左右邊界
        dx *= -1    #水平速度變號
    elif(rect.top <= 1 or rect.bottom >= screen.get_height()-1):  #到達上下邊界
        dy *= -1    #垂直速度變號
          
    screen.blit(bg, (0,0))
    screen.blit(ball, rect.topleft) 
    pg.display.update()    
    
pg.quit()

角色類別(Sprite)

在做遊戲時,一定會有許多元件要重複使用,所以用Sprite能創造多個相同的物件,除了複製多個物件,還可以進行動畫繪製及碰撞偵測等。在明天實作中會派上用場,這裡就大概提一下概念語法:

角色群組名稱 = pygame.sprite.Group()
#加入角色物件
角色群組名稱.add(角色物件)
#繪製到畫布上
角色群組名稱.draw(畫布)

簡易舉例:

#建立角色群組
allsprite = pygame.sprite.Group()
ball1 = Ball(8, 100, 100, 10, (0,0,255))  #建立藍球
allsprite.add(ball1)                      #加入角色群組
ball2 = Ball(6, 200, 250, 10, (255,0,0))  #建立紅球
allsprite.add(ball2)

ball1.update()  #物件更新
ball2.update()
allsprite.draw(screen)

碰撞偵測

角色物件的碰撞有很多不同形式的偵測,這邊用兩種剛剛講到的Sprite來進行偵測。

角色與角色碰撞

偵測變數(布林值) = pygame.sprite.collide_rect(角色物件1,角色物件2)

偵測變數為布林值,True為有發生碰撞,False則是沒有。

角色與群組碰撞

偵測變數(布林值) = pygame.sprite.spritecollide(角色物件,角色群組,移除值)

移除值為布林值,True會將碰撞到的角色從群組中刪除,False則是沒有。

def collidebounce(self):     #定義碰撞後造成反彈
    self.dx *= -1
    
result = pygame.sprite.collide_rect(ball1,ball2)
if result == True:           #假如碰撞到,反彈
    ball1.collidebounce()
    ball2.collidebounce()

滑鼠事件

最後一個應用在我實作上的是滑鼠事件,雖然還有個鍵盤事件,但我沒用到也就不多提了XD~
最主要分為以下兩類:

  • 滑鼠按鈕事件
    pygame.mouse.get_pressed()會回傳按鈕狀態串列,若值為True的話,就表示按鈕被按。
    按鈕索引的部分,0表示按滑鼠左鍵,1表示按滑鼠中鍵,2表示按滑鼠右鍵。
按鈕變數 = pygame.mouse.get_pressed()
if 按鈕變數 [按鈕索引]:
    程式碼...
  • 滑鼠滑動事件
    pygame.mouse.get_pos()會回傳目前滑鼠位置的坐標串列,位置變數為x,y坐標。
位置變數 = pygame.mouse.get_pos()

完整滑鼠事件例子:

import pygame as pg, random, math
pg.init()

#設定視窗背景
width, height = 640, 480                      
screen = pg.display.set_mode((width, height))   
pg.display.set_caption("Sean's game")         
bg = pg.Surface(screen.get_size())
bg = bg.convert()
bg.fill((255,255,255))

#藍球建立
ball = pg.Surface((70,70))
ball.fill((255,255,255))
pg.draw.circle(ball, (0,0,255), (35,35), 35, 0)
rect = ball.get_rect()
rect.center = (320,240)     
x, y = rect.topleft              
clock = pg.time.Clock()


#關閉程式的程式碼

running = True
playing = False  #開始時球不能移動
while running:
    clock.tick(30)        #每秒執行30次
    for event in pg.event.get():
        if event.type == pg.QUIT:
            running = False
    buttons = pg.mouse.get_pressed()
    if buttons[0]:          #按滑鼠左鍵後球可移動
        playing = True
    elif buttons[2]:        #按滑鼠右鍵後球不能移動
        playing = False
    if playing == True:     #球可移動狀態
        mouses = pg.mouse.get_pos()  #取得滑鼠坐標
        rect.centerx = mouses[0]     #移動滑鼠
        rect.centery = mouses[1]
    screen.blit(bg, (0,0))
    screen.blit(ball, rect.topleft) 
    pg.display.update()    
    
pg.quit()

明天最後一天嘞~來看看我最終成果吧~~/images/emoticon/emoticon42.gif

參考資料

  • 書名-Python初學特訓班 /文淵閣工作室-編著
  • 書名-輕鬆學Python3 /孫宏明-編著

上一篇
Day28-PyGame基本操作
下一篇
Day30-實作簡易打磚塊遊戲
系列文
Python 30天學習日誌30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言