I am in troubles, I am trying to make doors opening in my game. I am using pygame, and pytmx, I have built a Level made with Rooms, and in each Room I have a Renderer using pytmx, what I want to achieve is for exemple on level 0 the player has to move to a door to open it and enter level 1, my goal is to launch and draw the animation when player hit the door.
I tried to make my Door object (his parent is pygame Sprite class) update, it has kind of worked but of course the object was modified and displayed, but the tiled map object also and was staying in the back of the map, so I tried to just not initialize my objects in the tile map but if so the door doesn't bit at all. Then I tried to modify the tile object image and to reload my rendering but still doesn't work, does anyone of you has any idea ?
Here is some parts of the code to let you have better understanding
"""
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
So right now what I am trying to do is to use an animated tile object and launch the animation from tmx data, but I can't even understand how to launch the animation at the first rendering.
Thank you in advance for you answers.
So I found a solution, I am not trying to change and reload the object tiles, I simply made my game sprites in groups, modified my generating and rendering of tiles.
Classes modified :
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 groups generating, updating and rendering added (in my game 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()
And here is the result (I am working on better hitbox for the player ^^) door opening when player getting close to it