1

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.

SnK
  • 58
  • 1
  • 6

1 Answers1

1

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

SnK
  • 58
  • 1
  • 6