Pygame精灵 - 获取矩形碰撞侧面

2024-03-28 10:44:25 发布

您现在位置:Python中文网/ 问答频道 /正文

我是pygame的新手,我正在努力学习基础知识。我想创建障碍物并检测玩家矩形(代表精灵的碰撞盒)的哪一边与障碍物矩形相撞。有了这个,我可以在游戏中创建基础物理。在

这就是我所做的:

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

当它碰到积木的时候,它就停止掉下去,但是如果我向左或向右,然后进入积木,它就会直接进入积木并停止从那里坠落。我希望球员不能通过障碍。怎么做?在


Tags: keyrectimageselfeventaddinitdisplay
2条回答

最新版本是:

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

它工作得很好。在

下面是您的代码和建议的修复程序,以及其他一些改进建议。在

第一件事:错的是你从来没有检查过水平移动是否会使块碰撞。你只做了一次检查,实际上它只在碰撞发生后才做任何事情。在这种情况下,它会阻止“重力”做任何事情。因为你正在逐像素更新你的播放器,碰撞已经发生的事实基本上没有被注意到。解决方法是创建一个检查来验证移动块是否可以移动到某个位置,然后才允许它移动。做所有的运动,而不仅仅是重力运动。在

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

  1. 消除模块级的“浮动代码”,将所有内容都放在函数中。这对于未来任何一款游戏来说都是必不可少的,它甚至会有一个简单的开始屏幕(可能还有一个“再次播放”屏幕)。我建立了两个函数 你的游戏环境和创建你的对象,以及游戏的主要功能。你以后应该把“设置”阶段进一步分开, 为了能够创建更多的游戏对象,或者不同的游戏级别。

  2. 重用代码。您的__init__方法中的行大部分在两个类之间重复,但是对于初始位置和图像名常量。所以实际上只需要一个类,并为这些属性传递不同的值。因为你暗示你将有两种不同的游戏对象类型,通过使用群组等,我保留了这两个类,但考虑了共同的代码。

  3. 帧延迟:你的代码只会“尽可能快地”移动——这将在不同的计算机上创建不同的游戏速度,并使用100%的CPU——导致可能的机器减速和过度的功耗。我加入了一个30毫秒的硬编码暂停-这将允许你的游戏以接近30帧/秒的速度移动,在不同的设备之间保持稳定的速度。另一个需要改变的是你硬编码的重力和速度的“1”值,这将使玩家每次移动1px,必须更改为更大的值。

  4. 最后,但并非最不重要的是,“我能移动到那里吗”逻辑:它实际上看起来很广泛,但是可以感觉到,对“这个物体能移到那个位置吗”的简单的英语描述是冗长的。该方法的实质是:“存储当前位置”在给定的x和y位移上伪造一个位置检查我是否与什么东西相撞如果没有,恢复先前的位置,并说允许移动否则,恢复y位置,检查X方向是否有碰撞,如有,限制水平移动”恢复x位置,将y位置移动所需量,检查是否发生碰撞。如果是这样,请限制y方向的移动如果移动不受x或y方向的限制,则只是组合移动导致碰撞:限制两个方向的移动是公平的”恢复原始位置,返回x和y位置的允许更改,以及我们将要碰撞的对象”。所有这些逻辑都放在“living”objects类本身中,因此它可以在任何时候使用,在主游戏逻辑中只有一行代码。

代码:

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

相关问题 更多 >