【问题标题】:How can I rewrite my collision logic in my pygame platformer?如何在我的 pygame 平台游戏中重写我的碰撞逻辑?
【发布时间】:2020-08-29 04:40:52
【问题描述】:

我似乎不知道如何为我的平台游戏编写碰撞逻辑。

项目文件:https://github.com/1NilusNilus/Pygame-Platformer

玩家移动代码:

def move(self):
        print(self.POS)

        if self.POS[1] > SCREEN_SIZE[1]:
            self.POS[1] = SCREEN_SIZE[1] - self.SIZE[1]

        self.RECT.x = self.POS[0]
        self.RECT.y = self.POS[1]

        self.VEL[0] = 0
        if self.DIR["left"]:
            self.VEL[0] = -5

        if self.DIR["right"]:
            self.VEL[0] = 5

        self.POS[0] += self.VEL[0]

        

        self.VEL[1] += self.GRAVITY

瓷砖碰撞测试代码:

def testCollision(self, rect):

        self.RECT.x = self.POS[0]
        self.RECT.y = self.POS[1]

        for tile in self.TILES:
            if rect.colliderect(tile):
                self.hitlist.append(self.RECT)
        return self.hitlist

【问题讨论】:

  • 碰撞逻辑很粗糙!我以前肯定去过那里。这段代码有什么问题?
  • StackOverflow 通常不是人们为您编写代码的站点。 (如果您愿意,您需要付钱给别人!)我们可以帮助指出您可能遇到的问题并在途中帮助您,但要获得有用的答案,您需要尝试与我们会面。就您而言,有哪些无效的碰撞示例? self.tiles 和 self.RECT 定义为什么?
  • 我用我的碰撞逻辑重新发布这个问题
  • 你的标题说你想重写一些东西,而问题主体说你写东西有困难(大概是第一次)。是哪个?

标签: python pygame


【解决方案1】:

您没有描述您希望碰撞如何工作。所以我会在进行中弥补。

进行碰撞的最简单方法之一是在尝试移动期间进行测试。也就是说,在更改玩家坐标之前确定提议的移动是否合法。这很有效,因为代码知道原始玩家位置和行进方向。因此,一个优雅的解决方案会部分地将玩家沿所需方向移动到碰撞点。

所以对于初学者来说,你似乎保留了一个玩家POS 和一个玩家RECT。为什么要保留两个位置?让我们使用RECT。但请牢记 Python 样式指南 PEP8,我们将其称为 rect

查看您现有的功能,move() 左右移动播放器、增加重力并处理屏幕上的显示。恕我直言,玩家运动功能不应该知道重力,因此应该在其他地方处理。它可以简单地作为 y 变化的一部分传递。我将把屏幕测试留给读者作为练习。

所以关于碰撞 - 我对你的地图一无所知,但一个移动可能会在一次移动中与多个对象发生碰撞。想象一下这种 dx 像素的单跳,用例:

我们知道这个提议的单跳右会与 3 个对象发生碰撞。在这个移动实现中,我们只能移动到接触到最左边的地形元素“T2”的左侧。

你能看到知道提议的运动是“正确的”如何帮助解决这个问题吗?它允许我们说:“向右移动 dx 个像素,我们会碰到 3 个东西。所以停在最左边的一个”。如果你的玩家已经移动了,然后你的碰撞报告说:“呃,哦,3 次碰撞 Boss”,你如何解决它?你不能。

所以我们采取理论上的移动,如果没有碰撞,那么玩家可以移动所有的东西。但如果发生碰撞,我们会查看行进方向,并找到最接近我们撞到的东西。这成为该方向的移动限制。但是我们可以像处理独立运动一样简单地处理 dxdy

参考代码:

import pygame
import random

WINDOW_WIDTH  = 500
WINDOW_HEIGHT = 500

WHITE = ( 200, 200, 200 )
GREEN = (  30, 240,  80 )
BLUE  = (   3,   5,  54 )

class DummyMap:
    """ A random map of blockable terrain objects.
        Being random, it sometimes unhelpfully puts blocks over the 
        initial player position.  """

    def __init__( self, point_count, x_size=32, y_size=32 ):
        self.blockers = []
        for i in range( point_count ):
            random_x = random.randint( 0, WINDOW_WIDTH )
            random_y = random.randint( 0, WINDOW_HEIGHT )
            self.blockers.append( pygame.Rect( random_x, random_y, x_size, y_size ) )

    def draw( self, surface ):
        for tile in self.blockers:
            pygame.draw.rect( surface, GREEN, tile )
            
    def testCollision( self, rect ):
        """ This function is very much NOT efficeient for large lists.
            Consider using a quad-tree, etc. for faster collisions """
        colliders = []
        for tile in self.blockers:
            if ( tile.colliderect( rect ) ):
                colliders.append( tile )
        return colliders


