Skip to content

Commit 4f1728c

Browse files
committed
First working demo of multiple tileset sources specified in a single Tiled map layer being rendered as separate Batch drawing lists by the webgl renderer.
Phaser.Tilemap now creates internal map layers for each tileset except the first one (which will be handled by the createLayer call from the game). It also stores a reference to each tileset where the new map layers can access it. Phaser.TilemapLayerGL uses the new tileset reference to ensure the correct base image and tile sizes are used for each new layer. PIXI.Tilemap: added initialisation of glBatch to null in c'tor. Phaser.TilemapParser was cleaned up to remove the now unnecessary tilemap parameter and the attempt to create layers while parsing.
1 parent 8d4733c commit 4f1728c

5 files changed

Lines changed: 184 additions & 34 deletions

File tree

docs/_phaser_tilemap_GL_progress.txt

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ Attempted a quick hack to test the new layers approach but got bogged down in th
144144
Some notes for later:
145145
- Tilemap creates TilemapLayer objects (Sprites). It keeps no reference to them after adding them to a group (or the world group), the new layer object gets a 'map' reference.
146146
- Tilemap contains a layers member variable, it contains bespoke objects (see createBlankLayer) with some information about layers in the map data (which do not correspond to TilemapLayer objects at all)
147-
- TilemapParser parses raw map data into the Tilemap.data member. It creates Tileset objects and populates them for each tile set used in the raw map. It creates Tile objects for each tile in the map data.
147+
- TilemapParser parses raw map data into a local 'map' variable which is returned to be set as the Tilemap.data member. It creates Tileset objects and populates them for each tile set used in the raw map. It creates Tile objects for each tile in the map data.
148148
- Tileset contains information about separate tile sources, including the image, the first tile, tile layout in the image. It contains the draw function to render a tile on the canvas.
149149
- Tilemap.createLayer is called by Examples after the Tilemap has been created and parsed.
150150

@@ -153,6 +153,20 @@ So, I can't create new TilemapLayer objects in TilemapParser because it will pre
153153
day seven:
154154

155155
Read through the above and took a good look at the code. It's a messy solution in this context.
156-
A better idea... parse the map as previously, on return to Tilemap create layers for each 'unique' entry in tilesets array.
156+
A better idea... parse the map as originally, on return to Tilemap create layers for each 'unique' entry in tilesets array.
157157
This avoids the circular calls to and from tilemap and parse, allows map to have a populated data member after parse, and still creates the required new layers before the example calls createLayer.
158158
(Check if this process can be added to createLayer?)
159+
Added createInternalLayer to Phaser.Tilemap which creates a new internal layer for each non-zero member of the base Tilemap's tilesets list when createLayer is called. (The zero tileset is drawn by the layer created by createLayer).
160+
161+
The Tile objects have incorrect data... in the "create from objects" example the "tiles2" tiles which are 70x70 pixels have a width/height of 32x32 (the same as the base tiles from the "ground_1x1" set). I'll take a look at that in a minute after I get this to draw something (the tile used is in the top-left of the image so it should draw even through the sizes are wrong).
162+
163+
The image texture appears to also be incorrect in Tile objects that use different tilesets. This might explain why we get garbage drawn to screen, however looking at the phaser.io example I'm seeing the correct tiles being drawn. Need to check all my new code to find how it has got the wrong values.
164+
165+
Bug found in TilemapLayerGL where it sets the PIXI baseTexture. I'm seeing the top-left corner of the 70x70 tiles.
166+
167+
Added a tileset parameter to the map's layer data objects when creating an 'internal' layer.
168+
Made TilemapLayerGL grab the tile dimensions from the new tileset parameter instead of relying on the map containing only one size.
169+
170+
First working demo with multiple webgl layers each batching tiles from separate tilesets which were all attached to one map layer in the Tiled program.
171+
Tiles are still turning off too soon at the map edges (exactly like the Canvas version) but that should be fixable with this 'internal layer' approach.
172+

