关于python:Pygame精灵-获取rect碰撞面

Pygame sprites - get the rect collision side

我是pygame的新手,我正在尝试获取基本知识。 我想创建障碍物并检测玩家矩形(代表精灵的碰撞盒)的哪一侧与障碍物矩形碰撞。 这样,我就可以在游戏中创建基础物理。

这是我所做的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import pygame

class Player (pygame.sprite.Sprite):
    def __init__(self, x=0, y=0, s=100):
        super(Player,self).__init__()
        self.image = pygame.transform.scale(pygame.image.load("player.png"), (s, s))
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y

class Block (pygame.sprite.Sprite):
    def __init__(self, x=0, y=500, s=100):
        super(Block, self).__init__()
        self.image = pygame.transform.scale(pygame.image.load("wall.png"),(s, s))
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y

pygame.init()

display = pygame.display.set_mode((800,600))

player = Player()
block = Block()

sprites = pygame.sprite.Group()
living = pygame.sprite.Group()
noliving = pygame.sprite.Group()

sprites.add(player)
sprites.add(block)
living.add(player)
noliving.add(block)

gravity = 1

xCh = 0
yCh = 0

speed = 1

while True:

    display.fill((159, 159, 159))

    for liv in living:
        for noliv in noliving:
            if(not pygame.sprite.collide_rect(liv, noliv)):
                player.rect.y += gravity

    for event in pygame.event.get():
        if(event.type == pygame.KEYDOWN):
            if(event.key == pygame.K_ESCAPE):
                quit()
            elif(event.key == pygame.K_a or event.key == pygame.K_LEFT):
                xCh = -speed
            elif(event.key == pygame.K_d or event.key == pygame.K_RIGHT):
                xCh = speed
        elif(event.type == pygame.KEYUP):
            xCh = 0
            yCh = 0
        elif(event.type == pygame.QUIT):
            quit()

    player.rect.x += xCh

    sprites.draw(display)

    pygame.display.update()

玩家触摸该块时会停止掉落,但是如果我向左或向右走然后进入该块,则玩家会直接进入该块并停止从此处掉落。 我希望玩家无法通过障碍。 怎么做?


这是您的代码,其中包含建议的修复程序以及其他一些改进建议。

首先,第一件事:问题是您从未检查过水平移动是否会使块碰撞。您只进行了一次检查,实际上它仅在发生任何碰撞后才执行任何操作。并且在这种情况下,它将防止"重力"做任何事情。由于您要逐像素更新播放器,因此实际上几乎没有注意到碰撞的发生。解决方法是创建一个检查,以验证移动的块是否可以移动到某个位置,然后才允许移动。并为所有运动做这些,而不仅仅是重力运动。

