Python遊戲編程(十一)Collision Dectection

碰撞檢測(collision detection)負責計算屏幕上地兩個物體合適發生彼此接觸(也就是發生碰撞)。碰撞檢測在遊戲中應用還是比較多的,不如玩家接觸敵人損失生命值,得到金幣增加遊戲金錢等等。

在這個程序中,碰撞檢測將判斷兩個矩形是否彼此重疊。

目錄

(一)循環準備

1)導入模塊

2)使用一個時鐘來同步程序

3)創建窗口和數據結構

4)設置變量以記錄移動

(二)遊戲循環

1)處理事件

處理KEYDOWN事件

處理KEYUP事件

處理MOUSEBUTTONUP事件

2)添加新的食物塊

3)在屏幕上移動玩家

將玩家繪製到屏幕上

檢查碰撞

繪製方塊、收尾


主要內容:

  • 碰撞檢測;
  • pygame中地鍵盤輸入;
  • pygame中地鼠標輸入;
  • 當遍歷一個列表的時候不要修改它。

 

(一)循環準備

import pygame, sys, random
from pygame.locals import *

 # Set up pygame.
pygame.init()
mainClock = pygame.time.Clock()

# Set up the window.
WINDOWWIDTH = 400
WINDOWHEIGHT = 400
windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT),
       0, 32)
pygame.display.set_caption('Collision Detection')

# Set up the colors.
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
WHITE = (255, 255, 255)

# Set up the player and food data structures.
foodCounter = 0
NEWFOOD = 40
FOODSIZE = 20
player = pygame.Rect(300, 100, 50, 50)
foods = []
for i in range(20):
    foods.append(pygame.Rect(random.randint(0, WINDOWWIDTH - FOODSIZE),
           random.randint(0, WINDOWHEIGHT - FOODSIZE), FOODSIZE, FOODSIZE))

# Set up movement variables.
moveLeft = False
moveRight = False
moveUp = False
moveDown = False

MOVESPEED = 6

1)導入模塊

import pygame, sys, random
from pygame.locals import *

 # Set up pygame.
pygame.init()

2)使用一個時鐘來同步程序

mainClock = pygame.time.Clock()

一個程序運行的快慢取決於計算機的快慢。如果你想要程序在任何的計算機上都以相同的速度運行,我們需要一個函數能夠對於較快的計算機暫停的時間長一點,而對於較慢的計算機暫停的時間短一些。

pygame.time.Clock對象可以對任何計算機都暫停適當的時間。

3)創建窗口和數據結構

# Set up the player and food data structures.
foodCounter = 0
NEWFOOD = 40
FOODSIZE = 20
player = pygame.Rect(300, 100, 50, 50)
foods = []
for i in range(20):
    foods.append(pygame.Rect(random.randint(0, WINDOWWIDTH - FOODSIZE),
           random.randint(0, WINDOWHEIGHT - FOODSIZE), FOODSIZE, FOODSIZE))

首先是爲食物方塊創建了一些變量。

player = pygame.Rect爲玩家的位置設置了一個pygame.Rect對象,它表示方塊的大小和位置。

通過for循環,調用pygame.Rect()構造函數來返回一個新的pygame.Rect對象,用來表示食物方塊的位置和大小。並將其添加到foods中去。其中要注意我們想要一個隨機的座標,它在0和窗口大小減去食物方塊大小所得結果之間。如果使用了0到窗口大小之間的一個隨機座標,那麼可能會把食物方塊推到了窗口之外。

4)設置變量以記錄移動

# Set up movement variables.
moveLeft = False
moveRight = False
moveUp = False
moveDown = False

MOVESPEED = 6

以上代碼創建了記錄玩家方塊在每一個方向上的移動的一些變量。

這4個布爾值變量用來記錄按下了哪個方向鍵。例如,當用戶按下鍵盤上向左的方向鍵時,把moveLeft設置爲True。當鬆開這個鍵時,把moveLeft設置回False。

MOVESPEED = 5用於設置移動速度。

 

(二)遊戲循環

