本系列部落格介紹以python+pygame庫進行小遊戲的開發。有寫的不對之處還望各位海涵。
上一個部落格我們一起學習了pygame中的Sprite子產品和如何加載動畫:http://www.cnblogs.com/msxh/p/5013555.html
這次我們來一起學習pygame中的沖突檢測技術。
pygame支援非常多的沖突檢測技術,我們來一一的看一下他們是如何使用的:
一、精靈與精靈之間的沖突檢測
1.兩個精靈之間的矩形檢測
在隻有兩個精靈的時候我們可以使用pygame.sprite.collide_rect()函數來進行一對一的沖突檢測。這個函數需要傳遞2個參數,并且每個參數都是需要繼承自pygame.sprite.Sprite。
舉個例子:
spirte_1 = MySprite("sprite_1.png",200,200,1)
sprite_2 = MySprite("sprite_2.png",50,50,1)
result = pygame.sprite.collide_rect(sprite_1,sprite_2)
if result:
print "Collision occurred"
MySprite使我們上個部落格中建立的類,他繼承自sprite。
Hint:這個函數還有一個非常有用的變體:pygame.sprite.collide_rect_ratio()。這個函數需要一個額外的浮點類型的參數。這個參數用來指定檢測矩形的百分比。
有的時候我們希望沖突檢測更精準一些的話,就可以收縮檢測的區域,讓矩形更小一些,就是通過這個參數控制的。使用方法如下:
result = pygame.sprite.collide_rect_ratio( 0.5 )(sprite_1,sprite_2)
2.兩個精靈之間的圓檢測
矩形沖突檢測并不适用于所有形狀的精靈,是以pygame中還有個圓形沖突檢測。pygame.sprite.collide_circle(),這個函數是基于每個精靈的半徑值來進行檢測的。
你可以自己指定半徑,或者讓函數自己計算半徑。
result = pygame.sprite.collide_circle(sprite_1,sprite_2)
if result:
print "Collision occurred"
這個函數也有一個變體:pygame.sprite.collide_circle_ratio()。函數的功能和用法和上面的pygame.sprite.collide_rect_ratio()是類似的。
3.兩個精靈之間的像素遮罩檢測
如果矩形檢測和圓形檢測都不能滿足我們的需求怎麼辦?别擔心,pygame還為我們提供了一個更加精确的檢測:pygame.sprite.collide_mask()。
這個函數接收兩個精靈作為參數,傳回值是一個bool變量。
if pygame.sprite.collide_mask(sprite_1,sprite_2):
print ("Collision occurred")
4.精靈群組之間的矩形沖突檢測
pygame.sprite.spritecollide(sprite,sprite_group,bool)。調用這個函數的時候,一個組中的所有精靈都會逐個地對另外一個單個精靈進行沖突檢測,發生沖突的精靈會作為一個清單傳回。
這個函數的第一個參數就是單個精靈,第二個參數是精靈組,第三個參數是一個bool值,最後這個參數起了很大的作用。當為True的時候,會删除組中所有沖突的精靈,False的時候不會删除沖突的精靈
list_collide = pygame.sprite.spritecollide(sprite,sprite_group,False);
另外這個函數也有一個變體:pygame.sprite.spritecollideany()。這個函數在判斷精靈組和單個精靈沖突的時候,會傳回一個bool值。
5.精靈組之間的矩形沖突檢測
pygame.sprite.groupcollide()。利用這個函數可以檢測兩個組之間的沖突,他傳回一個字典。(鍵-值對)
好了大概常用的幾種沖突檢測函數我們已經了解完了,下面我們做一個小小的執行個體實際運用一下上面學到的知識。
二、沖突檢測執行個體---吃蘋果小遊戲
先看一下效果圖:
遊戲開始會在螢幕上随機生成一些蘋果,玩家通過上下左右方向鍵來控制人物去吃蘋果。
吃到一個蘋果,能量條就會增長一些,直到吃完所有的蘋果,遊戲結束。
【源代碼+素材下載下傳位址】
網盤下載下傳位址:http://yunpan.cn/c3SttjmY2yYPk 通路密碼 4a7b
github位址:https://github.com/XINCGer/Eat-apple-Game
1.子產品化程式設計
這個遊戲會使用到我們上個部落格建立的MySprite類,為了讓這個類變的更具有可重用性,我們将它做成一個子產品。
隻要将類的實作代碼放進一個單獨的py,然後在使用的時候引入他就可以了。比如我們将這個單獨的py取名為:MyLibrary.py
import MyLibrary
這樣在使用這個子產品裡面的函數和類的時候我們隻需要這樣做:MyLibrary.fun()。但是這樣看起來也不是很友善的說,是以我們使用import的變體:
from MyLibrary import *
#将檔案中的所有内容引入
2.進階行走動畫
通過效果圖,我們可以看到程式裡面用到了進階的行走動畫,人物一共有上下左右四個方向的行走動畫。
實際上這個精靈序列圖裡面一共有8個方向的行走動畫,為了簡便,我們隻是使用了其中的四方向,如圖:
通過行的數目就可以來友善的區分,動畫是向左走還是向右走的。現在說起來可能有點比較難以了解,看完下面的代碼就比較好了解了。我們還為Mysprite這個類增加了一個velocity屬性,以便精靈可以根據其方向來移動。
class MySprite(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.master_image = None
self.frame = 0
self.old_frame = -1
self.frame_width = 1
self.frame_height = 1
self.first_frame = 0
self.last_frame = 0
self.columns = 1
self.last_time = 0
self.direction = 0
#新增了velocity屬性,他是一個point
self.velocity = Point(0.0,0.0)
當按UP鍵的時候,将方向設定為0(向上),按DOWN鍵的時候,将方向設定為4(向下),按LEFT鍵,将方向設定為6(向左),按RIGHT鍵,将方向設定為2(向右)
if keys[K_ESCAPE]: sys.exit()
elif keys[K_UP] or keys[K_w]:
player.direction = 0
player_moving = True
elif keys[K_RIGHT] or keys[K_d]:
player.direction = 2
player_moving = True
elif keys[K_DOWN] or keys[K_s]:
player.direction = 4
player_moving = True
elif keys[K_LEFT] or keys[K_a]:
player.direction = 6
player_moving = True
這個方向就是我們之前說的用來決定使用動畫幀的範圍方法。并且還有一個player_moving變量,在按鍵按下的時候将它置為True,也就是按鍵按下的時候才會有行走動畫,否則人物将會是靜止的。
3.判斷人物與蘋果的沖突
為了獲得更精準的沖突,我們組合使用了不同的沖突函數。
首先用pygame.sprite.spritecollideany來判斷玩家是否與任意的蘋果産生了碰撞,如果産生碰撞,則再使用pygame.sprite.collide_circle_ratio縮小檢測範圍做一次檢測,
看看到底是哪個蘋果和人物産生了沖突,然後将産生碰撞的果實從精靈組中移除(remove)。
#檢測玩家是否與食物沖突,是否吃到果實
attacker = None
attacker = pygame.sprite.spritecollideany(player, food_group)
if attacker != None:
if pygame.sprite.collide_circle_ratio(0.65)(player,attacker):
player_health +=2;
food_group.remove(attacker);
吃了果實以後,能量值會增加,然後我們通過繪制一個矩形的能量條來反映給使用者。
好了最後上一下全部的源代碼(不包含MyLibrary子產品):
import itertools, sys, time, random, math, pygame
from pygame.locals import *
from MyLibrary import *
def calc_velocity(direction, vel=1.0):
velocity = Point(0,0)
if direction == 0: #上
velocity.y = -vel
elif direction == 2: #右
velocity.x = vel
elif direction == 4: #下
velocity.y = vel
elif direction == 6: #左
velocity.x = -vel
return velocity
pygame.init()
screen = pygame.display.set_mode((800,600))
pygame.display.set_caption("吃蘋果")
font = pygame.font.Font(None, 36)
timer = pygame.time.Clock()
#建立精靈組
player_group = pygame.sprite.Group()
food_group = pygame.sprite.Group()
#初始化玩家精靈組
player = MySprite()
player.load("farmer walk.png", 96, 96, 8)
player.position = 80, 80
player.direction = 4
player_group.add(player)
#初始化food精靈組
for n in range(1,50):
food = MySprite();
food.load("food_low.png", 35, 35, 1)
food.position = random.randint(0,780),random.randint(0,580)
food_group.add(food)
game_over = False
player_moving = False
player_health = 0
while True:
timer.tick(30)
ticks = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
keys = pygame.key.get_pressed()
if keys[K_ESCAPE]: sys.exit()
elif keys[K_UP] or keys[K_w]:
player.direction = 0
player_moving = True
elif keys[K_RIGHT] or keys[K_d]:
player.direction = 2
player_moving = True
elif keys[K_DOWN] or keys[K_s]:
player.direction = 4
player_moving = True
elif keys[K_LEFT] or keys[K_a]:
player.direction = 6
player_moving = True
else:
player_moving = False
if not game_over:
#根據角色的不同方向,使用不同的動畫幀
player.first_frame = player.direction * player.columns
player.last_frame = player.first_frame + player.columns-1
if player.frame < player.first_frame:
player.frame = player.first_frame
if not player_moving:
#當停止按鍵(即人物停止移動的時候),停止更新動畫幀
player.frame = player.first_frame = player.last_frame
else:
player.velocity = calc_velocity(player.direction, 1.5)
player.velocity.x *= 1.5
player.velocity.y *= 1.5
#更新玩家精靈組
player_group.update(ticks, 50)
#移動玩家
if player_moving:
player.X += player.velocity.x
player.Y += player.velocity.y
if player.X < 0: player.X = 0
elif player.X > 700: player.X = 700
if player.Y < 0: player.Y = 0
elif player.Y > 500: player.Y = 500
#檢測玩家是否與食物沖突,是否吃到果實
attacker = None
attacker = pygame.sprite.spritecollideany(player, food_group)
if attacker != None:
if pygame.sprite.collide_circle_ratio(0.65)(player,attacker):
player_health +=2;
food_group.remove(attacker);
if player_health > 100: player_health = 100
#更新食物精靈組
food_group.update(ticks, 50)
if len(food_group) == 0:
game_over = True
#清屏
screen.fill((50,50,100))
#繪制精靈
food_group.draw(screen)
player_group.draw(screen)
#繪制玩家血量條
pygame.draw.rect(screen, (50,150,50,180), Rect(300,570,player_health*2,25))
pygame.draw.rect(screen, (100,200,100,180), Rect(300,570,200,25), 2)
if game_over:
print_text(font, 300, 100, "G A M E O V E R")
pygame.display.update()
在下個部落格裡面我們将一起學習在遊戲裡面常用的一些資料結構: 資料,清單,元組,隊列,棧。