我正在使用pygame/python、Tiled for.tmx文件和Richard Jones的tmx库为一款深受《口袋妖怪》影响的游戏创建一个基于平铺的2d overworld。我使用的代码主要基于python中托盘镇的这个精彩演示。
游戏运行得很好,然而,我在地图上的瓷砖(例如房屋、树木)与玩家精灵重叠时遇到了问题,而玩家精灵消失在瓷砖后面是有意义的。例如:在这里的图像中,深度感知原理会告诉我们,前景中的房子应该遮挡背景中的玩家,但因为地图是2D的,所以没有深度,因此没有遮挡。我很想增加深度,但由于我对pygame(以及一般的python)非常陌生,我不知道如何在精灵上绘制相关的前景对象。
幸运的是,我并不是唯一一个遇到这个问题的人,而且有很多关于可能解决方案的文档。例如:
-
这个StackExchange问题
-
这个LibGDX教程
-
这个Unity教程
然而,这段代码通常不是为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()
我再次使用这里的tmx库。也许那里需要改变什么?希望有人能帮我解决这个问题。这绝对是可能的,正如python中的这个pokemon克隆所示(遗憾的是,没有可用的源代码)。此外,第一次使用StackOverflow的用户,请告诉我是否有任何虚假通行证:)
想明白了!正如Kingsley在评论中所建议的那样,解决方案是更改图层的绘制顺序。在Layers类中,层是按照列表的顺序绘制的,玩家精灵具有最高的索引,因此被绘制在其他所有元素之上。在列表中的背景层和前景层之间移动播放器会使其显示在前景对象后面。为此,我在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)
今晚我会做更多的实验,但目前这个解决方案似乎有效。谢谢你的帮助!