Examples of animated tileset and image layer in Phaser3

Is there any examples about how to display animated tilesets and image layer in Phaser3 ? Here is a tilemap with many layers. The background are composed by two images layers. Back layer 1 contains some animated layers representing the water. There are also some objects layers for the restrictions on activity areas. How to load and use these entries in phaser 3?

Phaser 3 creates and renders tile layers only, with no tile animations.

Any image layer data is in Tilemap#images. You could use that to add images the usual way, with this.add.image(…).

For object layers, you can use createFromObjects() to convert them into sprites or other Tilemap methods to get and work on the object data yourself.

For animated tiles, you could either convert them into sprites and play “real” animations or run a simple timer changing the tile ID.

Thanks ! I will try these.
If I create sprites for each animated tiles, will this be vey time-consuming ?
image
For example, if I have four animated tiles, I have to place the sprite on each of the solts. Can I combine these tiles to make a larger sprite ?

Hello, I don’t understand why these two things are very different: Phaser.GameObjects.Sprite and Phaser.Types.GameObjects.Sprite . According to the page of SpriteConfig, I can only set the key, frame and anims. However, in some examples, x, y and scale can also be set. (In the documentation of Scene.make.sprite and createFromTiles, the SpriteConfig argument is linked to the same page. )

const config8 = {
            key: 'logo',
            x: 400,
            y: 300,
            scale: { x: { randFloat: [ 0.2, 2 ] }, y: { randFloat: [ 1.5, 3 ] } }
        };

        this.make.sprite(config8);

make.sprite(…) takes GameObjectConfig and SpriteConfig.

You could use a TileSprite, but that doesn’t have an animation component like Sprite does.

Thanks, but I still have no idea how to create an aimated tile sprite :smiling_face_with_tear:
See the codes and comments below.

//I  create some layers
map.createLayer("Back Layer 1", tileset, 0, 0);
map.createLayer("Back Layer 2", tileset, 0, tanslateY);
map.createLayer("Front Layer", tileset,  0,  tanslateY);
//In back layer1, There are some tiles which I want them to be animated.
//Here is the json related to animated tiles
let animated_tiles_json = {
 "195": //tile 195 and 196 are combined to be an animated tile.
{"animation":[{"duration":100,"tileid":195}, 
{"duration":100,"tileid":196}]},
"216":
{"animation":[{"duration":200,"tileid":216}, 
{"duration":200,"tileid":217}]}
}
//For the animated tile "195", there are two frames
let sprites = map.createFromTiles(
   [195, 196], //the two frames
   null, 
   {key: "ani", x: 400, y: 300},   //the position and the name of this sprite
   this,  //the Phaser.scene
   undefined, //camera ? in fact, I don't know how to handle this argument
   "Back Layer 1" //the animated tiles are shown in back layer1
);
//However, sprites is an empty array
console.log(sprites); // []
this.add.sprite("ani"); //I get a black square, there is nothing in it.

Say I have a 54*30 tilemap layer which is bulit from the tileset containing 200 square tiles. Then the first argument of map.createFromTiles should be the indexes in the range[0, 1620] or in the range[0, 199] ?

Since there are two animations, I think you’ll probably call createFromTiles(195, …) and createFromTiles(216, …).

After “try and error”, I finally find the solution. Thank the help from @samme .I post the codes here for anyone who needs this,
For the image layer, just treat it as a normal image. Usually we need to tile the image on a rectangle area, so we need the tileSprite function, for example

this.add.tileSprite(0, 550 2048, 512, "Background").setOrigin(0, 0); 
this.add.tileSprite(0, 380, 2048, 512, "Clouds").setOrigin(0, 0);

where “Background” and “Clouds” are the keys of the textures which we load in preload() function.

For the animated tiles, we can create an animation or change the ids of tiles in a timer.
The first way, create an animation:

