2013-09-23 46 views
2

我正在製作一個小平臺遊戲,在其中放置塊以創建關卡,然後播放它。將碰撞檢測添加到pygame中的plattformer中

我得到了重力,跳躍和左右運動......但我不確定如何讓玩家在向左或向右移動時與牆壁碰撞。

我希望它的工作方式就像是這個 -

if key[K_LEFT]:

if not block to the left:

move to the left

我怎麼會去這樣做(相對於該源):

import pygame,random 
from pygame.locals import * 
import itertools 
pygame.init() 
screen=pygame.display.set_mode((640,480)) 
class Block(object): 
    sprite = pygame.image.load("texture\\dirt.png").convert_alpha() 
    def __init__(self, x, y): 
     self.rect = self.sprite.get_rect(centery=y, centerx=x) 

class Player(object): 
    sprite = pygame.image.load("texture\\playr.png").convert() 
    sprite.set_colorkey((0,255,0)) 
    def __init__(self, x, y): 
     self.rect = self.sprite.get_rect(centery=y, centerx=x) 

blocklist = [] 
player = [] 
colliding = False 

while True: 
    screen.fill((25,30,90)) 
    mse = pygame.mouse.get_pos() 
    key=pygame.key.get_pressed() 

    if key[K_LEFT]: 
     p.rect.left-=1 
    if key[K_RIGHT]: 
     p.rect.left+=1 
    if key[K_UP]: 
     p.rect.top-=10 

    for event in pygame.event.get(): 
     if event.type == QUIT: exit() 

     if key[K_LSHIFT]: 
      if event.type==MOUSEMOTION: 
       if not any(block.rect.collidepoint(mse) for block in blocklist): 
        x=(int(mse[0])/32)*32 
        y=(int(mse[1])/32)*32 
        blocklist.append(Block(x+16,y+16)) 
     else: 
      if event.type == pygame.MOUSEBUTTONUP: 
       if event.button == 1: 
        to_remove = [b for b in blocklist if b.rect.collidepoint(mse)] 
        for b in to_remove: 
         blocklist.remove(b) 

        if not to_remove: 
         x=(int(mse[0])/32)*32 
         y=(int(mse[1])/32)*32 
         blocklist.append(Block(x+16,y+16)) 

       elif event.button == 3: 
        x=(int(mse[0])/32)*32 
        y=(int(mse[1])/32)*32 
        player=[] 
        player.append(Player(x+16,y+16)) 

    for b in blocklist: 
     screen.blit(b.sprite, b.rect) 
    for p in player: 
     if any(p.rect.colliderect(block) for block in blocklist): 
      #collide 
      pass 
     else: 
      p.rect.top += 1 
     screen.blit(p.sprite, p.rect) 
    pygame.display.flip() 

回答

7

一個普遍的做法是將水平和垂直碰撞處理分成兩個獨立的步驟。

如果你這樣做,並追蹤你的球員的速度,很容易知道碰撞發生在哪一邊。

首先,讓我們給玩家一些屬性來跟蹤他的速度:

class Player(object): 
    ... 
    def __init__(self, x, y): 
     self.rect = self.sprite.get_rect(centery=y, centerx=x) 
     # indicates that we are standing on the ground 
     # and thus are "allowed" to jump 
     self.on_ground = True 
     self.xvel = 0 
     self.yvel = 0 
     self.jump_speed = 10 
     self.move_speed = 8 

我們現在需要一種方法來實際檢查的碰撞。前面已經說了,讓一切變得簡單,我們用我們的xvelyvel知道,如果我們與左側或右側碰撞等,這進入Player類:

def collide(self, xvel, yvel, blocks): 
    # all blocks that we collide with 
    for block in [blocks[i] for i in self.rect.collidelistall(blocks)]: 

     # if xvel is > 0, we know our right side bumped 
     # into the left side of a block etc. 
     if xvel > 0: self.rect.right = block.rect.left 
     if xvel < 0: self.rect.left = block.rect.right 

     # if yvel > 0, we are falling, so if a collision happpens 
     # we know we hit the ground (remember, we seperated checking for 
     # horizontal and vertical collision, so if yvel != 0, xvel is 0) 
     if yvel > 0: 
      self.rect.bottom = block.rect.top 
      self.on_ground = True 
      self.yvel = 0 
     # if yvel < 0 and a collision occurs, we bumped our head 
     # on a block above us 
     if yvel < 0: self.rect.top = block.rect.bottom 

接下來,我們將我們的移動處理,以類Player。所以讓我們創建一個跟蹤輸入的對象。在這裏,我使用了一個namedtuple,因爲爲什麼不。

from collections import namedtuple 
... 
max_gravity = 100 
Move = namedtuple('Move', ['up', 'left', 'right']) 
while True: 
    screen.fill((25,30,90)) 
    mse = pygame.mouse.get_pos() 
    key = pygame.key.get_pressed() 

    for event in pygame.event.get(): 
     ... 

    move = Move(key[K_UP], key[K_LEFT], key[K_RIGHT]) 
    for p in player: 
     p.update(move, blocklist) 
     screen.blit(p.sprite, p.rect) 

我們通過blocklistPlayerupdate方法,所以我們可以檢查碰撞。使用move對象,我們現在知道,球員應移動,所以讓我們實現Player.update

def update(self, move, blocks): 

    # check if we can jump 
    if move.up and self.on_ground: 
     self.yvel -= self.jump_speed 

    # simple left/right movement 
    if move.left: self.xvel = -self.move_speed 
    if move.right: self.xvel = self.move_speed 

    # if in the air, fall down 
    if not self.on_ground: 
     self.yvel += 0.3 
     # but not too fast 
     if self.yvel > max_gravity: self.yvel = max_gravity 

    # if no left/right movement, x speed is 0, of course 
    if not (move.left or move.right): 
     self.xvel = 0 

    # move horizontal, and check for horizontal collisions 
    self.rect.left += self.xvel 
    self.collide(self.xvel, 0, blocks) 

    # move vertically, and check for vertical collisions 
    self.rect.top += self.yvel 
    self.on_ground = False; 
    self.collide(0, self.yvel, blocks) 

