将碰撞检测添加到pygame中的plattformer [英] Add collision detection to a plattformer in pygame

查看:113
本文介绍了将碰撞检测添加到pygame中的plattformer的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一款小型平台游戏,在其中您可以放置​​积木来制作关卡,然后再玩。

I'm working on a small platformer game in which you place blocks to make a level, then play it.

我有重力,跳跃,左右移动向右移动..但是我不确定在左右移动时如何使玩家与墙壁碰撞。

I got gravity, jumping, and left and right movement.. but I am not sure how to make the player collide with walls when moving left or right.

我希望它的工作方式如下-

The way I want it to work is like this-

如果键[K_LEFT]:

如果没有向左移动:

向左移动

我将如何去做(相对于此来源):

How would I go about doing this (relative to this source):

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()


推荐答案

常见的做法是将水平和垂直碰撞处理分为两个单独的步骤。

A common approch is to seperate the horizontal and vertical collision handling into two seperate steps.

如果您这样做并跟踪玩家的速度,很容易知道发生碰撞的那一侧。

If you do this and also track the velocity of your player, it's easy to know on which side a collision happened.

首先,让我们为玩家提供一些属性以跟踪其速度:

First of all, let's give the player some attributes to keep track of his velocity:

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 类:

Now we need a method to actually check for a collision. As already said, to make things easy, we use our xvel and yvel to know if we collided with our left or right side etc. This goes into the Player class:

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

接下来,我们将运动处理移至播放器类。因此,让我们创建一个跟踪输入的对象。在这里,我使用了 namedtuple ,因为为什么不这样做。

Next, we move our movement handling to the Player class. So let's create on object that keeps track of the input. Here, I use a namedtuple, because why not.

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)

我们通过了阻止列表 Player update 方法,这样我们就可以检查碰撞。使用 move 对象,我们现在知道播放器应该移动到哪里,所以让我们实现 Player.update

We pass the blocklist to the update method of the Player so we can check for collision. Using the move object, we now know where the player should move, so let's implement 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)

唯一剩下的是使用 时钟 限制帧率,以使游戏以恒定速度运行。而已。

The only thing left is to use a Clock to limit the framerate to let the game run at a constant speed. That's it.

这是完整的代码:

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()

请注意,我更改了图像名称,因此我只需要一个图像文件即可对此进行测试。另外,我不知道您为什么将玩家保留在列表中,但是这是我们的游戏的动画效果:

Note that I changed the image names so I just need a single image file for testing this. Also, I don't know why you keep the player in a list, but here's a nice animation of our game in action:

这篇关于将碰撞检测添加到pygame中的plattformer的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