Search code examples
javaanimationtimerlibgdxtiled

Separate timer for each animated tile with libgdx and Tiled


Sorry for the poorly worded question, but that's the best I could think of.

So, I've got a small problem in my game project with libGDX (Java, desktop) and a tiled map created with Tiled.

With the new animated tile editor feature in Tiled, I created my animation, a single exploding bomb. It works great.

I place the bombs programmatically on the map (on its second layer) with this code (simplified to keep the important parts only):

tiledMap = new TmxMapLoader().load("maps/mymap.tmx");
TiledMapTileSet tileset = tiledMap.getTileSets().getTileSet("bombe");
animatedBomb= tileset.getTile(882);

showBomb(my_random_x, my_random_y);

public void showBomb(int x, int y){
 TiledMapTileLayer layer = (TiledMapTileLayer) tiledMap.getLayers().get(1);
 TiledMapTileLayer.Cell cellToChange = layer.getCell(x, y);
 if(cellToChange != null){
      cellToChange.setTile(animatedBomb);
 }
}

As I said, it works great, but there's a minor problem (a big one for me) that I can't seem fix: as each bomb is added, they seem to share the internal animation timer. For example:

The animation lasts 17 frames * 100ms. If I add a bomb at 0.0s of gametime, then add another one at 1.0s, the new bomb will spawn and change directly to its 10th frame, which is also the first bomb's current frame.

I'd like for each tile to have its separate animation timer. Is it possible ?

I've tried to do it with Tiled's animated tiles implementation, or with manual animations (created programmatically with AnimatedTiledMapTile and its static tiles array), but the behaviour is the same.

Update:

Even if I try to copy the object frame by frame, with a manual interval of 0.2f, the bombs are still exploding at the same time.

        TiledMapTileSet tileset = tiledMap.getTileSets().getTileSet("bombe");
        AnimatedTiledMapTile originalBomb = (AnimatedTiledMapTile)tileset.getTile(882);

        //copying the StaticTiledMapTile array of originalBomb
        Array<StaticTiledMapTile> bombTiles = new Array<StaticTiledMapTile>();
        StaticTiledMapTile[] copyArray = bombeAnimee.getFrameTiles().clone();
        for(StaticTiledMapTile tile : copyArray){
            bombTiles.add(tile);
        }

        AnimatedTiledMapTile anotherBomb = new AnimatedTiledMapTile(0.2f,bombTiles);

        TiledMapTileLayer.Cell cellToChange = layer.getCell(x, y);
        cellToChange.setTile(anotherBomb);

Solution

  • I had this same type of issue, ended up creating a custom AnimatedTiledMapTile, removing all the static and changed to private in this file, you create same way as you create the AnimatedTiledMapTile etc,

    public class SimpleAnimatedTile implements TiledMapTile {
    
    private long lastTiledMapRenderTime = 0;
    
    private int id;
    
    private BlendMode blendMode = BlendMode.ALPHA;
    
    private MapProperties properties;
    
    private MapObjects objects;
    
    private StaticTiledMapTile[] frameTiles;
    
    private int[] animationIntervals;
    private int frameCount = 0;
    private int loopDuration;
    private long initialTimeOffset = TimeUtils.millis();
    

    Then I created an array of these in my main GDX, when user selects a tile it adds a new SimpleAnimatedTile to the array

    private Array<SimpleAnimatedTile> animated_tile_array = new Array<SimpleAnimatedTile>();
    

    on user selecting the tile,

    animated_tile_array.add(new SimpleAnimatedTile(0.05f,tile_array));
    

    Then replaces the map tile with this,

    tileCell.setTile(animated_tile_array.get(animated_tile_array.size - 1));
    

    Then in the render, you need to call a method which will iterate and update each,

    updateTileAnimations();
    

    calls this method,

    private void updateTileAnimations()
    {
        for(int i = 0; i < animated_tile_array.size; i++)
        {
            SimpleAnimatedTile tile = animated_tile_array.get(i);
    
            //something to be added to sort which tile will animate or not etc <-IMPORTANT
            //or call something in custom SimpleAnimatedTile
            tile.updateAnimationBaseTime();
    
        }
    
    }
    

    Anyway this seems to stop the swarm effect of updating all at same time. hope this helps, was a quick and dirty implementation so welcome feedback to refine the solution.