唯一剩下的就是用一個Clock限制幀率,讓以恆定的速度遊戲運行。而已。

下面是完整的代碼:

import pygame,random 
from pygame.locals import * 
from collections import namedtuple 

pygame.init() 
clock=pygame.time.Clock() 
screen=pygame.display.set_mode((640,480)) 

max_gravity = 100 

class Block(object): 
    sprite = pygame.image.load("dirt.png").convert_alpha() 
    def __init__(self, x, y): 
     self.rect = self.sprite.get_rect(centery=y, centerx=x) 

class Player(object): 
    sprite = pygame.image.load("dirt.png").convert() 
    sprite.set_colorkey((0,255,0)) 
    def __init__(self, x, y): 
     self.rect = self.sprite.get_rect(centery=y, centerx=x) 
     # indicates that we are standing on the ground 
     # and thus are "allowed" to jump 
     self.on_ground = True 
     self.xvel = 0 
     self.yvel = 0 
     self.jump_speed = 10 
     self.move_speed = 8 

    def update(self, move, blocks): 

     # check if we can jump 
     if move.up and self.on_ground: 
      self.yvel -= self.jump_speed 

     # simple left/right movement 
     if move.left: self.xvel = -self.move_speed 
     if move.right: self.xvel = self.move_speed 

     # if in the air, fall down 
     if not self.on_ground: 
      self.yvel += 0.3 
      # but not too fast 
      if self.yvel > max_gravity: self.yvel = max_gravity 

     # if no left/right movement, x speed is 0, of course 
     if not (move.left or move.right): 
      self.xvel = 0 

     # move horizontal, and check for horizontal collisions 
     self.rect.left += self.xvel 
     self.collide(self.xvel, 0, blocks) 

     # move vertically, and check for vertical collisions 
     self.rect.top += self.yvel 
     self.on_ground = False; 
     self.collide(0, self.yvel, blocks) 

    def collide(self, xvel, yvel, blocks): 
     # all blocks that we collide with 
     for block in [blocks[i] for i in self.rect.collidelistall(blocks)]: 

      # if xvel is > 0, we know our right side bumped 
      # into the left side of a block etc. 
      if xvel > 0: self.rect.right = block.rect.left 
      if xvel < 0: self.rect.left = block.rect.right 

      # if yvel > 0, we are falling, so if a collision happpens 
      # we know we hit the ground (remember, we seperated checking for 
      # horizontal and vertical collision, so if yvel != 0, xvel is 0) 
      if yvel > 0: 
       self.rect.bottom = block.rect.top 
       self.on_ground = True 
       self.yvel = 0 
      # if yvel < 0 and a collision occurs, we bumped our head 
      # on a block above us 
      if yvel < 0: self.rect.top = block.rect.bottom 

blocklist = [] 
player = [] 
colliding = False 
Move = namedtuple('Move', ['up', 'left', 'right']) 
while True: 
    screen.fill((25,30,90)) 
    mse = pygame.mouse.get_pos() 
    key = pygame.key.get_pressed() 

    for event in pygame.event.get(): 
     if event.type == QUIT: exit() 

     if key[K_LSHIFT]: 
      if event.type==MOUSEMOTION: 
       if not any(block.rect.collidepoint(mse) for block in blocklist): 
        x=(int(mse[0])/32)*32 
        y=(int(mse[1])/32)*32 
        blocklist.append(Block(x+16,y+16)) 
     else: 
      if event.type == pygame.MOUSEBUTTONUP: 
       if event.button == 1: 
        to_remove = [b for b in blocklist if b.rect.collidepoint(mse)] 
        for b in to_remove: 
         blocklist.remove(b) 

        if not to_remove: 
         x=(int(mse[0])/32)*32 
         y=(int(mse[1])/32)*32 
         blocklist.append(Block(x+16,y+16)) 

       elif event.button == 3: 
        x=(int(mse[0])/32)*32 
        y=(int(mse[1])/32)*32 
        player=[] 
        player.append(Player(x+16,y+16)) 

    move = Move(key[K_UP], key[K_LEFT], key[K_RIGHT]) 

    for b in blocklist: 
     screen.blit(b.sprite, b.rect) 
    for p in player: 
     p.update(move, blocklist) 
     screen.blit(p.sprite, p.rect) 
    clock.tick(60) 
    pygame.display.flip() 

注意,我改變了形象的名字,所以我只需要測試這一個圖像文件。另外,我不知道爲什麼你保持在列表中的球員,但這裏是我們遊戲的動作漂亮的動畫:

enter image description here

1

由於您使用的是pygame,因此您可以使用pygame的rect.colliderect()來查看玩家的精靈是否與某個塊發生碰撞。然後使該返回側的某矩形是相對於其他矩形的函數:

def rect_side(rect1, rect2): # Returns side of rect1 relative to rect2. 
    if rect1.x > rect2.x: 
     return "right" 
    else: 
     return "left" 
    # If rect1.x == rect2.x the function will return "left". 

比如果rect_side()回報"right"您限制向左移動,反之亦然。如果你想要相同但包括垂直移動,你可以將rect1.yrect2.y進行比較,並處理輸出"up""down"。您可以製作一個代表水平和垂直方向的元組。

+0

但我的目標是在一個列表類..所以我仍然可以使用這種方法? –

+0

添加一個'get_rect()'方法,您可以從精靈的位置和大小返回一個'pygame.rect'對象。 –