2013-09-23 5 views
2

Я работаю над небольшой платформерной игрой, в которой вы размещаете блоки, чтобы сделать уровень, а затем воспроизводите его.Добавить обнаружение столкновения в plattformer в pygame

У меня есть гравитация, прыжки, движение влево и вправо ... но я не уверен, как заставить игрока столкнуться со стенами при движении влево или вправо.

Я так хочу, чтобы работать, как this-

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 

Теперь нам нужен способ, чтобы на самом деле проверить столкновения. Как уже было сказано, чтобы сделать вещи легко, мы используем наши xvel и yvel знать, если мы столкнулись с нашей левой или с правой стороны и т.д. Это входит в 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) 

Проезжает blocklist к update способу Player таким образом мы можем проверить столкновение. Используя 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's 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" ограничить движение влево, и наоборот.

PS: Если вы хотите, то же самое, но в том числе вертикальное движение можно сравнить rect1.y с rect2.y и иметь дело с выходами "up" и "down". Вы можете сделать кортеж, представляющий горизонтальные и вертикальные направления.

+0

Но мои объекты классов в списке .. так могу ли я использовать Этот метод? –

+0

Добавьте метод 'get_rect()' где вы возвращаете объект 'pygame.rect' из позиции и размера спрайтов. –

Смежные вопросы