createFromObjects gid property in Tiled

Hi,

I’m making a Mario clone as my first Phaser exercise.
As recommended by many tutorials I used Tiled to build a tile map.

I want to create sprites for the question mark bricks, which I’ve included as a layer in my tile map. I create both a static(graphic) layer and an object layer marking each brick individually.

questionMarkBricks

I just can’t for the life of me seem to be able to figure out how to make the bricks into sprites so I can change their tile on collision (to the browned question mark bricks).

The createFromObjects method requires a gid parameter. Tiled does not provide such a property, as far as I can see.

So where does this property come from? How do I add it to all my relevant objects in Tiled? Is there any way to deal with this except for adding it manually into the tilemap JSON file over and over again every time I make a change?

I can’t figure out how to use createFromTiles either.
I tried this:
questionMarkBricks = map.createFromTiles(24, null, {key: 'questionMarkBrick'}, this.scene, this.cameras.main, questionMarkBricksLayer);
Didn’t work.

Could anyone guide me on this one?

Hi, to understand you better, in your TILED map did you add the questionmark bricks as objects in an Object Layer or as tiles in a Tile Layer?

Both methods createFromObjects and createFromTiles can be very confusing if you are not familiar with how TILED internally manages id’s and gid’s.

1 Like

Notice that createFromTiles() expects the unique index of the tile (or tiles) within your map, in “TILED language” that means the gid of the tile. gid’s are calculated using the firstgid of each tileset (or image collection) plus a local id. In your example, the number 24 sounds like it’s a local id and you need to add the first gid of the tileset you are using.

// Get a reference to your tileset
var myTileset = map.getTileset('MyTileset');

// Get the firstgid of that tileset
var firstgid = myTileset.firstgid;

// Add it to the id of your tile
var questionMarkBricks = map.createFromTiles(firstgid + 24, null, {key: 'questionMarkBrick'}, this.scene, this.cameras.main, questionMarkBricksLayer);

Check this link for more info about TILED’s gid’s and local id’s https://doc.mapeditor.org/en/stable/reference/tmx-map-format/

Hope it helps :slight_smile:

I actually added the question mark bricks as both - the graphic tile texture in a Tile layer, and then I created an object layer and marked objects over each brick.

I’m not sure what “gid of the tileset” means.

I looked at the doc link you sent and I am still confused.

  • firstgid: The first global tile ID of this tileset (this global ID maps to the first tile in this tileset).

What does that mean, the first tile in the tileset? If this is my tileset in Tiled:

Where do I find the global ID?
And what is the local ID? Where do I find that?

In the image you attached, you can see in the properties panel that says ID 17, that’s the local id of the tile. Your tileset is the tile texture where ypu have all the Mario tiles. You can display your tilesets panel with the menu View->Views and Toolbars->Tileset.

The id of each tile is also displayed when you select a tile in the tileset. Or in the bottom left corner of TILED, when you move your mouse over a tile layer.

59

The number between square brackets is the local tile ID of the tile you have your mouse cursor on.

About the first gid, imagine each tileset as a collection of images, each image (or tile) of that collection has a unique id starting from 0 up to the total number of images/tiles contained in that tileset. If -for example- you have 2 tilesets (two images full of tiles), the first tile of each tileset will have the same local id (ID=0), the second tile of each tileset has ID=1, the third tile ID=2, and so on… so TILED cannot differentiate the first tile from the 1st tileset from the first tile of the 2nd tileset by using just the local ID. It needs something else. Thats where Global IDs (gid) come into place, global ids are unique ids to identify any tile or any image in a map, no matter how many tilesets you have. TILED assigns a firstgid (first global tile ID) to each tileset that maps to the first tile in that tileset.

So, lets say your map uses three different tilesets (one for background tiles, one for foreground and another one for items). For the sake of simplicity let say that each tileset has 16 tiles in it (4 rows x 4 columns). We would have this:

BG tileset
If this is the first tileset you added to the map, TILED will give it a firstgid=0.
Local ids will be from 0 to 15.
Global ids will be calculated as firstgid + local ID, that gives us global ids from 0 to 15 (cause the first gid is 0 in this case)

FG tileset
Lets assume that you added the foreground tileset after the background tileset. TILED knows that the last GID in use is 15, so the next available is 16 so for this tileset it will give it a firstgid=16.
Local ids for this tileset are also from 0 to 15, cause it has the same amount of tiles than the bg tileset.
Global ids will be calculated as firstgid + local ID, that gives us global ids from 16 to 31.

ITEMS tileset
Finally we you add this tileset last. The last GID in use was 31, the next available is 32 so for this tileset it will give it a firstgid=32.
Local ids for this tileset are also from 0 to 15.
Global ids will be from 32 to 47.

Now each tile -no matter what tileset it is- has its own and unique global ID for the Editor to use in our map.

For your question of where to find the global ID of each tile and the firstgid of each tileset… well… you can’t… not within TILED. You have to open the tmx file and search for the firstgid property of each tileset.

That firstgid is also exported in the json file you use in Phaser, and is accessible the way I mentioned earlier so you don’t need to actually know the value, just get it from the Tileset instance in phaser.

:smile:

1 Like

Wow thanks jackfreak!
That was super informative, I totally get it now.

I’ll try it out and update whether it worked or not.

Thanks for the detailed explanation!!

Well, it didn’t work. I got this error:

phaser.min.js:1 Uncaught TypeError: Cannot read property 'sprite' of undefined
    at Object.t.exports [as CreateFromTiles] (phaser.min.js:1)
    at initialize.createFromTiles (phaser.min.js:1)
    at initialize.create (game.js:71)
    at initialize.create (phaser.min.js:1)
    at initialize.loadComplete (phaser.min.js:1)
    at initialize.h.emit (phaser.min.js:1)
    at initialize.loadComplete (phaser.min.js:1)
    at initialize.fileProcessComplete (phaser.min.js:1)
    at initialize.onProcessComplete (phaser.min.js:1)
    at AudioContext.config.context (phaser.min.js:1)
t.exports @ phaser.min.js:1
createFromTiles @ phaser.min.js:1
create @ game.js:71
create @ phaser.min.js:1
loadComplete @ phaser.min.js:1
h.emit @ phaser.min.js:1
loadComplete @ phaser.min.js:1
fileProcessComplete @ phaser.min.js:1
onProcessComplete @ phaser.min.js:1
config.context @ phaser.min.js:1

also, when I run questionMarkBricks in the console, I get returned undefined.

I’ll just put the relevant part of my code here in hopes that my error will be spotted:

const config = {
    type: Phaser.AUTO, // Which renderer to use
    width: 350, // Canvas width in pixels
    height: 208, // Canvas height in pixels
    //zoom: 2, // Since we're working with 16x16 pixel tiles
    pixelArt: true, // Force the game to scale images up crisply
    parent: "game_container", // ID of the DOM element to add the canvas to
    scale: {
      parent: 'game_container',
      mode: Phaser.Scale.CENTER_BOTH,
      width: 350,
      height: 208
    },
    scene: {
      preload: preload,
      create: create,
      update: update
    },
    physics: {
      default: "arcade",
      arcade: {
        gravity: { y: 500 },
        debug: false
      }
    }
  };

  const game = new Phaser.Game(config);
  var controls, map, tiles, marioTileset, layer, player, goombas, goomba1, goomba2, themeMusic, marioDiesSound, coinCollectedSound, soundButton, groundLayer, graphicsLayer, coinLayer, coinTiles, questionMarkBricks;
  
  var score = 0;
  var soundsOn = true;
  var playerDead = false;
  
  function preload() {
    // Runs once, loads up assets like images and audio
    this.load.spritesheet('smallMario', './assets/sprites/sprites-mario-small.png', { frameWidth: 16, frameHeight: 16, endFrame: 14, spacing: 1 });
    this.load.spritesheet('big-mario', './assets/sprites/sprites-mario-big.png', { frameWidth: 16, frameHeight: 32, endFrame: 21, spacing: 1 });
    this.load.spritesheet('coin', './assets/sprites/coin.png', { frameWidth: 16, frameHeight: 32, endFrame: 21, spacing: 1 });
    this.load.spritesheet('goomba', './assets/sprites/goombas.png', { frameWidth: 16, frameHeight: 16, endFrame: 2 });
    this.load.spritesheet('soundButton', './assets/speaker_icon.png', { frameWidth: 16, frameHeight: 16, endFrame: 1 });
    this.load.image("mario-tiles", "./assets/NES-Super-Mario-Bros-Tileset.png");
    this.load.tilemapTiledJSON("Levelmap", "./tilemaps/mario-level-1-v2.json");
    this.load.bitmapFont('superMarioFont', './assets/fonts/supermario_0.png', './assets/fonts/supermario.fnt');
  }
  
  function create() {
    // Runs once, after all assets in preload are loaded

    // Load and assign Tilemap variables
    map = this.make.tilemap({ key: "Levelmap" });
    tiles = map.addTilesetImage("NES_Super_Mario", "mario-tiles");
    layer = map.createStaticLayer(0, tiles, 0, 0); // layer index, tileset, x, y
    coinTiles = map.addTilesetImage('coin');
    groundLayer = map.createStaticLayer('Ground', tiles, 0, 0);
    graphicsLayer = map.createStaticLayer('Graphics', tiles, 0, 0);
    coinLayer = map.createDynamicLayer('Coins', coinTiles, 0, 0);
    questionMarkBricksLayer = map.createStaticLayer('QuestionBricks', tiles, 0, 0);

    // tiles.firstgid equals 1
    questionMarkBricks = map.createFromTiles(tiles.firstgid+24, null, {key: 'questionMarkBrick', frame: 0}, this.scene, this.cameras.main, questionMarkBricksLayer);
    // questionMarkBricks equals undefined

    // There is more code in my create function after this but I don't believe it is relevant.
}

Thanks again for the help!

If you switch to unminified phaser.js and break on errors, you should be able to verify the argument values.

Think the scene argument is wrong. It should be this.

If you call it from the layer you can skip those extra arguments:

questionMarkBricks = questionMarkBricksLayer.createFromTiles(
  tiles.firstgid + 24,
  null,
  { key: 'questionMarkBrick', frame: 0 }
);

Also you may want to use -1 for replacements to remove the original tiles.

samme is right, the scene parameter should be this instead of this.scene.
Inside the create() function this points to the current scene.

Also createFromTiles() expects a SpriteConfig type of object, the key property should be the key of a texture you loaded in preload(), your code says key: 'questionMarkBrick' but I don’t see any texture with that key in your preload().

Glad my explanation helped :slight_smile: .

1 Like
1 Like

I’m sorry, I’m not really sure how to work with textures.

Do you have a good tutorial/guide on using textures?

@udidol in the code you shared , look in the preload() function… you are not loading any spritesheet or image named 'questionMarkBrick', that’s probably why your code throws an error when the game executes this line:

questionMarkBricks = map.createFromTiles(tiles.firstgid+24, null, {
    key: 'questionMarkBrick', // <-- PHASER CAN'T FIND A SPRITESHEET WITH 
                              // THIS KEY NAME. YOU HAVE TO LOAD ONE IN preload()
    frame: 0
}, this, this.cameras.main, questionMarkBricksLayer);

Try adding this to your preload() function:

this.load.spritesheet('questionMarkBrick', './assets/bricks.png', { 
  frameWidth: 16,
  frameHeight: 16
});

Make sure that you have bricks.png in your /assets folder. This is the image (you can replace it with a better one :wink: ):
bricks

Good luck! :space_invader::space_invader::space_invader:

Oh, I didn’t realize “texture” is meant as a general word for image graphics.
Loading spritesheets and images I understand :).

I did that with the coins in the game, but I was wondering if there’s no easier way to do it by using tiles from the already loaded tilemap image?
That’s what I was trying to do, to try and avoid loading additional files…