碰撞檢測(collision detection)負責計算屏幕上地兩個物體合適發生彼此接觸(也就是發生碰撞)。碰撞檢測在遊戲中應用還是比較多的,不如玩家接觸敵人損失生命值,得到金幣增加遊戲金錢等等。
在這個程序中,碰撞檢測將判斷兩個矩形是否彼此重疊。
目錄
主要內容:
- 碰撞檢測;
- 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類似的,我們還有MOUSEBUTTONDOWN和MOUSEBUTTONUP兩個事件。它們的參數爲:
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對象的方法,調整程序運時間。
參考:
- https://blog.csdn.net/qq_41556318/article/details/86303039
- https://blog.csdn.net/Hubz131/article/details/86718684
- Python遊戲編程快速上手