# Run the game loop.
while True:
     # Check for events.
     for event in pygame.event.get():
         
         if event.type == QUIT:
             
             pygame.quit()
             
             sys.exit()
         if event.type == KEYDOWN:
             # Change the keyboard variables.
             if event.key == K_LEFT or event.key == K_a:
                 moveRight = False
                 moveLeft = True
             if event.key == K_RIGHT or event.key == K_d:
                 moveLeft = False
                 moveRight = True
             if event.key == K_UP or event.key == K_w:
                 moveDown = False
                 moveUp = True
             if event.key == K_DOWN or event.key == K_s:
                 moveUp = False
                 moveDown = True
         if event.type == KEYUP:
             if event.key == K_ESCAPE:
                 pygame.quit()
                 sys.exit()
             if event.key == K_LEFT or event.key == K_a:
                 moveLeft = False
             if event.key == K_RIGHT or event.key == K_d:
                 moveRight = False
             if event.key == K_UP or event.key == K_w:
                 moveUp = False
             if event.key == K_DOWN or event.key == K_s:
                 moveDown = False
             if event.key == K_x:
                 player.top = random.randint(0, WINDOWHEIGHT -
                       player.height)
                 player.left = random.randint(0, WINDOWWIDTH -
                       player.width)

         if event.type == MOUSEBUTTONUP:
             foods.append(pygame.Rect(event.pos[0], event.pos[1],
                   FOODSIZE, FOODSIZE))

     foodCounter += 1
     if foodCounter >= NEWFOOD:
         # Add new food.
         foodCounter = 0
         foods.append(pygame.Rect(random.randint(0, WINDOWWIDTH -
               FOODSIZE), random.randint(0, WINDOWHEIGHT - FOODSIZE),
               FOODSIZE, FOODSIZE))

     # Draw the white background onto the surface.
     windowSurface.fill(WHITE)

     # Move the player.
     if moveDown and player.bottom < WINDOWHEIGHT:
         player.top += MOVESPEED
     if moveUp and player.top > 0:
         player.top -= MOVESPEED
     if moveLeft and player.left > 0:
         player.left -= MOVESPEED
     if moveRight and player.right < WINDOWWIDTH:
         player.right += MOVESPEED

     # Draw the player onto the surface.
     pygame.draw.rect(windowSurface, BLACK, player)

     # Check whether the player has intersected with any food squares.
     for food in foods[:]:
         
         if player.colliderect(food):
             foods.remove(food)

     # Draw the food.
     for i in range(len(foods)):
         pygame.draw.rect(windowSurface, GREEN, foods[i])

     # Draw the window onto the screen.
     pygame.display.update()
     mainClock.tick(40)

1)處理事件

pygame模塊可以產生事件以響應來自鼠標或者鍵盤的用戶輸入。如下是常見的事件。

事件   產生途徑    參數
QUIT 用戶按下關閉按鈕    none
ATIVEEVENT     Pygame被激活或者隱藏     gain, state
KEYDOWN     鍵盤被按下     unicode, key, mod
KEYUP     鍵盤被放開     key, mod
MOUSEMOTION     鼠標移動   pos, rel, buttons
MOUSEBUTTONDOWN     鼠標按下     pos, button
MOUSEBUTTONUP     鼠標放開     pos, button
JOYAXISMOTION     遊戲手柄(Joystick or pad) 移動     joy, axis, value
JOYBALLMOTION    遊戲球(Joy ball) 移動    joy, axis, value
JOYHATMOTION    遊戲手柄(Joystick) 移動     joy, axis, value
JOYBUTTONDOWN     遊戲手柄按下     joy, button
JOYBUTTONUP     遊戲手柄放開     joy, button
VIDEORESIZE    Pygame窗口縮放     size, w, h
VIDEOEXPOSE    Pygame窗口部分公開(expose)  none
USEREVENT     觸發了一個用戶事件    code

MOUSEMOTION事件會在鼠標動作的時候發生,它有三個參數:

buttons 一個含有三個數字的元組,三個值分別代表左鍵、中鍵和右鍵,1就是按下了。
pos 位置
rel 代表了現在距離上次產生鼠標事件時的距離

和MOUSEMOTION類似的,我們還有MOUSEBUTTONDOWNMOUSEBUTTONUP兩個事件。它們的參數爲:

button 這個值代表了哪個按鍵被操作
pos 位置

鍵盤的事件爲KEYDOWN和KEYUP :

key 按下或者放開的鍵值,是一個數字,Pygame中可以使用K_xxx來表示,比如字母a就是K_a,還有K_SPACE和K_RETURN等。
mo 包含了組合鍵信息,如果mod & KMOD_CTRL是真的話,表示用戶同時按下了Ctrl鍵。類似的還有KMOD_SHIFT,KMOD_ALT。
unicode 代表了按下鍵的Unicode值

Key中鍵盤按鍵的常量變量可查看Pygame.locals常量

 

常用事件函數:

  •     pygame.event.pump()  —  讓 Pygame 內部自動處理事件
  •     pygame.event.get()  —  從隊列中獲取事件
  •     pygame.event.poll()  —  從隊列中獲取一個事件
  •     pygame.event.wait()  —  等待並從隊列中獲取一個事件
  •     pygame.event.peek()  —  檢測某類型事件是否在隊列中
  •     pygame.event.clear()  —  從隊列中刪除所有的事件
  •     pygame.event.event_name()  —  通過 id 獲得該事件的字符串名字
  •     pygame.event.set_blocked()  —  控制哪些事件禁止進入隊列
  •     pygame.event.set_allowed()  —  控制哪些事件允許進入隊列
  •     pygame.event.get_blocked()  —  檢測某一類型的事件是否被禁止進入隊列
  •     pygame.event.set_grab()  —  控制輸入設備與其他應用程序的共享
  •     pygame.event.get_grab()  —  檢測程序是否共享輸入設備
  •     pygame.event.post()  —  放置一個新的事件到隊列中
  •     pygame.event.Event()  —  創建一個新的事件對象
  •     pygame.event.EventType  —  代表 SDL 事件的 Pygame 對象
     

