【问题标题】:Pygame sprites - get the rect collision sidePygame sprites - 获取矩形碰撞侧
【发布时间】:2017-08-29 22:06:00
【问题描述】:

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

玩家在碰到方块时停止下落,但如果我向左或向右然后进入方块,玩家会直接进入方块并停止从那里下落。我希望玩家无法穿过街区。该怎么做?

【问题讨论】:

    标签: python python-3.x pygame sprite


    【解决方案1】:

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

    首先要做的事情是:错误的是您从未检查过水平移动是否会使块发生碰撞。您只有一个检查到位,它实际上只在发生任何碰撞之后执行任何操作。在这种情况下,它会阻止“重力”做任何事情。由于您逐个像素地更新您的玩家,碰撞已经发生的事实几乎没有被注意到。解决方法是创建一个检查来验证移动块是否可以移动到某个位置,然后才允许它。并为所有运动做这件事,而不仅仅是重力运动。

    所以,在这里我将解释可以让这个示例代码演变成完整游戏的改进。

    1. 在模块级别消除“浮动代码”并将所有内容放入函数中。这对于任何未来将具有像开始屏幕(并且可能是“重玩”屏幕)这样简单的东西的游戏都是必不可少的。我做了两个函数,一个创建设置 您的游戏环境并创建您的对象,以及主要的游戏功能。您应该在将来进一步分离“设置”阶段, 因为能够创建更多的游戏对象或不同的游戏关卡。

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

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

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

    代码:

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

    【讨论】:

      【解决方案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()
      

      它运行良好。

      【讨论】:

      • 但在此代码中,您将 xCh 更改为 0,然后再通过按键更改其值。它可能会“让事情变得更好”——但它实际上并不能阻止重叠的发生。这种方法本质上是不正确的。
      猜你喜欢
      • 1970-01-01
      • 2023-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多