【问题标题】:Overlay tiles onto sprite in pygame在pygame中将瓷砖覆盖到精灵上
【发布时间】:2019-06-21 23:37:12
【问题描述】:

我正在使用 pygame/python、用于 .tmx 文件的 Tiled 和 Richard Jones 的 tmx 库为游戏创建一个基于图块的 2d 世界 - 深受 Pokemon 的影响。我使用的代码主要基于this Python 中 Pallet Town 的精彩演示。

游戏运行得很好,但是,当玩家精灵消失在他们身后有意义时,我在地图上制作瓷砖(例如房屋、树木)与玩家精灵重叠时遇到了问题。例如:在图像here 中,深度感知原理告诉我们,前景中的房子应该遮挡背景中的玩家,但由于地图是二维的,因此没有深度,因此没有遮挡。我很想增加深度,但鉴于我对 pygame(和一般的 python)非常陌生,我不知道如何在精灵上绘制相关的前景对象。

幸运的是,我并不孤单,并且存在大量关于可能解决方案的文档。例如:

但是,此代码通常不是为 python 编写的,我不确定如何在我的情况下实现它。按 z 位置(或“深度”属性)排序/绘图似乎是最明智的做法,但查看 tmx 库我只能找到提到的 x 和 y 值。将播放器精灵添加到 Tiled 中的空对象层也是一种解决方案,但我再次不确定如何执行此操作,并且我的所有尝试都导致了错误消息。 (这里没有详细说明尝试,因为老实说我不知道​​我做了什么,而且它无论如何都没有用。)

我目前的代码如下:

class Player(pygame.sprite.Sprite):
    def __init__(self, location, collStart, orientation, *groups):
        super(Player, self).__init__(*groups)
        self.image = pygame.image.load('sprites/player.png')
        self.imageDefault = self.image.copy()
        self.rect = pygame.Rect(location, (26,26))
        self.collider = pygame.Rect(collStart, (13,13))
        self.orient = orientation 
        self.holdTime = 0
        self.walking = False
        self.dx = 0
        self.step = 'rightFoot'
        # Set default orientation
        self.setSprite()
        self.speed = pygame.time.get_ticks() + 50  # slows down walking speed 
        by .5 sec (current time + 50 ms)


    def setSprite(self):
        # this function contains information about where to find which sprite 
        in the sprite sheet, probably not relevant here.

    def update(self, dt, game):
        key = pygame.key.get_pressed()
        if pygame.time.get_ticks() >= self.speed:
            self.speed = pygame.time.get_ticks() + 50
            # Setting orientation and sprite based on key input, removed the 
            #code here because it wasn't relevant
            #[....]   
            # Walking mode enabled if a button is held for 0.1 seconds
            if self.holdTime >= 100:
                self.walking = True
            lastRect = self.rect.copy()
            lastColl = self.collider.copy() # collider covers the bottom section of the sprite
            # Code for walking in the direction the player is facing, not relevant here
            #[....]      
            # Collision detection:
            # Reset to the previous rectangle if player collides
            # with anything in the foreground layer
            if len(game.tilemap.layers['triggers'].collide(self.collider,
                                                            'solid')) > 0:
                self.rect = lastRect
                self.collider = lastColl
            # Area entry detection, loads dialog screen from the dialog file:
            elif len(game.tilemap.layers['triggers'].collide(self.collider,
                                                            'entry')) > 0:
                entryCell = game.tilemap.layers['triggers'].find('entry')[0]
                game.fadeOut()
                run()
                pygame.quit()
                quit()

                return
            if self.dx == 16:
                # Makes the player appear to take steps w/ different feet, not relevant here
            #[....]
            # After traversing 32 pixels, the walking animation is done
            if self.dx == 32:
                self.walking = False
                self.setSprite()
                self.dx = 0

            game.tilemap.set_focus(self.rect.x, self.rect.y)

class Game(object):
    def __init__(self, screen):
        self.screen = screen

    def initArea(self, mapFile):
        """Load maps and initialize sprite layers for each new area"""
        self.tilemap = tmx.load(mapFile, screen.get_size())
        self.players = tmx.SpriteLayer()
        self.objects = tmx.SpriteLayer()
        # In case there is no sprite layer for the current map
        except KeyError:
            pass
        else:
            self.tilemap.layers.append(self.objects)
        # Initializing player sprite
        startCell = self.tilemap.layers['triggers'].find('playerStart')[0]
        self.player = Player((startCell.px, startCell.py), (startCell.px, 
        startCell.bottom-4),
                             startCell['playerStart'], self.players)
        self.tilemap.layers.append(self.players)
        self.tilemap.set_focus(self.player.rect.x, self.player.rect.y)  

    def main(self):
        clock = pygame.time.Clock()
        self.initArea('test tilemap.tmx')

        while 1:
            dt = clock.tick(30)

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    return
                if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
                    return

            self.tilemap.update(dt, self)
            screen.fill((0,0,0))
            self.tilemap.draw(self.screen)
            pygame.display.flip()

我再次使用here 找到的 tmx 库。也许那里需要改变一些东西?希望有人可以帮助我解决这个问题。这绝对是可能的,如this pokemon clone in python 所示(遗憾的是,没有可用的源代码)。 另外,第一次使用 StackOverflow 的用户,如果我犯了任何错误,请告诉我 :)

【问题讨论】:

  • 是否可以叠加两个平铺地图?一张为背景,一张为前景。绘制顺序为:背景→播放器→前景。这样草(或其他)就会被绘制在玩家身上,给人一种 z 顺序的感觉。任何空的前景图块都只是“清除”。
  • @Kingsley 这听起来是个不错的解决方案!你能帮我弄清楚如何实现它吗?目前,玩家精灵被绘制在其他一切之上,即使 Tiled 中有背景和前景图层。我认为这是因为玩家不是 Tiled 图层的一部分,而是稍后使用 Player 类添加的。我将如何更改绘制顺序以使玩家出现在前景层后面?

标签: python pygame tile tmx


【解决方案1】:

想通了!正如 Kingsley 在 cmets 中所建议的那样,解决方案是更改图层的绘制顺序。图层是按照图层类中的列表顺序绘制的,玩家精灵具有最高的索引,因此被绘制在其他所有内容之上。在列表中的背景层和前景层之间移动播放器使其出现在前景对象的后面。 为此,我在 Game 类的 initArea 函数中添加了以下代码:

  def initArea(self, mapFile):
    """Load maps and initialize sprite layers for each new area"""
    self.tilemap = tmx.load(mapFile, screen.get_size())
    self.players = tmx.SpriteLayer()
    self.objects = tmx.SpriteLayer()
    # Initializing player sprite
    startCell = self.tilemap.layers['triggers'].find('playerStart')[0]
    self.player = Player((startCell.px, startCell.py), (startCell.px, startCell.bottom-4),
                         startCell['playerStart'], self.players)
    foregroundItem = self.tilemap.layers.__getitem__("foreground") # finds the layer called foreground
    foregroundIndex = self.tilemap.layers.index(foregroundItem)  # finds the position of the foreground layer in the Layers list (Layers class specified in .tmx file)
    self.tilemap.layers.insert(foregroundIndex-1, self.players)  # move the Player layer one layer below the foreground layer
    self.tilemap.set_focus(self.player.rect.x, self.player.rect.y)

今晚我会多做一些实验,但目前这个解决方案似乎可行。感谢您的帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-05-20
    • 1970-01-01
    • 1970-01-01
    • 2023-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多