處理KEYDOWN事件

if event.type == KEYDOWN:
             # Change the keyboard variables.
             if event.key == K_LEFT or event.key == K_a:
                 moveRight = False
                 moveLeft = True
             if event.key == K_RIGHT or event.key == K_d:
                 moveLeft = False
                 moveRight = True
             if event.key == K_UP or event.key == K_w:
                 moveDown = False
                 moveUp = True
             if event.key == K_DOWN or event.key == K_s:
                 moveUp = False
                 moveDown = True

以上代碼實現:當按下A鍵時,就會改變記錄移動變量的布爾值。按下DWS按鍵時同理。

處理KEYUP事件

         if event.type == KEYUP:
             if event.key == K_ESCAPE:
                 pygame.quit()
                 sys.exit()
             if event.key == K_LEFT or event.key == K_a:
                 moveLeft = False
             if event.key == K_RIGHT or event.key == K_d:
                 moveRight = False
             if event.key == K_UP or event.key == K_w:
                 moveUp = False
             if event.key == K_DOWN or event.key == K_s:
                 moveDown = False
             if event.key == K_x:
                 player.top = random.randint(0, WINDOWHEIGHT -
                       player.height)
                 player.left = random.randint(0, WINDOWWIDTH -
                       player.width)

當釋放按下的鍵時,會觸發KEYUP事件。

如果用戶釋放的是ESC鍵,那麼程序終止。記住,在pygame中在調用sys.exit()之前,必須先調用pygame.quit()函數。

如果是該方向的按鍵是釋放狀態的話,就會改變變量爲False。

如果玩家釋放的是X鍵,則將用戶的方塊隨機的放在一個位置。

處理MOUSEBUTTONUP事件

通過點擊鼠標添加新的食物塊。當鼠標釋放時,會觸發MOUSEBUTTONUP事件。

         if event.type == MOUSEBUTTONUP:
             foods.append(pygame.Rect(event.pos[0], event.pos[1],
                   FOODSIZE, FOODSIZE))

如果鼠標進行了點擊,就在該位置添加一個食物塊。

2)添加新的食物塊

可以使用鼠標添加,也可以設置自動添加。

         if event.type == MOUSEBUTTONUP:
             foods.append(pygame.Rect(event.pos[0], event.pos[1],
                   FOODSIZE, FOODSIZE))

     foodCounter += 1
     if foodCounter >= NEWFOOD:
         # Add new food.
         foodCounter = 0
         foods.append(pygame.Rect(random.randint(0, WINDOWWIDTH -
               FOODSIZE), random.randint(0, WINDOWHEIGHT - FOODSIZE),
               FOODSIZE, FOODSIZE))

NEWFOOD爲食物塊添加的速度。

3)在屏幕上移動玩家

每次循環之後需要用白色windowSurface.fill(WHITE)填充屏幕。

我們已經根據用戶的按鍵,設置了移動變量(moveDown、moveUp、moveLeft、和moveRight)。現在,通過調整玩家的X座標和Y座標,來移動玩家的方塊(用存儲在player中的pygame.Rect對象來表示)。

     # Draw the white background onto the surface.
     windowSurface.fill(WHITE)

     # Move the player.
     if moveDown and player.bottom < WINDOWHEIGHT:
         player.top += MOVESPEED
     if moveUp and player.top > 0:
         player.top -= MOVESPEED
     if moveLeft and player.left > 0:
         player.left -= MOVESPEED
     if moveRight and player.right < WINDOWWIDTH:
         player.right += MOVESPEED

將玩家繪製到屏幕上

     # Draw the player onto the surface.
     pygame.draw.rect(windowSurface, BLACK, player)
  • windowSurface:告訴Python要將矩形繪製到哪一個Surface對象上。
  • BLACK:RGB顏色值(0,0,0)
  • player:存儲着Rect對象,告訴P櫻桃紅要繪製的矩形的位置和大小。

檢查碰撞

使用Rect對象都擁有的碰撞檢測方法colliderect():

     # Check whether the player has intersected with any food squares.
     for food in foods[:]:
         
         if player.colliderect(food):
             foods.remove(food)

其中foods[ : ]是foods列表的一個副本。

繪製方塊、收尾

     # Draw the food.
     for i in range(len(foods)):
         pygame.draw.rect(windowSurface, GREEN, foods[i])

     # Draw the window onto the screen.
     pygame.display.update()
     mainClock.tick(40)

其中mainClock.tick(40):調用我們前面前面創建的Clock對象的方法,調整程序運時間。

 

 

 

 

 

 

參考:

  1. https://blog.csdn.net/qq_41556318/article/details/86303039
  2. https://blog.csdn.net/Hubz131/article/details/86718684
  3. Python遊戲編程快速上手

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章