简体   繁体   English

如何在我的 pygame 平台游戏中重写我的碰撞逻辑?

[英]How can I rewrite my collision logic in my pygame platformer?

I can't seem to figure out how to write the collision logic for my platformer.我似乎无法弄清楚如何为我的平台游戏编写碰撞逻辑。

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

Player Movement Code:玩家移动代码:

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

Tile Collision Test Code:瓷砖碰撞测试代码:

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

You don't describe how you want the collision to work.你不介绍你想怎么碰撞工作。 So I will make it up as I go along.所以我会随着我的进展而弥补。

One of the simplest ways to do collision is to test during attempted movement.进行碰撞的最简单方法之一是在尝试移动期间进行测试。 That is, decide if the proposed move is legal before changing the player's co-ordinates.也就是说,更改玩家的坐标之前确定提议的移动是否合法。 This works well because the code knows the original player location, and the direction of travel.这很有效,因为代码知道原始玩家位置和行进方向。 So an elegant solution partially moves the player in the desired direction up to the point of collision.因此,一个优雅的解决方案部分地将玩家朝所需方向移动到碰撞点。

So for starters you seem to be keeping a player POS and a player RECT .所以对于初学者来说,你似乎要保留一个玩家POS和一个玩家RECT Why keep two locations?为什么要保留两个位置? Let's just use the RECT .让我们只使用RECT But keeping Python Style Guide PEP8 in mind, we'll call it rect .但请记住 Python 风格指南PEP8 ,我们将其称为rect

Looking at your existing function, the move() moves the player left-right, adds gravity, and handles being on-screen.查看您现有的函数, move()将玩家左右移动,增加重力,并处理屏幕上的问题。 IMHO a Player movement function should not be knowing about gravity, so this should be handled somewhere else.恕我直言,玩家移动功能不应该知道重力,所以这应该在其他地方处理。 It can simply be passed as part of the y-change.它可以简单地作为 y 变化的一部分传递。 I'll leave the on-screen test as an exercise for the reader.我将把屏幕测试留给读者作为练习。

So about collisions - I don't know anything about your map, but it's possible that a move could collide with more than 1 object in a single move.所以关于碰撞 - 我对你的地图一无所知,但一次移动可能会与 1 个以上的物体发生碰撞。 Imagine this single-jump of dx pixels, use-case:想象一下这个dx像素的单跳,用例:

运动用例

We know this proposed single-jump right collides with 3 objects.我们知道这个提议的单跳右击与 3 个物体发生碰撞。 In this implementation of movement, we can only move so-far as to be touching the left-side of left-most terrain element "T2".在这个移动的实现中,我们只能移动到最左边的地形元素“T2”的左侧。

Can you see how knowing the proposed movement was "right" helps with this?你能看出知道提议的运动是“正确的”有什么帮助吗? It allows us to say: "Well moving dx pixels right, we would hit 3 things. So stop at the left-most one".它允许我们说:“向右移动dx像素,我们会击中 3 个东西。所以停在最左边的一个”。 If your player has already moved, and then your collision report says: "Uh-oh, 3 collisions Boss", how can you fix it?如果你的玩家已经移动了,然后你的碰撞报告说:“哦,哦,3 次碰撞 Boss”,你如何修复它? You can't.你不能。

So we take the theoretical move, if there's no collisions, well the player can move all of it.所以我们采用理论上的移动,如果没有碰撞,那么玩家可以移动所有的。 But if there is a collision, we look at the direction of travel, and find the closest thing we hit.但是如果发生碰撞,我们会查看行进方向,并找到距离我们最近的物体。 This becomes the limit of movement for that direction .这成为那个方向的移动极限。 But we can simply handle both dx and dy in the same manner as independent movements.但是我们可以像处理独立运动一样简单地处理dxdy

移动演示

Reference Code:参考代码:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM