UNPKG

7.68 kBJavaScriptView Raw
1/** @module splat-ecs/lib/import-from-tiled */
2
3var clone = require("./clone");
4var path = require("path");
5var setOrAddComponent = require("./set-or-add-component");
6
7/**
8 * <p>Import an orthogonal tilemap from the Tiled map editor.</p>
9 * <p>This will create entities in the current scene representing all the tiles & objects in the tilemap. Each tile gets a <code>tile</code> component tag so you can find them later. Each tile also gets a <code>grid</code> component with the x/y/z grid coordinates in tile space.</p>
10 * <p>All of the properties that are in the tilemap are set as components in each tile entity, and the property values are treated as JSON. More specific properties override less specific properties. The order of precedence for tile properties is:</p>
11 * <ol>
12 * <li>tile layer</li>
13 * <li>tile in tileset</li>
14 * <li>tileset</li>
15 * </ol>
16 * <p>The order of precendence for object properties is:</p>
17 * <ol>
18 * <li>object</li>
19 * <li>object layer</li>
20 * <li>tile in tileset</li>
21 * <li>tileset</li>
22 * </ol>
23 * <p>A special "container" entity is also created with a <code>container</code> component tag that has the dimensions of the map in its <code>size</code> component.</p>
24 * @function importTilemap
25 * @param {Object} file JSON file exported from Tiled. This should be required in a scene enter
26 * script and passed to the function.
27 * @param {external:EntityPool} entities EntityPool from game.entities
28 * @param {ImageLoader} images ImageLoader from game.images
29 * @see {@link http://www.mapeditor.org/ Tiled Map Editor}
30 */
31module.exports = function importTilemap(file, entities, images) {
32
33 var imageComponents = tilesetsToImageComponents(file.tilesets, images);
34
35 for (var z = 0; z < file.layers.length; z++) {
36 var layer = file.layers[z];
37 if (layer.type == "tilelayer") {
38 makeTiles(file, z, entities, imageComponents);
39 } else if (layer.type == "objectgroup") {
40 makeObjects(file, z, entities, imageComponents);
41 }
42 }
43
44 // create a "container" entity so we can find the bounds of the map, and maybe constrain the player to it
45 var container = entities.create();
46 entities.setComponent(container, "name", "container");
47 entities.setComponent(container, "container", true);
48 entities.addComponent(container, "position");
49 var size = entities.addComponent(container, "size");
50 size.width = file.width * file.tilewidth;
51 size.height = file.height * file.tileheight;
52};
53
54function tilesetsToImageComponents(tilesets, images) {
55 var imageComponents = [];
56 for (var i = 0; i < tilesets.length; i++) {
57 if (tilesets[i].image) {
58 tilesetToImage(tilesets[i], imageComponents);
59 } else {
60 collectionOfImagesToImage(tilesets[i], imageComponents, images);
61 }
62 }
63 return imageComponents;
64}
65
66function tilesetToImage(tileset, imageComponents) {
67 var i = tileset.firstgid;
68 var j = 0;
69 for (var y = tileset.margin; y < tileset.imageheight - tileset.margin; y += tileset.tileheight + tileset.spacing) {
70 for (var x = tileset.margin; x < tileset.imagewidth - tileset.margin; x += tileset.tilewidth + tileset.spacing) {
71 var tileProps = (tileset.tileproperties || {})[j];
72 var props = merge(clone(tileset.properties || {}), tileProps);
73 imageComponents[i] = {
74 image: {
75 name: path.basename(tileset.image),
76 sourceX: x,
77 sourceY: y,
78 sourceWidth: tileset.tilewidth,
79 sourceHeight: tileset.tileheight,
80 destinationWidth: tileset.tilewidth,
81 destinationHeight: tileset.tileheight
82 },
83 properties: props
84 };
85 i++;
86 j++;
87 }
88 }
89}
90
91function collectionOfImagesToImage(tileset, imageComponents, images) {
92 var keys = Object.keys(tileset.tiles).map(function(id) { return parseInt(id); });
93 for (var k = 0; k < keys.length; k++) {
94 var key = keys[k];
95 var tileProps = (tileset.tileproperties || {})[key];
96 var props = merge(clone(tileset.properties || {}), tileProps);
97 var name = path.basename(tileset.tiles[key].image);
98 var image = images.get(name);
99 imageComponents[tileset.firstgid + key] = {
100 image: {
101 name: name,
102 sourceX: 0,
103 sourceY: 0,
104 sourceWidth: image.width,
105 sourceHeight: image.height,
106 destinationWidth: image.width,
107 destinationHeight: image.height
108 },
109 properties: props
110 };
111 }
112}
113
114function merge(dest, src) {
115 if (src === undefined) {
116 return dest;
117 }
118 var keys = Object.keys(src);
119 for (var i = 0; i < keys.length; i++) {
120 dest[keys[i]] = src[keys[i]];
121 }
122 return dest;
123}
124
125function makeTiles(file, z, entities, imageComponents) {
126 var layer = file.layers[z];
127 for (var i = 0; i < layer.data.length; i++) {
128 var tile = layer.data[i];
129 if (tile === 0) {
130 continue;
131 }
132 var image = clone(imageComponents[tile].image);
133 var gridX = i % file.width;
134 var gridY = Math.floor(i / file.width);
135 var x = gridX * file.tilewidth;
136 var y = gridY * file.tileheight - image.sourceHeight;
137 var entity = makeTile({ x: x, y: y, z: z }, { x: gridX, y: gridY, z: z }, image, entities);
138 setComponentsFromProperties(entity, imageComponents[tile].properties, entities);
139 setComponentsFromProperties(entity, layer.properties, entities);
140 }
141}
142
143function makeTile(position, grid, image, entities) {
144 var tile = entities.create();
145 entities.setComponent(tile, "name", "tile");
146 entities.setComponent(tile, "tile", true);
147
148 var pos = entities.addComponent(tile, "position");
149 pos.x = position.x;
150 pos.y = position.y;
151 pos.z = position.z;
152
153 var g = entities.addComponent(tile, "grid");
154 g.x = grid.x;
155 g.y = grid.y;
156 g.z = grid.z;
157
158 addImage(entities, tile, image);
159
160 var size = entities.addComponent(tile, "size");
161 size.width = image.sourceWidth;
162 size.height = image.sourceHeight;
163
164 return tile;
165}
166
167function addImage(entities, id, image) {
168 var img = entities.addComponent(id, "image");
169 img.name = image.name;
170 img.sourceX = image.sourceX;
171 img.sourceY = image.sourceY;
172 img.sourceWidth = image.sourceWidth;
173 img.sourceHeight = image.sourceHeight;
174 img.destinationWidth = image.destinationWidth;
175 img.destinationHeight = image.destinationHeight;
176}
177
178function makeObjects(file, z, entities, imageComponents) {
179 var layer = file.layers[z];
180 for (var i = 0; i < layer.objects.length; i++) {
181 var object = layer.objects[i];
182 makeObject(layer, object, z, entities, imageComponents);
183 }
184}
185
186function makeObject(layer, object, z, entities, imageComponents) {
187 var entity = entities.create();
188 entities.setComponent(entity, "name", object.name);
189 entities.setComponent(entity, "type", object.type);
190
191 var position = entities.addComponent(entity, "position");
192 position.x = object.x;
193 position.y = object.y;
194 position.z = z;
195
196 var size = entities.addComponent(entity, "size");
197 size.width = object.width;
198 size.height = object.height;
199
200 if (object.gid !== undefined) {
201 addImage(entities, entity, imageComponents[object.gid].image);
202 setComponentsFromProperties(entity, imageComponents[object.gid].properties, entities);
203 }
204 setComponentsFromProperties(entity, layer.properties, entities);
205 setComponentsFromProperties(entity, object.properties, entities);
206}
207
208function setComponentsFromProperties(entity, properties, entities) {
209 if (!properties) {
210 return;
211 }
212 Object.keys(properties).forEach(function(key) {
213 var value = parsePropertyValue(properties[key]);
214 setOrAddComponent(entities, entity, key, value);
215 });
216}
217
218function parsePropertyValue(val) {
219 try {
220 return JSON.parse(val);
221 } catch (e) {
222 if (e instanceof SyntaxError) {
223 return val;
224 } else {
225 throw e;
226 }
227 }
228}