src/pixi/extras/Tilemap.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ PIXI.Tilemap = function(texture, mapwidth, mapheight, tilewidth, tileheight, lay
3737

3838
this.layer = layer;
3939

40+
// store the list of batch drawing instructions (for use with WebGL rendering)
41+
this.glBatch = null;
42+
4043
/**
4144
* Remember last tile drawn to avoid unnecessary set-up
4245
*

src/tilemap/Tilemap.js

Lines changed: 156 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ Phaser.Tilemap = function (game, key, tileWidth, tileHeight, width, height) {
3434
*/
3535
this.key = key;
3636

37-
var data = Phaser.TilemapParser.parse(this.game, key, tileWidth, tileHeight, width, height, this);
37+
var data = Phaser.TilemapParser.parse(this.game, key, tileWidth, tileHeight, width, height);
3838
this.data = data;
3939

4040
if (data === null)
@@ -101,7 +101,12 @@ Phaser.Tilemap = function (game, key, tileWidth, tileHeight, width, height) {
101101
* @property {array} tilesets - An array of Tilesets.
102102
*/
103103
this.tilesets = data.tilesets;
104-
104+
105+
/**
106+
* @property {array} tilesetLayers - An array of internal layers used to separate multiple tilesets from a single map layer.
107+
*/
108+
this.tilesetLayers = null;
109+
105110
/**
106111
* @property {array} imagecollections - An array of Image Collections.
107112
*/
@@ -312,6 +317,20 @@ Phaser.Tilemap.prototype = {
312317
if (this.tilesets[idx])
313318
{
314319
this.tilesets[idx].setImage(img);
320+
321+
// create an empty layer for the map parts corresponding to this tileset
322+
// if ( !this.tilesetLayers )
323+
// {
324+
// // for the first tileset, don't create a layer because createLayer will be called to do that
325+
// this.tilesetLayers = [];
326+
// }
327+
// else
328+
// {
329+
// // for all the rest, go ahead and make a (currently) blank layer
330+
// // name, width, height, tileWidth, tileHeight, group
331+
// this.tilesetLayers.push( this.createBlankLayer( "_internal" + this.tilesetLayers.length.toString() ) );
332+
// }
333+
315334
return this.tilesets[idx];
316335
}
317336
else
@@ -360,6 +379,17 @@ Phaser.Tilemap.prototype = {
360379
}
361380
}
362381

382+
// create an empty layer for the map parts corresponding to this tileset
383+
// if ( !this.tilesetLayers )
384+
// {
385+
// this.tilesetLayers = [];
386+
// }
387+
// else
388+
// {
389+
// // name, width, height, tileWidth, tileHeight, group
390+
// this.tilesetLayers.push( this.createBlankLayer( "_internal" + this.tilesetLayers.length.toString() ) );
391+
// }
392+
363393
return newSet;
364394

365395
}
@@ -568,37 +598,151 @@ Phaser.Tilemap.prototype = {
568598

569599
// Add Buffer support for the left of the canvas
570600

571-
console.log("Tilemap.createLayer", layer, width, height);
572-
573601
if (width === undefined) { width = this.game.width; }
574602
if (height === undefined) { height = this.game.height; }
575603
if (group === undefined) { group = this.game.world; }
576604

605+
console.log("Tilemap.createLayer", layer, width, height);
606+
577607
var index = layer;
578608

579609
if (typeof layer === 'string')
580610
{
581611
index = this.getLayerIndex(layer);
582612
}
583613

584-
// createLayer can be called by TilemapParser.parseTiledJSON so this.layers is undefined because we called parse to populate it
585-
// TODO: this creates an ugly circularity in the class relationship, parse/tilemap should be reworked to be cleaner
586-
if (index === null || (this.layers && index > this.layers.length))
614+
if (index === null || index > this.layers.length)
587615
{
588616
console.warn('Tilemap.createLayer: Invalid layer ID given: ' + index);
589617
return;
590618
}
591619

620+
// create the internal layers for different tilesets using this one as a base description
621+
for (var i = 1, l = this.tilesets.length; i < l; i++)
622+
{
623+
var ts = this.tilesets[i];
624+
var li = this.layers[index];
625+
this.createInternalLayer( "_internal" + i.toString(), ts, li.width, li.height, ts.tileWidth, ts.tileHeight, group );
626+
}
627+
592628
if ( this.game.renderType === Phaser.WEBGL )
593629
{
594-
// use WebGL variant of TilemapLayer when pixiTest is true
630+
// use WebGL variant of TilemapLayer
595631
return group.add(new Phaser.TilemapLayerGL(this.game, this, index, width, height));
596632
}
597633

598634
return group.add(new Phaser.TilemapLayer(this.game, this, index, width, height));
599635

600636
},
601637

638+
/**
639+
* Creates a new internal layer on this Tilemap. By default TilemapLayers are fixed to the camera.
640+
*
641+
* @method Phaser.Tilemap#createBlankLayer
642+
* @param {string} name - The name of this layer. Must be unique within the map.
643+
* @param {Phaser.Tileset} tileset - The tileset whose data is to be added to this layer.
644+
* @param {number} width - The width of the layer in tiles.
645+
* @param {number} height - The height of the layer in tiles.
646+
* @param {number} tileWidth - The width of the tiles the layer uses for calculations.
647+
* @param {number} tileHeight - The height of the tiles the layer uses for calculations.
648+
* @param {Phaser.Group} [group] - Optional Group to add the layer to. If not specified it will be added to the World group.
649+
* @return {Phaser.TilemapLayer} The TilemapLayer object. This is an extension of Phaser.Image and can be moved around the display list accordingly.
650+
*/
651+
createInternalLayer: function (name, tileset, width, height, tileWidth, tileHeight, group) {
652+
653+
console.log("Tilemap.createInternalLayer", name, width, height);
654+
655+
if (group === undefined) { group = this.game.world; }
656+
657+
if (this.getLayerIndex(name) !== null)
658+
{
659+
console.warn('Tilemap.createBlankLayer: Layer with matching name already exists');
660+
return;
661+
}
662+
663+
var layer = {
664+
665+
name: name,
666+
x: 0,
667+
y: 0,
668+
width: width,
669+
height: height,
670+
widthInPixels: width * tileWidth,
671+
heightInPixels: height * tileHeight,
672+
alpha: 1,
673+
visible: true,
674+
properties: {},
675+
indexes: [],
676+
callbacks: [],
677+
bodies: [],
678+
data: null,
679+
tileset: tileset
680+
681+
};
682+
683+
var row;
684+
var output = [];
685+
686+
for (var y = 0; y < height; y++)
687+
{
688+
row = [];
689+
for (var x = 0; x < width; x++)
690+
{
691+
// get the equivalent tile from this Tilemap
692+
var tile = this.layers[0].data[y][x];
693+
// find out which tileset it is in
694+
var setIndex = this.tiles[tile.index] && this.tiles[tile.index][2];
695+
var ts = this.tilesets[setIndex];
696+
// is it one of the ones we want to move?
697+
if ( ts == tileset )
698+
{
699+
// move the tile to this new layer
700+
row.push( tile );
701+
// erase it from the original (mixed tileset) layer
702+
this.layers[0].data[y][x] = new Phaser.Tile(layer, -1, x, y, tileWidth, tileHeight);
703+
}
704+
else
705+
{
706+
// add an empty tile
707+
row.push(new Phaser.Tile(layer, -1, x, y, tileWidth, tileHeight));
708+
}
709+
}
710+
711+
output.push(row);
712+
}
713+
714+
layer.data = output;
715+
716+
this.layers.push(layer);
717+
718+
var w = layer.widthInPixels;
719+
var h = layer.heightInPixels;
720+
721+
if (w > this.game.width)
722+
{
723+
w = this.game.width;
724+
}
725+
726+
if (h > this.game.height)
727+
{
728+
h = this.game.height;
729+
}
730+
731+
var output;
732+
if ( this.game.renderType === Phaser.WEBGL )
733+
{
734+
output = new Phaser.TilemapLayerGL(this.game, this, this.layers.length - 1, w, h);
735+
}
736+
else
737+
{
738+
output = new Phaser.TilemapLayer(this.game, this, this.layers.length - 1, w, h);
739+
}
740+
output.name = name;
741+
742+
return group.add(output);
743+
744+
},
745+
602746
/**
603747
* Creates a new and empty layer on this Tilemap. By default TilemapLayers are fixed to the camera.
604748
*
@@ -611,9 +755,10 @@ Phaser.Tilemap.prototype = {
611755
* @param {Phaser.Group} [group] - Optional Group to add the layer to. If not specified it will be added to the World group.
612756
* @return {Phaser.TilemapLayer} The TilemapLayer object. This is an extension of Phaser.Image and can be moved around the display list accordingly.
613757
*/
614-
createBlankLayer: function (name, width, height, tileWidth, tileHeight, group, pixiTest) {
758+
createBlankLayer: function (name, width, height, tileWidth, tileHeight, group) {
759+
760+
console.log("Tilemap.createBlankLayer", name, width, height);
615761

616-
if (pixiTest === undefined) { pixiTest = true; } // TODO: disable test of TilemapLayerGL unless requested
617762
if (group === undefined) { group = this.game.world; }
618763

619764
if (this.getLayerIndex(name) !== null)
@@ -677,7 +822,7 @@ Phaser.Tilemap.prototype = {
677822
}
678823

679824
var output;
680-
if ( pixiTest )
825+
if ( this.game.renderType === Phaser.WEBGL )
681826
{
682827
output = new Phaser.TilemapLayerGL(this.game, this, this.layers.length - 1, w, h);
683828
}

src/tilemap/TilemapLayerGL.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
*/
2323
Phaser.TilemapLayerGL = function (game, tilemap, index, width, height) {
2424

25+
console.log( "Phaser.TilemapLayerGL", index, width, height);
26+
2527
this.game = game;
2628

2729
width |= 0;
@@ -220,9 +222,10 @@ Phaser.TilemapLayerGL = function (game, tilemap, index, width, height) {
220222
*/
221223
this._results = [];
222224

223-
// TODO: need PIXI textures for each tileset source image
224-
var baseTexture = new PIXI.BaseTexture(this.map.tilesets[0].image);
225-
PIXI.Tilemap.call(this, new PIXI.Texture(baseTexture), this.map.width, this.map.height, this.map.tileWidth, this.map.tileHeight, this.layer);
225+
// get PIXI textures for each tileset source image
226+
var tileset = this.layer.tileset || this.map.tilesets[0];
227+
var baseTexture = new PIXI.BaseTexture( tileset.image );
228+
PIXI.Tilemap.call(this, new PIXI.Texture(baseTexture), this.map.width, this.map.height, tileset.tileWidth, tileset.tileHeight, this.layer);
226229

227230
Phaser.Component.Core.init.call(this, game, 0, 0, null, null);
228231

src/tilemap/TilemapParser.js

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,12 @@ Phaser.TilemapParser = {
3838
* @param {Phaser.Tilemap} [tileMap=null] - The Phaser.Tilemap this data is created for (used for createLayer to add new layers when mixed tilesets are used).
3939
* @return {object} The parsed map object.
4040
*/
41-
parse: function (game, key, tileWidth, tileHeight, width, height, tileMap) {
41+
parse: function (game, key, tileWidth, tileHeight, width, height) {
4242

4343
if (tileWidth === undefined) { tileWidth = 32; }
4444
if (tileHeight === undefined) { tileHeight = 32; }
4545
if (width === undefined) { width = 10; }
4646
if (height === undefined) { height = 10; }
47-
if (tileMap === undefined) { tileMap = null; }
4847

4948
if (key === undefined)
5049
{
@@ -66,7 +65,7 @@ Phaser.TilemapParser = {
6665
}
6766
else if (!map.format || map.format === Phaser.Tilemap.TILED_JSON)
6867
{
69-
return this.parseTiledJSON(map.data, tileMap);
68+
return this.parseTiledJSON(map.data);
7069
}
7170
}
7271
else
@@ -200,10 +199,9 @@ Phaser.TilemapParser = {
200199
* Parses a Tiled JSON file into valid map data.
201200
* @method Phaser.TilemapParser.parseJSON
202201
* @param {object} json - The JSON map data.
203-
* @param {Phaser.Tilemap} [tileMap=null] - The Phaser.Tilemap object which has the createLayer functionality.
204202
* @return {object} Generated and parsed map data.
205203
*/
206-
parseTiledJSON: function (json, tileMap) {
204+
parseTiledJSON: function (json) {
207205

208206
if (json.orientation !== 'orthogonal')
209207
{
@@ -649,7 +647,6 @@ Phaser.TilemapParser = {
649647
var tile;
650648
var sid;
651649
var set;
652-
var setLayers = [];
653650

654651
// go through each of the map data layers
655652
for (var i = 0; i < map.layers.length; i++)
@@ -676,18 +673,6 @@ Phaser.TilemapParser = {
676673
// find the relevant tileset
677674

678675
sid = map.tiles[tile.index][2];
679-
680-
// if this is a different tileset to the one already being used by this layer...
681-
// TODO: check if the tileset has changed size, we can probably skip this if it's identical
682-
if ( set && set.name !== map.tilesets[sid].name )
683-
{
684-
// if there's no layer for this tileset, create a clone of this layer and attach the tileset to it
685-
if ( !setLayers[set.name] )
686-
{
687-
setLayers[set.name] = tileMap.createLayer();
688-
}
689-
// TODO: add the tile to the layer which holds this tileset, and make it an invalid tile in this layer
690-
}
691676
set = map.tilesets[sid];
692677

693678

0 commit comments

Comments
 (0)