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游戏编程快速上手

 

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