class Player:
    """ Simple moveable player block, which collides with map elements """

    def __init__( self, x, y ):
        self.image  = pygame.Surface( ( 32, 32 ) )
        self.rect   = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.image.fill( WHITE )

    def draw( self, surface ):
        surface.blit( self.image, self.rect )

    def move( self, dx, dy, game_map ):
        """ Move the player, handling collisions """

        # calculate the target position of any x-move
        if ( dx != 0 ):
            move_rect = player.rect.copy()
            move_rect.move_ip( dx, 0 )

            print( "DEBUG: proposed x-move to (%d, %d)" % ( move_rect.x, move_rect.y ) )

            # Does this new position collide with the map elements?
            collide_rects = game_map.testCollision( move_rect )

            if ( len( collide_rects ) > 0 ):
                # yes collided, determine which object is the nearest
                if ( dx > 0 ):
                    # Going right, get the left-most x out of everything we hit
                    lowest_left_side = min( [ r.left for r in collide_rects ] )
                    # We can only move right as far as this lowest left-side, minus our width
                    final_dx = lowest_left_side - self.rect.right
                else:
                    # Going left, get the right-most x out of everything we hit
                    highest_right_side = max( [ r.right for r in collide_rects ] )
                    # We can only move left as far as the highest right-side
                    final_dx = highest_right_side - self.rect.left # (this is a negative value)
            else:
                final_dx = dx  # no collsiions, no worries

            # Do the x-movement
            self.rect.x += final_dx
            print( "DEBUG: final x-move to (%d, %d)" % ( self.rect.x, self.rect.y ) )

        if ( dy != 0 ):
            move_rect = player.rect.copy()
            move_rect.move_ip( 0, dy )

            print( "DEBUG: proposed y-move to (%d, %d)" % ( move_rect.x, move_rect.y ) )

            # Does this new position collide with the map elements?
            collide_rects = game_map.testCollision( move_rect )

            if ( len( collide_rects ) > 0 ):
                # yes collided, determine which object is the nearest
                if ( dy < 0 ):
                    # Going up, get the bottom-most y out of everything we hit
                    lowest_bottom_side = min( [ r.bottom for r in collide_rects ] )
                    # We can only move up as far as this lowest bottom
                    final_dy = lowest_bottom_side - self.rect.top
                else:
                    # Going down, get the top-most y out of everything we hit
                    highest_top_side = max( [ r.top for r in collide_rects ] )
                    # We can only move down as far as the highest top-side, minus our height
                    final_dy = highest_top_side - self.rect.bottom # (this is a negative value)
            else:
                final_dy = dy  # no collsiions, no worries

            # Do the y-movement
            self.rect.y += final_dy
            print( "DEBUG: final x-move to (%d, %d)" % ( self.rect.x, self.rect.y ) )


                


### initialisation
pygame.init()
window = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ) )
pygame.display.set_caption("Collision Demo")

# Game elements
player   = Player( WINDOW_WIDTH//2, WINDOW_HEIGHT//2 )
game_map = DummyMap( 37 )

### Main Loop
clock = pygame.time.Clock()
done = False
while not done:

    # Handle user-input
    for event in pygame.event.get():
        if ( event.type == pygame.QUIT ):
            done = True

    # Movement keys
    keys = pygame.key.get_pressed()
    dx = 0
    dy = 0 # 2 # gravity sucks
    if ( keys[pygame.K_UP] ):
        dy -= 5
    if ( keys[pygame.K_DOWN] ):
        dy += 5
    if ( keys[pygame.K_LEFT] ):
        dx -= 5
    if ( keys[pygame.K_RIGHT] ):
        dx += 5
    # Try to move the player according to the human's wishes
    player.move( dx, dy, game_map )


    # Update the window, but not more than 60fps
    window.fill( BLUE )
    game_map.draw( window )
    player.draw( window )
    pygame.display.flip()

    # Clamp FPS
    clock.tick_busy_loop(60)

pygame.quit()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多