使用 pytmx 在精确时间启动动画对象,pygame
Animated objects launch at a precise time with pytmx, pygame
我有麻烦了,我正试图在我的游戏中打开大门。
我正在使用 pygame 和 pytmx,我已经构建了一个用房间制作的关卡,并且在每个房间中我都有一个使用 pytmx 的渲染器,我想要实现的是例如在 0 级玩家必须移动到门打开它并进入 1 级,我的目标是在玩家撞门时启动并绘制动画。
我尝试更新我的 Door 对象(他的父对象是 pygame Sprite class),它有点工作但当然对象被修改和显示,但是平铺地图对象也一直呆在地图的后面,所以我试着不在瓦片地图中初始化我的对象,但如果是这样,门根本不会咬人。然后我尝试修改瓦片对象图像并重新加载我的渲染但仍然不起作用,你们中的任何人有任何想法吗?
下面是部分代码,让大家更好的理解
"""
This is a test of using the pytmx library with Tiled.
"""
import pygame
import pytmx
class Renderer(object):
"""
This object renders tile maps from Tiled
"""
def __init__(self, filename):
tm = pytmx.load_pygame(filename, pixelalpha=True)
self.object_images = []
self.size = tm.width * tm.tilewidth, tm.height * tm.tileheight
self.tmx_data = tm
self.map_surface = self.make_map()
self.current_frame = 0
def render(self, surface):
tw = self.tmx_data.tilewidth
th = self.tmx_data.tileheight
print(self.tmx_data.tile_properties.items())
if self.tmx_data.background_color:
surface.fill(self.tmx_data.background_color)
for layer in self.tmx_data.layers:
if isinstance(layer, pytmx.TiledTileLayer):
for x, y, image in layer.tiles():
if image:
surface.blit(image.convert_alpha() , (x * tw, y * th))
elif isinstance(layer, pytmx.TiledObjectGroup):
for object in layer:
if object.image:
surface.blit(object.image.convert_alpha() , (object.x, object.y))
elif isinstance(layer, pytmx.TiledImageLayer):
if image:
surface.blit(image , (0, 0))
def make_map(self):
temp_surface = pygame.Surface(self.size)
self.render(temp_surface)
return temp_surface
def reload(self):
self.map_surface = self.make_map()
def update_object_image(self, object_id, surface):
pass
def get_layer(self, layer_name):
return self.tmx_data.get_layer_by_name(layer_name)
class Room:
def __init__(self, room_image, level_options):
self.renderer = Renderer(room_image)
self.surface = self.renderer.map_surface
self.rect = self.surface.get_rect()
self.level_options = level_options
self.obstacles = pygame.sprite.Group()
self.doors = pygame.sprite.Group()
self.load_obstacles()
self.load_doors()
def load_obstacles(self):
obstacles = self.level_options['obstacle_layers']
for obstacle in obstacles:
try:
items = self.renderer.get_layer(obstacle)
for x, y, image in items.tiles():
self.obstacles.add(Obstacle(x, y, image, SPRITE_SIZE))
except ValueError:
return
def load_doors(self):
doors = self.renderer.get_layer(self.level_options['door_layer'])
if isinstance(doors, pytmx.TiledTileLayer):
for x, y, image in doors.tiles():
if image:
self.doors.add(Door(self, x, y, image, SPRITE_SIZE, self.level_options['open_door_actions']))
elif isinstance(doors, pytmx.TiledObjectGroup):
for object in doors:
self.doors.add(Door(self, object.x, object.y, object.image, SPRITE_SIZE, self.level_options['open_door_actions'], object))
def update_doors(self):
for door in self.doors:
if door.object:
door.update()
#self.renderer.update_object_image(door.object.id, door.image)
class Wall(pygame.sprite.Sprite):
def __init__(self, x, y, image, sprite_size):
pygame.sprite.Sprite.__init__(self)
self.rect = pygame.Rect(x * sprite_size,y * sprite_size, sprite_size, sprite_size)
self.image = image
class Obstacle(pygame.sprite.Sprite):
def __init__(self, x, y, image, sprite_size):
pygame.sprite.Sprite.__init__(self)
self.rect = pygame.Rect(x * sprite_size,y * sprite_size, sprite_size, sprite_size)
self.image = image
class Door(pygame.sprite.Sprite):
def __init__(self, parent, x, y, image, sprite_size, open_actions, object = None):
self.parent = parent
pygame.sprite.Sprite.__init__(self)
self.object = object
if self.object:
self.rect = pygame.Rect(x ,y, self.object.width, self.object.height)
self.current_index = 0
self.load_animations()
else:
self.rect = pygame.Rect(x * sprite_size,y * sprite_size, sprite_size, sprite_size)
self.image = image
self.open_actions = open_actions
self.last_updated = 0
self.is_open = False
self.is_activated = False
def load_animations(self):
self.animations = []
filename = os.path.join(os.getcwd(), IMAGES_FOLDER, 'maps', 'animations', self.object.animated_image)
spritesheet = Spritesheet(filename, self.object.width, self.object.height)
for i in range(spritesheet.data['columns']):
self.animations.append(spritesheet.parse_sprite(i))
self.image = self.animations[self.current_index]
def open_animation(self):
if self.is_open:
return
if not self.object or not self.is_activated or not self.animations:
return
self.image = self.animations[self.current_index]
if self.current_index == len(self.animations) - 1:
self.is_open = True
def update(self):
if self.is_open:
if self.object:
self.image = self.animations[-1]
if self.is_activated:
if self.object:
now = pygame.time.get_ticks()
if now - self.last_updated > 200:
self.last_updated = now
self.current_index = (self.current_index + 1) % len(self.animations)
self.image = self.animations[self.current_index]
if not self.is_open:
self.image = self.animations[0]
def check_doors_state(self, player):
if 'any' in self.open_actions:
self.is_activated = True
for action in player.actions:
if action in self.open_actions:
self.is_activated = True
所以现在我想做的是使用动画图块对象并从 tmx 数据启动动画,但我什至无法理解如何在第一次渲染时启动动画。
提前感谢您的回答。
所以我找到了一个解决方案,我不是要更改和重新加载对象图块,我只是将我的游戏精灵分组,修改我的图块生成和渲染。
类修改为:
class Room:
def __init__(self, level, room_image, level_options):
self.level = level
self.renderer = Renderer(room_image)
self.surface = self.renderer.map_surface
self.tmx_data = self.renderer.tmx_data
self.rect = self.surface.get_rect()
self.level_options = level_options
self.walls = pygame.sprite.Group()
self.doors = pygame.sprite.Group()
#self.load_walls()
#self.load_doors()
def load_walls(self):
walls = self.renderer.get_layer(self.level_options['wall_layer'])
if isinstance(walls, pytmx.TiledObjectGroup):
for object in walls:
self.walls.add(Wall(object.x, object.y, object, SPRITE_SIZE))
def load_doors(self):
doors = self.renderer.get_layer(self.level_options['door_layer'])
if isinstance(doors, pytmx.TiledTileLayer):
for x, y, image in doors.tiles():
if image:
self.doors.add(Door(self, x, y, image, SPRITE_SIZE, self.level_options['open_door_actions']))
elif isinstance(doors, pytmx.TiledObjectGroup):
for object in doors:
self.doors.add(Door(self, object.x, object.y, object.image, SPRITE_SIZE, self.level_options['open_door_actions'], object))
def update(self):
self.doors.update()
def draw(self, display, camera = None):
self.doors.draw(self.surface)
if not camera:
display.blit(self.surface, self.rect)
else:
display.blit(self.surface, (self.rect.x - camera.offset.x, self.rect.y - camera.offset.y))
def get_opening_actions(self):
return self.level_options['open_door_actions']
class Wall(pygame.sprite.Sprite):
def __init__(self, game, x, y):
self.groups = game.all_sprites, game.walls
pygame.sprite.Sprite.__init__(self, self.groups)
self.game = game
self.image = game.wall_img
self.rect = self.image.get_rect()
self.x = x
self.y = y
self.rect.x = x * SPRITE_SIZE
self.rect.y = y * SPRITE_SIZE
class Obstacle(pygame.sprite.Sprite):
def __init__(self, game, x, y, w, h):
self.groups = game.walls
pygame.sprite.Sprite.__init__(self, self.groups)
self.game = game
self.rect = pygame.Rect(x, y, w, h)
self.hit_rect = self.rect
self.x = x
self.y = y
self.rect.x = x
self.rect.y = y
class Door(pygame.sprite.Sprite):
def __init__(self, game, x, y, w, h, open_actions, animated_image):
self.groups = game.all_sprites, game.doors
pygame.sprite.Sprite.__init__(self, self.groups)
self.game = game
self.rect = pygame.Rect(x ,y, w, h)
self.current_index = 0
self.animated_image = animated_image
self.load_animations()
self.open_actions = open_actions
self.last_updated = 0
self.is_open = False
self.is_activated = False
def load_animations(self):
self.animations = []
filename = os.path.join(os.getcwd(), IMAGES_FOLDER, 'maps', 'animations', self.animated_image)
spritesheet = Spritesheet(filename, self.rect.width, self.rect.height)
for i in range(spritesheet.data['columns']):
self.animations.append(spritesheet.parse_sprite(i))
self.image = self.animations[self.current_index]
def open_animation(self):
if self.is_open:
return
if not self.is_activated or not self.animations:
return
now = pygame.time.get_ticks()
if now - self.last_updated > 15:
self.last_updated = now
self.current_index = (self.current_index + 1) % len(self.animations)
self.image = self.animations[self.current_index]
if self.current_index == len(self.animations) - 1:
self.current_index = -1
self.is_open = True
def update(self):
if not self.is_open:
self.image = self.animations[0]
if self.is_open:
self.image = self.animations[-1]
def check_doors_state(self, player):
if 'any' in self.open_actions:
self.is_activated = True
for action in player.actions:
if action in self.open_actions:
self.is_activated = True
添加了 Sprites 组生成、更新和渲染(在我的游戏中 class):
def new(self, level = 0):
# initialize all variables and do all the setup for a new game
self.all_sprites = pygame.sprite.Group()
self.walls = pygame.sprite.Group()
self.doors = pygame.sprite.Group()
self.mobs = pygame.sprite.Group()
## LOAD LEVEL
self.level = Level(level)
self.room = self.level.current_room
for tile_object in self.room.tmx_data.objects:
if tile_object.name == 'player':
self.player = Player(self, tile_object.x, tile_object.y)
if tile_object.name == 'obstacle':
Obstacle(self, tile_object.x, tile_object.y, tile_object.width, tile_object.height)
if tile_object.name == 'wall':
Wall(self, tile_object.x, tile_object.y, tile_object.width, tile_object.height)
if tile_object.name == 'door':
Door(self, tile_object.x, tile_object.y, tile_object.width, tile_object.height, self.room.get_opening_actions(), tile_object.animated_image)
def update(self):
self.all_sprites.update()
def draw(self):
self.window.fill((0, 0, 0))
## DISPLAY MAP
self.level.draw_room(self.window, self.camera)
## DISPLAY SPRITES
for sprite in self.all_sprites:
self.window.blit(sprite.image, (sprite.rect.x - self.camera.offset.x, sprite.rect.y - self.camera.offset.y))
## REFRESH SCREEN
pygame.display.flip()
这是结果(我正在为玩家开发更好的 Hitbox ^^)
door opening when player getting close to it
我有麻烦了,我正试图在我的游戏中打开大门。 我正在使用 pygame 和 pytmx,我已经构建了一个用房间制作的关卡,并且在每个房间中我都有一个使用 pytmx 的渲染器,我想要实现的是例如在 0 级玩家必须移动到门打开它并进入 1 级,我的目标是在玩家撞门时启动并绘制动画。
我尝试更新我的 Door 对象(他的父对象是 pygame Sprite class),它有点工作但当然对象被修改和显示,但是平铺地图对象也一直呆在地图的后面,所以我试着不在瓦片地图中初始化我的对象,但如果是这样,门根本不会咬人。然后我尝试修改瓦片对象图像并重新加载我的渲染但仍然不起作用,你们中的任何人有任何想法吗?
下面是部分代码,让大家更好的理解
"""
This is a test of using the pytmx library with Tiled.
"""
import pygame
import pytmx
class Renderer(object):
"""
This object renders tile maps from Tiled
"""
def __init__(self, filename):
tm = pytmx.load_pygame(filename, pixelalpha=True)
self.object_images = []
self.size = tm.width * tm.tilewidth, tm.height * tm.tileheight
self.tmx_data = tm
self.map_surface = self.make_map()
self.current_frame = 0
def render(self, surface):
tw = self.tmx_data.tilewidth
th = self.tmx_data.tileheight
print(self.tmx_data.tile_properties.items())
if self.tmx_data.background_color:
surface.fill(self.tmx_data.background_color)
for layer in self.tmx_data.layers:
if isinstance(layer, pytmx.TiledTileLayer):
for x, y, image in layer.tiles():
if image:
surface.blit(image.convert_alpha() , (x * tw, y * th))
elif isinstance(layer, pytmx.TiledObjectGroup):
for object in layer:
if object.image:
surface.blit(object.image.convert_alpha() , (object.x, object.y))
elif isinstance(layer, pytmx.TiledImageLayer):
if image:
surface.blit(image , (0, 0))
def make_map(self):
temp_surface = pygame.Surface(self.size)
self.render(temp_surface)
return temp_surface
def reload(self):
self.map_surface = self.make_map()
def update_object_image(self, object_id, surface):
pass
def get_layer(self, layer_name):
return self.tmx_data.get_layer_by_name(layer_name)
class Room:
def __init__(self, room_image, level_options):
self.renderer = Renderer(room_image)
self.surface = self.renderer.map_surface
self.rect = self.surface.get_rect()
self.level_options = level_options
self.obstacles = pygame.sprite.Group()
self.doors = pygame.sprite.Group()
self.load_obstacles()
self.load_doors()
def load_obstacles(self):
obstacles = self.level_options['obstacle_layers']
for obstacle in obstacles:
try:
items = self.renderer.get_layer(obstacle)
for x, y, image in items.tiles():
self.obstacles.add(Obstacle(x, y, image, SPRITE_SIZE))
except ValueError:
return
def load_doors(self):
doors = self.renderer.get_layer(self.level_options['door_layer'])
if isinstance(doors, pytmx.TiledTileLayer):
for x, y, image in doors.tiles():
if image:
self.doors.add(Door(self, x, y, image, SPRITE_SIZE, self.level_options['open_door_actions']))
elif isinstance(doors, pytmx.TiledObjectGroup):
for object in doors:
self.doors.add(Door(self, object.x, object.y, object.image, SPRITE_SIZE, self.level_options['open_door_actions'], object))
def update_doors(self):
for door in self.doors:
if door.object:
door.update()
#self.renderer.update_object_image(door.object.id, door.image)
class Wall(pygame.sprite.Sprite):
def __init__(self, x, y, image, sprite_size):
pygame.sprite.Sprite.__init__(self)
self.rect = pygame.Rect(x * sprite_size,y * sprite_size, sprite_size, sprite_size)
self.image = image
class Obstacle(pygame.sprite.Sprite):
def __init__(self, x, y, image, sprite_size):
pygame.sprite.Sprite.__init__(self)
self.rect = pygame.Rect(x * sprite_size,y * sprite_size, sprite_size, sprite_size)
self.image = image
class Door(pygame.sprite.Sprite):
def __init__(self, parent, x, y, image, sprite_size, open_actions, object = None):
self.parent = parent
pygame.sprite.Sprite.__init__(self)
self.object = object
if self.object:
self.rect = pygame.Rect(x ,y, self.object.width, self.object.height)
self.current_index = 0
self.load_animations()
else:
self.rect = pygame.Rect(x * sprite_size,y * sprite_size, sprite_size, sprite_size)
self.image = image
self.open_actions = open_actions
self.last_updated = 0
self.is_open = False
self.is_activated = False
def load_animations(self):
self.animations = []
filename = os.path.join(os.getcwd(), IMAGES_FOLDER, 'maps', 'animations', self.object.animated_image)
spritesheet = Spritesheet(filename, self.object.width, self.object.height)
for i in range(spritesheet.data['columns']):
self.animations.append(spritesheet.parse_sprite(i))
self.image = self.animations[self.current_index]
def open_animation(self):
if self.is_open:
return
if not self.object or not self.is_activated or not self.animations:
return
self.image = self.animations[self.current_index]
if self.current_index == len(self.animations) - 1:
self.is_open = True
def update(self):
if self.is_open:
if self.object:
self.image = self.animations[-1]
if self.is_activated:
if self.object:
now = pygame.time.get_ticks()
if now - self.last_updated > 200:
self.last_updated = now
self.current_index = (self.current_index + 1) % len(self.animations)
self.image = self.animations[self.current_index]
if not self.is_open:
self.image = self.animations[0]
def check_doors_state(self, player):
if 'any' in self.open_actions:
self.is_activated = True
for action in player.actions:
if action in self.open_actions:
self.is_activated = True
所以现在我想做的是使用动画图块对象并从 tmx 数据启动动画,但我什至无法理解如何在第一次渲染时启动动画。
提前感谢您的回答。
所以我找到了一个解决方案,我不是要更改和重新加载对象图块,我只是将我的游戏精灵分组,修改我的图块生成和渲染。
类修改为:
class Room:
def __init__(self, level, room_image, level_options):
self.level = level
self.renderer = Renderer(room_image)
self.surface = self.renderer.map_surface
self.tmx_data = self.renderer.tmx_data
self.rect = self.surface.get_rect()
self.level_options = level_options
self.walls = pygame.sprite.Group()
self.doors = pygame.sprite.Group()
#self.load_walls()
#self.load_doors()
def load_walls(self):
walls = self.renderer.get_layer(self.level_options['wall_layer'])
if isinstance(walls, pytmx.TiledObjectGroup):
for object in walls:
self.walls.add(Wall(object.x, object.y, object, SPRITE_SIZE))
def load_doors(self):
doors = self.renderer.get_layer(self.level_options['door_layer'])
if isinstance(doors, pytmx.TiledTileLayer):
for x, y, image in doors.tiles():
if image:
self.doors.add(Door(self, x, y, image, SPRITE_SIZE, self.level_options['open_door_actions']))
elif isinstance(doors, pytmx.TiledObjectGroup):
for object in doors:
self.doors.add(Door(self, object.x, object.y, object.image, SPRITE_SIZE, self.level_options['open_door_actions'], object))
def update(self):
self.doors.update()
def draw(self, display, camera = None):
self.doors.draw(self.surface)
if not camera:
display.blit(self.surface, self.rect)
else:
display.blit(self.surface, (self.rect.x - camera.offset.x, self.rect.y - camera.offset.y))
def get_opening_actions(self):
return self.level_options['open_door_actions']
class Wall(pygame.sprite.Sprite):
def __init__(self, game, x, y):
self.groups = game.all_sprites, game.walls
pygame.sprite.Sprite.__init__(self, self.groups)
self.game = game
self.image = game.wall_img
self.rect = self.image.get_rect()
self.x = x
self.y = y
self.rect.x = x * SPRITE_SIZE
self.rect.y = y * SPRITE_SIZE
class Obstacle(pygame.sprite.Sprite):
def __init__(self, game, x, y, w, h):
self.groups = game.walls
pygame.sprite.Sprite.__init__(self, self.groups)
self.game = game
self.rect = pygame.Rect(x, y, w, h)
self.hit_rect = self.rect
self.x = x
self.y = y
self.rect.x = x
self.rect.y = y
class Door(pygame.sprite.Sprite):
def __init__(self, game, x, y, w, h, open_actions, animated_image):
self.groups = game.all_sprites, game.doors
pygame.sprite.Sprite.__init__(self, self.groups)
self.game = game
self.rect = pygame.Rect(x ,y, w, h)
self.current_index = 0
self.animated_image = animated_image
self.load_animations()
self.open_actions = open_actions
self.last_updated = 0
self.is_open = False
self.is_activated = False
def load_animations(self):
self.animations = []
filename = os.path.join(os.getcwd(), IMAGES_FOLDER, 'maps', 'animations', self.animated_image)
spritesheet = Spritesheet(filename, self.rect.width, self.rect.height)
for i in range(spritesheet.data['columns']):
self.animations.append(spritesheet.parse_sprite(i))
self.image = self.animations[self.current_index]
def open_animation(self):
if self.is_open:
return
if not self.is_activated or not self.animations:
return
now = pygame.time.get_ticks()
if now - self.last_updated > 15:
self.last_updated = now
self.current_index = (self.current_index + 1) % len(self.animations)
self.image = self.animations[self.current_index]
if self.current_index == len(self.animations) - 1:
self.current_index = -1
self.is_open = True
def update(self):
if not self.is_open:
self.image = self.animations[0]
if self.is_open:
self.image = self.animations[-1]
def check_doors_state(self, player):
if 'any' in self.open_actions:
self.is_activated = True
for action in player.actions:
if action in self.open_actions:
self.is_activated = True
添加了 Sprites 组生成、更新和渲染(在我的游戏中 class):
def new(self, level = 0):
# initialize all variables and do all the setup for a new game
self.all_sprites = pygame.sprite.Group()
self.walls = pygame.sprite.Group()
self.doors = pygame.sprite.Group()
self.mobs = pygame.sprite.Group()
## LOAD LEVEL
self.level = Level(level)
self.room = self.level.current_room
for tile_object in self.room.tmx_data.objects:
if tile_object.name == 'player':
self.player = Player(self, tile_object.x, tile_object.y)
if tile_object.name == 'obstacle':
Obstacle(self, tile_object.x, tile_object.y, tile_object.width, tile_object.height)
if tile_object.name == 'wall':
Wall(self, tile_object.x, tile_object.y, tile_object.width, tile_object.height)
if tile_object.name == 'door':
Door(self, tile_object.x, tile_object.y, tile_object.width, tile_object.height, self.room.get_opening_actions(), tile_object.animated_image)
def update(self):
self.all_sprites.update()
def draw(self):
self.window.fill((0, 0, 0))
## DISPLAY MAP
self.level.draw_room(self.window, self.camera)
## DISPLAY SPRITES
for sprite in self.all_sprites:
self.window.blit(sprite.image, (sprite.rect.x - self.camera.offset.x, sprite.rect.y - self.camera.offset.y))
## REFRESH SCREEN
pygame.display.flip()
这是结果(我正在为玩家开发更好的 Hitbox ^^) door opening when player getting close to it