为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

侃侃无极 2023-06-27 16:36:29
我似乎不知道如何为我的平台游戏编写碰撞逻辑。项目文件: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
查看完整描述

1 回答

?
狐的传说

TA贡献1804条经验 获得超3个赞

您没有描述您希望碰撞如何进行。所以我会一边做一边弥补。

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

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

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

关于碰撞 - 我对你的地图一无所知,但一次移动可能会与超过 1 个物体发生碰撞。想象一下dx像素的单跳,用例:

//img2.sycdn.imooc.com/649a9fab0001766605640216.jpg

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

你能看出知道拟议的运动是“正确的”对这有什么帮助吗?它允许我们说:“向右移动dx像素,我们会碰到 3 个东西。所以停在最左边的那个”。如果你的玩家已经移动,然后你的碰撞报告显示:“呃-哦,3 次碰撞,老大”,你该如何修复它?你不能。

所以我们采取理论上的移动方式,如果没有碰撞,那么玩家就可以移动所有的物体。但如果发生碰撞,我们会查看行进方向,并找到距离我们最近的碰撞点。这成为该方向的移动限制。但我们可以像独立运动一样简单地处理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()




查看完整回答
反对 回复 2023-06-27
  • 1 回答
  • 0 关注
  • 143 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信