如何在pygame中修复碰撞代码?

时间:2018-03-26 00:11:39

标签: python-3.x pygame collision-detection

以下是我的直接问题:如何使用我的代码在pygame中进行碰撞?

我花了最近几天试图在我的游戏中添加碰撞,但无济于事。即使按照类似线程提供的其他答案,我所做的每一次尝试都没有按预期工作。在我目前的实现中,玩家只需通过矩形弹跳到另一侧。我可以看到碰撞被检测到,但结果并不像预期的那样。我只是希望我的玩家不会穿过平台并站在他们身上,或者如果他们撞到他的头,他就会摔倒。我的代码在下面是我当前的碰撞检测工作。

import pygame as pg
from settings import Music_Mixer, loadCustomFont, States, screen, GROUND_HEIGHT
from time import sleep

"""This section contains entity states which are separate from game and menu states."""
class Player(pg.sprite.Sprite):
    def __init__(self, x, y):
        pg.sprite.Sprite.__init__(self)
        self.health = 100
        self.speed = 1
        self.screen = screen

        self.pos_x = x
        self.pos_y = y

        self.running = False
        self.is_jumping = False

        self.velocity = 15
        self.mass = 3

        #List of pictures for animations.
        stick_still = pg.image.load('Images/Animations/PlayerRun/Stickman_stand_still.png').convert_alpha()
        stick_still_2 = pg.image.load('Images/Animations/PlayerRun/Stickman_stand_still_2.png').convert_alpha()

        #Right running pictures.
        stick_run_1_right = pg.image.load('Images/Animations/PlayerRun/Stickman_run_1.png').convert_alpha()
        stick_run_2_right = pg.image.load('Images/Animations/PlayerRun/Stickman_run_2.png').convert_alpha()
        stick_run_3_right = pg.image.load('Images/Animations/PlayerRun/Stickman_run_3.png').convert_alpha()
        stick_run_4_right = pg.image.load('Images/Animations/PlayerRun/Stickman_run_4.png').convert_alpha()
        stick_run_5_right = pg.image.load('Images/Animations/PlayerRun/Stickman_run_4.png').convert_alpha()

        #Left running pictures.
        stick_run_1_left = pg.image.load('Images/Animations/PlayerRun/Stickman_run_1_left.png').convert_alpha()
        stick_run_2_left = pg.image.load('Images/Animations/PlayerRun/Stickman_run_2_left.png').convert_alpha()
        stick_run_3_left = pg.image.load('Images/Animations/PlayerRun/Stickman_run_3_left.png').convert_alpha()
        stick_run_4_left = pg.image.load('Images/Animations/PlayerRun/Stickman_run_4_left.png').convert_alpha()
        stick_run_5_left = pg.image.load('Images/Animations/PlayerRun/Stickman_run_4_left.png').convert_alpha()

        #Lists for animation movement.
        self.STICKMAN_IDLE = [stick_still]
        self.STICKMAN_RUN_RIGHT = [stick_run_1_right, stick_run_2_right, stick_run_5_right, stick_run_3_right, stick_run_4_right]
        self.STICKMAN_RUN_LEFT = [stick_run_1_left, stick_run_2_left, stick_run_5_left, stick_run_3_left, stick_run_4_left]


        self.images = self.STICKMAN_IDLE
        self.image = self.images[0]

        self.rect = self.image.get_rect(center=(x, y))

        self.anim_index = 0
        self.anim_timer = 0
        self.ms = 0

    #Moves the player and begins the animation phase.
    def move_player(self, speed, dt):
        self.pressed = pg.key.get_pressed()

        if self.pressed[pg.K_a]:
            self.running = True
            if self.running:
                    self.pos_x -= 5  # Move left.
                    self.ms = 0.07
                    self.images = self.STICKMAN_RUN_LEFT  # Change the animation.
        if self.pressed[pg.K_d]:
            self.running = True
            if self.running:
                    self.pos_x += 5  # Move right.
                    self.ms = 0.07
                    self.images = self.STICKMAN_RUN_RIGHT  # Change the animation.
        if not self.pressed[pg.K_d] and not self.pressed[pg.K_a]:
            self.images = self.STICKMAN_IDLE  # Change the animation.
            self.ms = 0.07
        if self.pressed[pg.K_w]:
            self.is_jumping = True

        # Update the rect because it's used to blit the image.
        self.rect.center = self.pos_x, self.pos_y

    #Makes the player jump.
    def jumping(self, dt):
        if self.is_jumping:
            #Calculate force. 
            F = (0.5 * self.mass * (self.velocity))

            #Change position.
            self.pos_y = self.pos_y - F

            #Change velocity.
            self.velocity = self.velocity - 1

            if self.pos_y == GROUND_HEIGHT:
                self.pos_y = GROUND_HEIGHT
                self.is_jumping = False
                self.velocity = 15

    #Checks for collision.
    def is_collided_with(self, l):
        for wall in l:
            if self.rect.colliderect(wall.rect):
                if self.rect.right > wall.rect.left:
                    self.rect.right = wall.rect.left

                if self.rect.left < wall.rect.right:
                    self.rect.left = wall.rect.right

                if self.rect.bottom < wall.rect.top:
                    self.rect.bottom = wall.rect.top

                if self.rect.top > wall.rect.bottom:
                    self.rect.top = wall.rect.bottom

    #Animates the running movement of the player.
    def runAnim(self, dt):
        # Add the delta time to the anim_timer and increment the
        # index after 70 ms.
        self.anim_timer += dt

        if self.anim_timer > self.ms:
            self.anim_timer = 0  # Reset the timer.
            self.anim_index += 1  # Increment the index.
            self.anim_index %= len(self.images)  # Modulo to cycle the index.
            self.image = self.images[self.anim_index]  # And switch the image.

    #draws the player to the screen.
    def draw_entity(self):
        screen.blit(self.image, self.rect)

#Creates platforms that the user can jump onto.
class Platform(pg.sprite.Sprite):
    def __init__(self, x, y):
        pg.sprite.Sprite.__init__(self)
        self.pos_x = x
        self.pos_y = y

        self.moving = False

        self.image = None
        self.rect = pg.Rect(x, y, 150, 20)

    #draws the platform to the screen.
    def draw_plat(self):
        pg.draw.rect(screen, (0,0,0), self.rect)

3 个答案:

答案 0 :(得分:1)

您的问题在于您试图找出与您碰撞过的物体的哪一侧。问题是您当前的检查不是互斥的。如果您正在互相穿透某个对象(从任何一方),您通常会触发所有此函数中的检查:

def is_collided_with(self, l):
    for wall in l:
        if self.rect.colliderect(wall.rect):
            if self.rect.right > wall.rect.left:
                self.rect.right = wall.rect.left

            if self.rect.left < wall.rect.right:
                self.rect.left = wall.rect.right

            if self.rect.bottom < wall.rect.top:
                self.rect.bottom = wall.rect.top

            if self.rect.top > wall.rect.bottom:
                self.rect.top = wall.rect.bottom

最终结果是,您总是在您触摸的任何墙壁的下方和左侧。

您需要以其他方式检查碰撞的方向。一种常见的方法是跟踪您移动的方向,并使用它来确定与您碰撞的物体的哪一侧(因为如果您与非移动物体发生碰撞) ,你只能碰撞自己的动作)。不幸的是,你似乎没有为你的角色保持速度,所以这对你的游戏来说可能并不那么简单。但请注意,如果你想让你的角色有一些惯性&#34;当你开始和停止按键时需要一些时间来加速和减速,你可能需要添加一个力度属性,所以也许你现在可以添加它(不添加加速度的东西),只需用它来做碰撞响应逻辑。

无论如何,这里有一些代码,你可以适应你想要用来告诉你移动方向的任何方法(我只是放入函数调用来抽象测试,你可以写一个内联测试,例如self.velocity_x > 0或其他):

def is_collided_with(self, l):
    for wall in l:
        if self.rect.colliderect(wall.rect):
            if self.is_moving_right():      # replace this with a velocity check or something
                self.rect.right = wall.rect.left
            elif self.is_moving_left():     # and here
                self.rect.left = wall.rect.right

            if self.is_moving_down:         # and here
                self.rect.bottom = wall.rect.top
            elif self.is_moving_up():       # and here too
                self.rect.top = wall.rect.bottom

当您对角移动时,此代码可能仍然无法完成您想要的碰撞,但它应该适用于沿单个轴发生的碰撞。您可能希望将您的运动分成两个部分,一个是水平的,一个是垂直的,并且在它们之间进行了额外的碰撞测试,以避免需要解决对角线碰撞。

对角线碰撞有很多复杂性,因为你无法判断你是否只是在对角线移动时与另一个物体面对面碰撞,或者你是否真的碰到了角落。您可能想要进行一些涉及相对位置和速度的更复杂的计算,以查看碰撞的确切位置(这样您就可以决定如何解决碰撞)。

碰撞响应可能有很多细微之处,不同风格的游戏有不同的传统解决方案。如果您的游戏是平台游戏,您应该搜索该游戏风格的特定解决方案,因为针对其他游戏风格的更一般的解决方案可能不足以满足您的需求。例如,在平台游戏中,了解角色是否站在地面而不是摔倒或跳跃通常很重要,这需要在你处理垂直碰撞时采取一些额外的步骤。

答案 1 :(得分:0)

在我看来,进行碰撞的最佳方法是不使用坐标。 Pygame自动检查两个精灵是否发生碰撞。我不确定您要如何处理碰撞,但是此链接将向您精确地介绍如何进行操作,并提供有关如何解决确切问题的视频示例。 http://programarcadegames.com/index.php?chapter=introduction_to_sprites&lang=en#section_13

答案 2 :(得分:0)

请参阅https://pygame.org/docs/ref/sprite.html#pygame.sprite.collide_rect。它将帮助您更好地理解它。