1 package com.badlogic.gdx.maps.tiled;
3 import com.badlogic.gdx.assets.AssetDescriptor;
4 import com.badlogic.gdx.assets.AssetLoaderParameters;
5 import com.badlogic.gdx.assets.AssetManager;
6 import com.badlogic.gdx.assets.loaders.FileHandleResolver;
7 import com.badlogic.gdx.assets.loaders.SynchronousAssetLoader;
8 import com.badlogic.gdx.assets.loaders.resolvers.InternalFileHandleResolver;
9 import com.badlogic.gdx.files.FileHandle;
10 import com.badlogic.gdx.graphics.Texture;
11 import com.badlogic.gdx.graphics.g2d.TextureRegion;
12 import com.badlogic.gdx.maps.ImageResolver;
13 import com.badlogic.gdx.maps.ImageResolver.AssetManagerImageResolver;
14 import com.badlogic.gdx.maps.ImageResolver.DirectImageResolver;
15 import com.badlogic.gdx.maps.MapProperties;
16 import com.badlogic.gdx.maps.tiled.TiledMapTileLayer.Cell;
17 import com.badlogic.gdx.maps.tiled.tiles.AnimatedTiledMapTile;
18 import com.badlogic.gdx.maps.tiled.tiles.StaticTiledMapTile;
19 import com.badlogic.gdx.utils.Array;
20 import com.badlogic.gdx.utils.GdxRuntimeException;
21 import com.badlogic.gdx.utils.ObjectMap;
22 import com.badlogic.gdx.utils.XmlReader;
23 import com.badlogic.gdx.utils.XmlReader.Element;
25 import java.io.IOException;
26 import java.util.StringTokenizer;
28 public class TideMapLoader extends SynchronousAssetLoader<TiledMap, TideMapLoader.Parameters> {
30 public static class Parameters extends AssetLoaderParameters<TiledMap> {
34 private XmlReader xml = new XmlReader();
37 public TideMapLoader() {
38 super(new InternalFileHandleResolver());
41 public TideMapLoader (FileHandleResolver resolver) {
45 public TiledMap load (String fileName) {
47 FileHandle tideFile = resolve(fileName);
48 root = xml.parse(tideFile);
49 ObjectMap<String, Texture> textures = new ObjectMap<String, Texture>();
50 for(FileHandle textureFile: loadTileSheets(root, tideFile)) {
51 textures.put(textureFile.path(), new Texture(textureFile));
53 DirectImageResolver imageResolver = new DirectImageResolver(textures);
54 TiledMap map = loadMap(root, tideFile, imageResolver);
55 map.setOwnedResources(textures.values().toArray());
57 } catch(IOException e) {
58 throw new GdxRuntimeException("Couldn't load tilemap '" + fileName + "'", e);
64 public TiledMap load (AssetManager assetManager, String fileName, FileHandle tideFile, Parameters parameter) {
66 return loadMap(root, tideFile, new AssetManagerImageResolver(assetManager));
67 } catch (Exception e) {
68 throw new GdxRuntimeException("Couldn't load tilemap '" + fileName + "'", e);
73 public Array<AssetDescriptor> getDependencies (String fileName, FileHandle tmxFile, Parameters parameter) {
74 Array<AssetDescriptor> dependencies = new Array<AssetDescriptor>();
76 root = xml.parse(tmxFile);
77 for(FileHandle image: loadTileSheets(root, tmxFile)) {
78 dependencies.add(new AssetDescriptor(image.path(), Texture.class));
81 } catch (IOException e) {
82 throw new GdxRuntimeException("Couldn't load tilemap '" + fileName + "'", e);
87 * Loads the map data, given the XML root element and an {@link ImageResolver} used
88 * to return the tileset Textures
89 * @param root the XML root element
90 * @param tmxFile the Filehandle of the tmx file
91 * @param imageResolver the {@link ImageResolver}
92 * @return the {@link TiledMap}
94 private TiledMap loadMap(Element root, FileHandle tmxFile, ImageResolver imageResolver) {
95 TiledMap map = new TiledMap();
96 Element properties = root.getChildByName("properties");
97 if (properties != null) {
98 loadProperties(map.getProperties(), properties);
100 Element tilesheets = root.getChildByName("TileSheets");
101 for (Element tilesheet : tilesheets.getChildrenByName("TileSheet")) {
102 loadTileSheet(map, tilesheet, tmxFile, imageResolver);
104 Element layers = root.getChildByName("Layers");
105 for (Element layer : layers.getChildrenByName("Layer")) {
106 loadLayer(map, layer);
113 * @param root the root XML element
114 * @return a list of filenames for images containing tiles
115 * @throws IOException
117 private Array<FileHandle> loadTileSheets(Element root, FileHandle tideFile) throws IOException {
118 Array<FileHandle> images = new Array<FileHandle>();
119 Element tilesheets = root.getChildByName("TileSheets");
120 for (Element tileset : tilesheets.getChildrenByName("TileSheet")) {
121 Element imageSource = tileset.getChildByName("ImageSource");
122 FileHandle image = getRelativeFileHandle(tideFile, imageSource.getText());
128 private void loadTileSheet(TiledMap map, Element element, FileHandle tideFile, ImageResolver imageResolver) {
129 if (element.getName().equals("TileSheet")) {
130 String id = element.getAttribute("Id");
131 String description = element.getChildByName("Description").getText();
132 String imageSource = element.getChildByName("ImageSource").getText();
134 Element alignment = element.getChildByName("Alignment");
135 String sheetSize = alignment.getAttribute("SheetSize");
136 String tileSize = alignment.getAttribute("TileSize");
137 String margin = alignment.getAttribute("Margin");
138 String spacing = alignment.getAttribute("Spacing");
140 String[] sheetSizeParts = sheetSize.split(" x ");
141 int sheetSizeX = Integer.parseInt(sheetSizeParts[0]);
142 int sheetSizeY = Integer.parseInt(sheetSizeParts[1]);
144 String[] tileSizeParts = tileSize.split(" x ");
145 int tileSizeX = Integer.parseInt(tileSizeParts[0]);
146 int tileSizeY = Integer.parseInt(tileSizeParts[1]);
148 String[] marginParts = margin.split(" x ");
149 int marginX = Integer.parseInt(marginParts[0]);
150 int marginY = Integer.parseInt(marginParts[1]);
152 String[] spacingParts = margin.split(" x ");
153 int spacingX = Integer.parseInt(spacingParts[0]);
154 int spacingY = Integer.parseInt(spacingParts[1]);
156 FileHandle image = getRelativeFileHandle(tideFile, imageSource);
157 TextureRegion texture = imageResolver.getImage(image.path());
159 // TODO: Actually load the tilesheet
160 // Need to make global ids as Tide doesn't have global ids.
161 TiledMapTileSets tilesets = map.getTileSets();
163 for (TiledMapTileSet tileset : tilesets) {
164 firstgid += tileset.size();
167 TiledMapTileSet tileset = new TiledMapTileSet();
169 tileset.getProperties().put("firstgid", firstgid);
172 int stopWidth = texture.getRegionWidth() - tileSizeX;
173 int stopHeight = texture.getRegionHeight() - tileSizeY;
175 for (int y = marginY; y <= stopHeight; y += tileSizeY + spacingY) {
176 for (int x = marginX; x <= stopWidth; x += tileSizeX + spacingX) {
177 TiledMapTile tile = new StaticTiledMapTile(new TextureRegion(texture, x, y, tileSizeX, tileSizeY));
179 tileset.putTile(gid++, tile);
183 Element properties = element.getChildByName("Properties");
184 if (properties != null) {
185 loadProperties(tileset.getProperties(), properties);
188 tilesets.addTileSet(tileset);
192 private void loadLayer(TiledMap map, Element element) {
193 if (element.getName().equals("Layer")) {
194 String id = element.getAttribute("Id");
195 String visible = element.getAttribute("Visible");
197 Element dimensions = element.getChildByName("Dimensions");
198 String layerSize = dimensions.getAttribute("LayerSize");
199 String tileSize = dimensions.getAttribute("TileSize");
201 String[] layerSizeParts = layerSize.split(" x ");
202 int layerSizeX = Integer.parseInt(layerSizeParts[0]);
203 int layerSizeY = Integer.parseInt(layerSizeParts[1]);
205 String[] tileSizeParts = tileSize.split(" x ");
206 int tileSizeX = Integer.parseInt(tileSizeParts[0]);
207 int tileSizeY = Integer.parseInt(tileSizeParts[1]);
209 TiledMapTileLayer layer = new TiledMapTileLayer(layerSizeX, layerSizeY, tileSizeX, tileSizeY);
210 Element tileArray = element.getChildByName("TileArray");
211 Array<Element> rows = tileArray.getChildrenByName("Row");
212 TiledMapTileSets tilesets = map.getTileSets();
213 TiledMapTileSet currentTileSet = null;
216 for (int row = 0, rowCount = rows.size; row < rowCount; row++) {
217 Element currentRow = rows.get(row);
218 y = rowCount -1 - row;
220 for (int child = 0, childCount = currentRow.getChildCount(); child < childCount; child++) {
221 Element currentChild = currentRow.getChild(child);
222 String name = currentChild.getName();
223 if (name.equals("TileSheet")) {
224 currentTileSet = tilesets.getTileSet(currentChild.getAttribute("Ref"));
225 firstgid = currentTileSet.getProperties().get("firstgid", Integer.class);
226 } else if (name.equals("Null")) {
227 x += currentChild.getIntAttribute("Count");
228 } else if (name.equals("Static")) {
229 Cell cell = new Cell();
230 cell.setTile(currentTileSet.getTile(firstgid + currentChild.getIntAttribute("Index")));
231 layer.setCell(x++, y, cell);
232 } else if (name.equals("Animated")) {
233 // Create an AnimatedTile
234 int interval = currentChild.getInt("Interval");
235 Element frames = currentChild.getChildByName("Frames");
236 Array<StaticTiledMapTile> frameTiles = new Array<StaticTiledMapTile>();
237 for (int frameChild = 0, frameChildCount = frames.getChildCount(); frameChild < frameChildCount; frameChild++) {
238 Element frame = frames.getChild(frameChild);
239 String frameName = frame.getName();
240 if (frameName.equals("TileSheet")) {
241 currentTileSet = tilesets.getTileSet(frame.getAttribute("Ref"));
242 firstgid = currentTileSet.getProperties().get("firstgid", Integer.class);
243 } else if (frameName.equals("Static")) {
244 frameTiles.add((StaticTiledMapTile) currentTileSet.getTile(firstgid + frame.getIntAttribute("Index")));
247 Cell cell = new Cell();
248 cell.setTile(new AnimatedTiledMapTile(interval / 1000f, frameTiles));
249 layer.setCell(x++, y, cell); //TODO: Reuse existing animated tiles
253 map.getLayers().add(layer);
257 private void loadProperties(MapProperties properties, Element element) {
258 if (element.getName().equals("Properties")) {
259 for (Element property : element.getChildrenByName("Property")) {
260 String key = property.getAttribute("Key", null);
261 String type = property.getAttribute("Type", null);
262 String value = property.getText();
264 if (type.equals("Int32")) {
265 properties.put(key, Integer.parseInt(value));
266 } else if (type.equals("String")) {
267 properties.put(key, value);
268 } else if (type.equals("Boolean")) {
269 properties.put(key, value.equalsIgnoreCase("true"));
271 properties.put(key, value);
277 private static FileHandle getRelativeFileHandle(FileHandle file, String path) {
278 StringTokenizer tokenizer = new StringTokenizer(path, "\\/");
279 FileHandle result = file.parent();
280 while (tokenizer.hasMoreElements()) {
281 String token = tokenizer.nextToken();
282 if (token.equals(".."))
283 result = result.parent();
285 result = result.child(token);