//Suppose I have loaded a map with the key "map1"
let map = this.make.tilemap({key: 'map1'});
//It has a tileset, named "tileset", here I create the tileset.
let tileset = map.addTilesetImage("tileset", "tileset_image");
//Here is the json related to the animated tiles, these can be generated by Tiled
//However, phaser.js won't read these informations
let animated_tiles_json = {
            "195":{"animation":[{"duration":100,"tileid":195}, {"duration":100,"tileid":196}]},
            "216":{"animation":[{"duration":200,"tileid":216}, {"duration":200,"tileid":217}]}
        };

Here comes the important part. If we want to create an animation, we need a texture with multiple frames. The image we load (“tileset_image” in this case) is already a texture, with a single default fram __BASE. We just need to add frames to this textures. And the frames we should add are just the tiles we want to animate.

let tileid, x, y, tileWidth, tileHeight;
tileWidth = tileset.tileWidth;
tileHeight = tileset.tileHeight;

for (let key in animated_tiles_json) {
     if (animated_tiles_json.hasOwnProperty(key))
            {
                for(let i = 0; i < animated_tiles_json[key]["animation"].length; ++i)
                {
                    tileid = 1 + animated_tiles_json[key]["animation"][i]["tileid"];
                    x = map.tiles[tileid][0];
                    y = map.tiles[tileid][1];
                    this.textures.list["tileset_image"].add(
                        "tile_frame_"+tileid, //the name of frame
                        0, //source index
                        x, //x coordinate of frame
                        y, //y coordinate of frame
                        tileWidth, 
                        tileHeight); 
                }
            }
        }

Now we can create the animation:

this.anims.create({
            key: "flame_ani",
            frames: [
                {key: "tileset_image", frame: "tile_frame_196", duration:200},
                {key: "tileset_image", frame: "tile_frame_197", duration:200}
            ],
            repeat: -1
        });
       
        this.anims.create({
            key: "water_ani",
            frames: [
                {key: "tileset_image", frame: "tile_frame_217", duration:200},
                {key: "tileset_image", frame: "tile_frame_218", duration:200}
            ],
            repeat: -1
        });

The last step, we should create sprites on the position where the tiles should be animated.

//Create the "Back Layer 1"
//On this layer, there are some tiles representing water, which should be animated.
//The indexes of them are 217.
map.createLayer("Back Layer 1", tileset, 0, 0);
//Now we select all the tiles with id 217, and replace them by the empty tile, 0.
//And create sprites on the locations of these tiles. All these works are done by createFromTiles.
let sprites = map.createFromTiles(217, 0, {anims: "water_ani"});
for (let i = 0; i < sprites.length; ++i)
{
      sprites[i].setOrigin(0, 0);
 }

For the second way, we firstly define a class:

class changeTile {
    constructor()
    {
        this.I = 0; 
        this.layer = null; 
        this.tile_index = []; 
        this.tile_id_array = []; 
    }

    next ()
    {
        if (this.I == this.tile_id_array.length) this.I = 0;
        for(let i = 0; i < this.tile_index.length; ++i)
            this.layer.data[this.tile_index[i][0]][this.tile_index[i][1]].index = this.tile_id_array[this.I];
        this.I += 1;
    }
}

next, fill the class and create a timer

let ct1 = new changeTile();
        let back_layer_1 = map.getLayer("Back Layer 1");
        //The layer object which we want to change its tile id
        ct1.layer = back_layer_1;
        //Tiles 217 and 218 forms the animation
        ct1.tile_id_array.push(217, 218);
        for(let i = 0; i < back_layer_1.height; ++i)
            for(let j = 0; j < back_layer_1.width; ++j)
                //Here, we find the tiles which we should change the ids.
                if (back_layer_1.data[i][j].index == 217) ct1.tile_index.push([i, j]);
        //At last, we create the timer.
        this.event1 = this.time.addEvent(
            {
                delay: 200,
                loop:true,
                callback: () => {ct1.next(); }
            }
        );
1 Like

You can use { anims: "water_ani", origin: 0 } here.