因此,在这里,我将解释可以使此示例代码演变为完整游戏的改进。

  • 在模块级别消除"浮动代码",并将所有内容放入函数中。这对于将来拥有甚至与起始屏幕(可能还有"再次播放"屏幕)一样简单的任何游戏来说都是至关重要的。我做了两个功能,一个创建设置
    您的游戏环境并创建您的对象,以及主要的游戏功能。您应该在将来进一步分开"设置"阶段,
    能够创建更多游戏对象或不同的游戏级别。

  • 重用您的代码。您的__init__方法中的行在两个类之间大多重复,但是包含初始位置和图像名称常量。因此,您实际上只需要一个类,并为这些属性传递不同的值。既然您暗示通过使用组等,您将拥有两种不同的游戏对象类型,因此我保留了这两个类,但排除了通用代码。

  • 帧延迟:您的代码将简单地"尽可能快地"移动-这将在不同的计算机上创建不同的游戏速度,并使用100%的CPU-导致可能的计算机速度降低和功耗过多。我提供了30ms的硬编码暂停-这将使您的游戏以接近30FPS的速度移动,并在不同设备上保持稳定的速度。另一个需要进行的更改是您的重力和速度的硬编码" 1"值,这会使玩家一次移动1px,必须更改为更大的值。

  • 最后但并非最不重要的一点是"我可以移动到那里"的逻辑:它实际上似乎很广泛-但可以理解,"可以使该对象移动到该位置"检查的纯英文描述很冗长。该方法实质上是:"存储当前位置"。"用给定的x和y位移在位置上假"。"检查我是否与某物发生碰撞"。"如果没有,请恢复先前的位置,并说允许移动"。"否则,请恢复y位置,检查是否在X方向上发生碰撞,如果有,则限制水平移动"。"恢复x位置,将y位置移动所需的量,检查是否发生碰撞。如果发生,则限制在y方向上的移动"。"如果运动不受x或y的限制,则只是组合运动会导致碰撞:限制两个方向的运动都是公平的"。"还原原始位置,返回允许的x和y位置更改,以及我们要与之碰撞的对象"。所有这些逻辑都放在"活动"对象类内部,因此可以随时使用,只要在主游戏逻辑中使用一行代码即可。

  • 码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    import pygame


    class GameObject(pygame.sprite.Sprite):
        def __init__(self, x=0, y=0, s=100, image=""):
            super(GameObject, self).__init__()
            if not image:
                image = self.image
            self.image = pygame.transform.scale(pygame.image.load(image), (s, s))
            self.rect = self.image.get_rect()
            self.rect.x = x
            self.rect.y = y


    class Player(GameObject):
        image ="player.png"

        def can_move(self, xCh, yCh, group):
            old_pos = self.rect.x, self.rect.y
            self.rect.x += xCh
            self.rect.y += yCh
            collided_with  = pygame.sprite.spritecollideany(self, group)
            if not collided_with:
                # No Collisions at all - allow movement
                self.rect.x = old_pos[0]
                self.rect.y = old_pos[1]
                return True, xCh, yCh, None
            # Check if the colision was due to horizontal movement:
            self.rect.y = old_pos[1]
            if pygame.sprite.spritecollideany(self, group):
                # Yes, then indicate horizontal movement should be 0
                xCh = 0

            # check if collision was due to vertical movement:
            self.rect.y += yCh
            self.rect.x = old_pos[0]

            if pygame.sprite.spritecollideany(self, group):
                # Yes - indicate vertical movemnt should be 0
                yCh = 0

            # if only diagonal movement causes collision, then
            # cancel movrment in both axes:
            # (i.e. just the diagonal movement would hit the
            #  obstacle )
            if not xCh == 0 and not yCh == 0:
                xCh = 0
                yCh = 0

            self.rect.x = old_pos[0]
            self.rect.y = old_pos[1]
            return False, xCh, yCh, collided_with



    class Block(GameObject):
        image ="wall.png"


    def setup():
        global display, player, living, noliving, gravity, speed, sprites
        pygame.init()

        display = pygame.display.set_mode((800,600))

        player = Player()
        block = Block(y=500)

        sprites = pygame.sprite.Group()
        living = pygame.sprite.Group()
        noliving = pygame.sprite.Group()

        sprites.add(player)
        sprites.add(block)
        living.add(player)
        noliving.add(block)

        gravity = 10
        speed = 10


    def main():
        xCh = 0
        yCh = 0
        while True:

            display.fill((159, 159, 159))

            for event in pygame.event.get():
                if(event.type == pygame.KEYDOWN):
                    if(event.key == pygame.K_ESCAPE):
                        quit()
                    elif(event.key == pygame.K_a or event.key == pygame.K_LEFT):
                        xCh = -speed
                    elif(event.key == pygame.K_d or event.key == pygame.K_RIGHT):
                        xCh = speed
                elif(event.type == pygame.KEYUP):
                    xCh = 0
                    yCh = 0
                elif(event.type == pygame.QUIT):
                    quit()

            yCh = gravity

            for liv in living:
                if liv == player:
                    check_x_ch = xCh
                else:
                    check_x_ch = 0
                can_move, xCh, yCh, obstacle = liv.can_move(xCh, yCh, noliving)

                liv.rect.x += xCh
                liv.rect.y += yCh

                # Do other actions if"can_move" indicates a block was hit...


            sprites.draw(display)

            pygame.display.update()
            pygame.time.delay(30)


    setup()
    main()


    它的最新版本是这样的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    import pygame

    class GameObj(pygame.sprite.Sprite):
        def __init__(self, image, x, y, s):
            super(GameObj, self).__init__()
            self.image = pygame.transform.scale(pygame.image.load("img/"+ image), (s, s))
            self.rect = self.image.get_rect()
            self.rect.x = x
            self.rect.y = y

    class Player(GameObj):

        leftUnable = False
        rightUnable = False

        jumpHeight = 200
        jumpSpeed = 5

        image ="player.png"
        inAir = True
        def __init__(self, x, y, s=100):
            super(Player,self).__init__(self.image, x, y, s)

    class Block(GameObj):
        image ="wall.png"
        def __init__(self, x, y, s=100):
            super(Block, self).__init__(self.image, x, y, s)

    def collideNum(sprite, group):
        total = 0
        for member in group:
            if(pygame.sprite.collide_rect(sprite, member)):
                total += 1

        return total

    def setup():
        pygame.init()

        global display, player, block, sprites, living, noliving, clock

        display = pygame.display.set_mode((800, 600))
        pygame.display.set_caption("Test")

        clock = pygame.time.Clock()

        player = Player(100, 0)
        block = Block(100, 300)
        block1 = Block(200, 400)
        block2 = Block(400, 400)

        sprites = pygame.sprite.Group()
        living = pygame.sprite.Group()
        noliving = pygame.sprite.Group()

        noliving.add(block)
        noliving.add(block1)
        noliving.add(block2)

        living.add(player)

        for liv in living:
            sprites.add(liv)

        for noliv in noliving:
            sprites.add(noliv)

        main()

    def main():
        speed = 5
        gravity = 5

        xCh, yCh = 0, 0

        player.leftUnable = False
        player.rightUnable = False

        while True:
            clock.tick(60)

            display.fill((184, 184, 184))

            yCh = gravity

            for event in pygame.event.get():
                if(event.type == pygame.QUIT):
                    quit()
                elif(event.type == pygame.KEYDOWN):
                    if(event.key == pygame.K_ESCAPE):
                        quit()
                    elif((event.key == pygame.K_a or event.key == pygame.K_LEFT) and not player.leftUnable):
                        for noliv in noliving:
                            if(pygame.sprite.collide_rect(player, noliv)):
                                if(noliv.rect.left < player.rect.left < noliv.rect.right and player.rect.bottom > noliv.rect.top + 5):
                                    player.leftUnable = True
                                    break
                                else:
                                    xCh = -speed
                                    player.leftUnable = False

                            else:
                                xCh = -speed
                                player.leftUnable = False

                    elif((event.key == pygame.K_d or event.key == pygame.K_RIGHT) and not player.rightUnable):
                        for noliv in noliving:
                            if(pygame.sprite.collide_rect(player, noliv)):
                                if(noliv.rect.left < player.rect.right < noliv.rect.right and player.rect.bottom > noliv.rect.top + 5):
                                    player.rightUnable = True
                                    break
                                else:
                                    xCh = speed
                                    player.rightUnable = False

                            else:
                                xCh = speed
                                player.rightUnable = False
                    elif(event.key == pygame.K_SPACE or event.key == pygame.K_w or event.key == pygame.K_UP):
                        oldPos = player.rect.bottom
                        xCh = 0
                        if(not player.inAir):
                            while player.rect.bottom > oldPos - player.jumpHeight:
                                clock.tick(60)
                                display.fill((184, 184, 184))

                                for ev in pygame.event.get():
                                    if(ev.type == pygame.KEYDOWN):
                                        if(ev.key == pygame.K_d or ev.key == pygame.K_RIGHT):
                                            xCh = speed
                                        elif(ev.key == pygame.K_a or ev.key == pygame.K_LEFT):
                                            xCh = -speed
                                    elif(ev.type == pygame.KEYUP):
                                        xCh = 0

                                player.rect.x += xCh

                                player.rect.y -= player.jumpSpeed
                                player.inAir = True

                                sprites.draw(display)
                                pygame.display.update()
                elif(event.type == pygame.KEYUP):
                    xCh = 0

            for liv in living:
                for noliv in noliving:
                    if(pygame.sprite.collide_rect(liv, noliv)):
                        liv.inAir = False
                        break
                    else:
                        liv.inAir = True



            for noliv in noliving:
                if(pygame.sprite.collide_rect(player, noliv)):
                    if(noliv.rect.left < player.rect.left < noliv.rect.right and player.rect.bottom > noliv.rect.top + 5):
                        player.leftUnable = True
                        if(collideNum(player, noliving) == 1):
                            player.inAir = True
                        if(xCh < 0):
                            xCh = 0
                    elif(noliv.rect.left < player.rect.right < noliv.rect.right and player.rect.bottom > noliv.rect.top + 5):
                        player.rightUnable = True
                        if(collideNum(player, noliving) == 1):
                            player.inAir = True
                        if(xCh > 0):
                            xCh = 0
                    else:
                        player.leftUnable = False
                        player.rightUnable = False
                else:
                    player.leftUnable = False
                    player.rightUnable = False

            if(not player.inAir):
                yCh = 0

            if(player.rect.top > display.get_size()[1]):
                setup()

            player.rect.x += xCh
            player.rect.y += yCh

            sprites.draw(display)
            pygame.display.update()

    setup()

    运行良好。