1. Mis on Tiled

Tiled on 2D/3D kaardiredaktor, mis aitab luua mängumaailma. Põhifunktsiooniks on mängukaartide loomine kasutades Tileset. Tiled'i plussiks on selle paindlikkus ja intuitiivsus.

Tileset on mingi pakk tekstuurides, mida saab kasutada kaartide joonistamiseks

Kui tekkivad probleemid, siis saab avada originaalkoodi ja otsida mis läks valesti.


2. Tiled'i seadistamine

2.1. Uue kaardi loomine

  1. Vajuta New Map'i peale;

  2. Map sätted pole vaja muuta;

  3. Map Size sätted võivad olla erinevad, sõltuvad projektist, aga siin hakkame kasutama 60x34 (1920x1088 pikselit) (vajadusel saab muuta pärast loomist);

  4. Tile size säte sõltub projektist, tavaliselt kasutatakse 16x16, 32x32, 64x64, 128x128 jne, kuid selles juhendis hakkame kasutama 32x32;

  5. Vajutame Ok.

New Map

2.2. Lisa uus Tileset

Alguses on kaart tühi. Esialgul loome uue Tileset'i, vajutades New Tileset nuppu. Tileset ei pea olema üks, saab lisada mitu korraga, kui on vajadus.

New Tileset

Seejärel otsime Tileset'i (või loome käsitsi uue). Selleks, et leida sobiv Tileset, saab kasutada erinevaid ressursse. Näiteks, saab kasutada craftpix. Ise olen valinud seda. Samuti tuleb meeles pidada, et tuleb otsida Tileset suurusega 32x32, või kui teie projektis on teine suurus, siis vastav suurus.

New Tileset
New Tileset created

Uus tileset avab end küll uues aknas. Aga sellega ei pea midagi tegema ja saab lihtsalt tagasi projekti aknasse minna. Tileset on automaatselt laaditud meie esialgse projektiakna Tileset aknasse paremal ääres.

2.3. Kaardi loomine

Create the Map

Valime Tileset aknast tile'i ja hakkame kanval joonistama. Lõpuks vajutame CTRL + S või COMMAND + S, et salvestada oma kaardi.

Väga soovitan natuke katsetada ja proovida kogu Tiled'i funktsionaalsus üleval ribal.


3. Koodiosa

Esialgul, loome uue klassi TiledMapLoader. Teeme seda selleks, et meie projektis oleks struktuur ja kui on soov seda taaskasutada, et oleks lihtsam.

TiledMapLoader.java

public class TiledMapLoader {

    private TiledMap map;

    /**
     * @param path path to the map, starting from /assets folder
     */
    public TiledMapLoader(String path) {
        this.map = new TmxMapLoader().load(path);
    }

    /**
     * @return OrthogonalTiledMapRenderer of the TiledMap
     */
    public OrthogonalTiledMapRenderer setupMap() {
        return new OrthogonalTiledMapRenderer(map);
    }
}

Kutsume enda kaardiklassi välja endale sobivas Screen liidest implementivas klassis.

GameScreen.java

private OrthogonalTiledMapRenderer mapRenderer;

@Override
public void show() {
    mapRenderer = new TiledMapLoader("tiled/map.tmx").setupMap();
}

@Override
public void render(float delta) {
    Gdx.gl.glClearColor(1f, 1f, 1f, 1);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    mapRenderer.render();
}

See töötab praegu, aga näeb jube välja. Kaart on kusagil vasakul ja seda on näha ainult osaliselt. Selleks loome kaamera ja lingime seda koos mapRenderer'iga. Mainiks ka seda, et kasutame OrthographicCamera, sest meil on 2D mäng ja kasutades seda tüüpi kaamerat me saame näha meie maailma ilma perspektiivita

GameScreen.java

private OrthographicCamera camera;
private float worldWidth;
private float worldHeight;

@Override
public void show() {
    camera = new OrthographicCamera();
    mapRenderer = new TiledMapLoader("tiled/map.tmx").setupMap();
    worldWidth = (int) mapRenderer.getMap().getProperties().get("width") * Constants.PPM;
    worldHeight = (int) mapRenderer.getMap().getProperties().get("height") * Constants.PPM;
}


@Override
public void render(float delta) {
    Gdx.gl.glClearColor(1f, 1f, 1f, 1);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    camera.update();
    mapRenderer.setView(camera);
    mapRenderer.render();
}

@Override
public void resize(int width, int height) {
    camera.setToOrtho(false, worldWidth, worldHeight); // Set camera to an orthographic projection and set viewport
}

Loome uue klassi Constants ja lisame PPM.

Constants.java

public class Constants {

    public static final float PPM = 32.0f;
}

PPM (pixels per meter) defineerib mängumaailma meetrite ja ekraanipikslite omavaheline seos (1 meeter mängumaailmas on võrdne 32 piksliga).

Ready Map

4. Kuidas objekte parsida

Hetkel oleme loonud staatilise(töötab nagu taust, sh pole collisione) kaardi. Nüüd peame õppima, kuidas parsida objekte, et luua seinad, liikuvad objektid jne.

Objektide loomiseks teeme Tiled'is uue kihi Layer -> New -> Object Layer ja nimetame seda fence'ks.

Tile kihtide ja objektide kihtide erinevus on selles, et tile kiht on mõeldud kaardi joonistamiseks ja objektide kiht on mõeldud selleks, et luua näiteks collisione.

Object layer

Valime fence kihi ja siis meil on valida kolme kujundi, punkti ning terve Tile'i vahel.

  • Rectangle Object (enim kasutatud)

  • Point Object

  • Ellipse Object

  • Polygon Object

  • Tile Object

Object types

4.1. Rectangle Object

Ristküliku objektide jaoks vajutame Insert Rectangle või R klaviatuuril, et valida.

*Kui hoida SHIFT, siis on lihtsam joonistada ruudu. Kui hoida CTRL, siis on lihtsam joonistada üle ruudustiku.

Rectangle object

Parsime kõik objektid kaardilt.

TiledMapLoader.java

/**
 * Parse all objects from all layers of the TiledMap.
 */
public TiledMapLoader parseAllObjects() {
    // iterating all layers
    for (MapLayer mapLayer : map.getLayers()) {
        // iterating all objects in current the layer
        for (MapObject mapObject : mapLayer.getObjects()) {
            // check if current MapObject is instance of RectangleMapObject and cast MapObject to RectangleMapObject
            if (mapObject instanceof RectangleMapObject rectangleMapObject) {
                // get rectangle from RectangleMapObject
                Rectangle rectangle = rectangleMapObject.getRectangle();
                // ...
            }
        }
    }
    return this;
}

Või parsime ainult konkreetse kihi objektid.

TiledMapLoader.java

/**
 * Parse objects from specific layer of the TiledMap.
 *
 * @param layer name of the layer to parse
 */
public TiledMapLoader parseObjectByLayer(String layer) {
    // finding objects from layer by name and iterating
    for (MapObject mapObject : map.getLayers().get(layer).getObjects()) {
        // check if current MapObject is instance of RectangleMapObject and cast MapObject to RectangleMapObject
        if (mapObject instanceof RectangleMapObject rectangleMapObject) {
            // get rectangle from RectangleMapObject
            Rectangle rectangle = rectangleMapObject.getRectangle();
            // ...
        }
    }
    return this;
}

Ja kasutame ekraani klassis.

GameScreen.java

@Override
public void show() {
    camera = new OrthographicCamera();
    mapRenderer = new TiledMapLoader("tiled/map.tmx")
            .parseObjectByLayer("fence")
            // .parseAllObjects()
            .setupMap();
    worldWidth = (int) mapRenderer.getMap().getProperties().get("width") * Constants.PPM;
    worldHeight = (int) mapRenderer.getMap().getProperties().get("height") * Constants.PPM;
}

4.2. Point Object

Punkti objektide jaoks vajutame Insert Point või I klaviatuuril, et valida.

Point object

Punktide jaoks pole konkreetse klassi ja kasutatakse RectangleMapObject null pikkusega ja kõrgusega (width=0,height=0), sest punktil pole suurust.

Punktide kasutamine saab olla kasulik mängija alguspunktide loomisel. Saab panna mitu punkti ja juhuslikult mängija spawnida.

4.3. Ellipse Object

Ovaalsete objektide jaoks vajutame Insert Ellipse või C klaviatuuril.

Ellipse object

TiledMapLoader.java

/**
 * Parse all objects from all layers of the TiledMap.
 */
public TiledMapLoader parseAllObjects() {
    // iterating all layers
    for (MapLayer mapLayer : map.getLayers()) {
        // iterating all objects in current the layer
        for (MapObject mapObject : mapLayer.getObjects()) {
            // check if current MapObject is instance of EllipseMapObject and cast MapObject to EllipseMapObject
            if (mapObject instanceof EllipseMapObject ellipseMapObject) {
                // get ellipse from EllipseMapObject
                Ellipse ellipse = ellipseMapObject.getEllipse();
                // ...
            }
        }
    }
    return this;
}

4.4. Polygon Object

Hulknurkseid objektide jaoks vajutame Insert Polygon või P klaviatuuril, et valida.

Polygon object

TiledMapLoader.java

/**
 * Parse all objects from all layers of the TiledMap.
 */
public TiledMapLoader parseAllObjects() {
    // iterating all layers
    for (MapLayer mapLayer : map.getLayers()) {
        // iterating all objects in current the layer
        for (MapObject mapObject : mapLayer.getObjects()) {
            // check if current MapObject is instance of PolygonMapObject and cast MapObject to PolygonMapObject
            if (mapObject instanceof PolygonMapObject polygonMapObject) {
                // get polygon from PolygonMapObject
                Polygon polygon = polygonMapObject.getPolygon();
                // ...
            }
        }
    }
    return this;
}

4.5. Tile Object

Tile objektide jaoks vajutame Insert Tile või T klaviatuuril, et valida. Valime Tileset aknast tile ja paneme seda kaardi peale.

Tile object
/**
 * Parse all objects from all layers of the TiledMap.
 */
public TiledMapLoader parseAllObjects() {
    for (MapLayer mapLayer : map.getLayers()) { // iterating all layers
        for (MapObject mapObject : mapLayer.getObjects()) { // iterating all objects in current the layer
            if (mapObject instanceof TiledMapTileMapObject tiledMapTileMapObject) { // check if current MapObject is instance of TiledMapTileMapObject and cast MapObject to TiledMapTileMapObject
                float x = tiledMapTileMapObject.getX();
                float y = tiledMapTileMapObject.getY();
                TextureRegion textureRegion = tiledMapTileMapObject.getTextureRegion();
                // use variables to draw SpriteBatch
            }
        }
    }
    return this;
}

Kasutame siin TextureRegion, et saada mis tekstuur oli kasutatud objekti loomisel.

*Tile objektid saab ka kasutada, et joonistada ilma ruudustiku joondust järgimatta.

4.6. Lisainfo

Kui on soovi, saab detailselt uurida materjale objektide loomisest.

https://doc.mapeditor.org/en/stable/manual/objects/


  1. Objektide sidumine

Objekte saab ka omavahel siduda. Näiteks saab luua vaenlase objekti ja panna punkti kuhu ta peaks minema.

Esiteks, paneme objektid kaardile.

Add objects

Siis lisa objektile uus omadus path ja pane Object tüüp

Add property
Object

Ja vali nuppu Select Object on Map ja vajuta teise objekti peale.

Select Object on Map

Ja pärast võib saada see objekt kasutades

object.getProperties().get("path");

6. Kõik kujunditüüpid koos

Mõnikord peab kasutama mitu tüüpe korraga.

TiledMapLoader.java

/**
 * Parse all objects from all layers of the TiledMap.
 */
public TiledMapLoader parseAllObjects() {
    for (MapLayer mapLayer : map.getLayers()) { // iterating all layers
        for (MapObject mapObject : mapLayer.getObjects()) { // iterating all objects in current the layer
            if (mapObject instanceof RectangleMapObject rectangleMapObject) { // check if current MapObject is instance of RectangleMapObject and cast MapObject to RectangleMapObject
                Rectangle rectangle = rectangleMapObject.getRectangle(); // get rectangle from RectangleMapObject
                // ...
            } else if (mapObject instanceof EllipseMapObject ellipseMapObject) { // check if current MapObject is instance of EllipseMapObject and cast MapObject to EllipseMapObject
                Ellipse ellipse = ellipseMapObject.getEllipse(); // get ellipse from EllipseMapObject
                // ...
            } else if (mapObject instanceof PolygonMapObject polygonMapObject) { // check if current MapObject is instance of PolygonMapObject and cast MapObject to PolygonMapObject
                Polygon polygon = polygonMapObject.getPolygon(); // get polygon from PolygonMapObject
                // ...
            } else if (mapObject instanceof TiledMapTileMapObject tiledMapTileMapObject) { // check if current MapObject is instance of TiledMapTileMapObject and cast MapObject to TiledMapTileMapObject
                float x = tiledMapTileMapObject.getX();
                float y = tiledMapTileMapObject.getY();
                TextureRegion textureRegion = tiledMapTileMapObject.getTextureRegion();
                // use variables to draw SpriteBatch
            }
        }
    }
    return this;
}

On ka teine variant kuidas seda saab kirjutada kasutades switch. Minu lõplik variant:

TiledMapLoader.java

/**
 * Parse objects from specific layer of the TiledMap.
 *
 * @param layer name of the layer to parse
 */
public TiledMapLoader parseObjectByLayer(String layer) {
    for (MapObject mapObject : map.getLayers().get(layer).getObjects()) { // finding objects from layer by name and iterating
        handleMapObject(mapObject);
    }
    return this;
}

/**
 * Parse all objects from all layers of the TiledMap.
 */
public TiledMapLoader parseAllObjects() {
    for (MapLayer mapLayer : map.getLayers()) { // iterating all layers
        for (MapObject mapObject : mapLayer.getObjects()) { // iterating all objects in current the layer
            handleMapObject(mapObject);
        }
    }
    return this;
}

/**
 * Handle object according to its type
 *
 * @param mapObject map object to handle
 */
public void handleMapObject(MapObject mapObject) {
    switch (mapObject) {
        case RectangleMapObject object -> { // If object is instance of RectangleMapObject
            Rectangle rectangle = object.getRectangle(); // get rectangle from RectangleMapObject
            // ...
        }
        case EllipseMapObject object -> { // If object is instance of EllipseMapObject
            Ellipse ellipse = object.getEllipse(); // get ellipse from EllipseMapObject
            // ...
        }
        case PolygonMapObject object -> { // If object is instance of PolygonMapObject
            Polygon polygon = object.getPolygon(); // get polygon from PolygonMapObject
            // ...
        }
        case TiledMapTileMapObject object -> { // If object is instance of TiledMapTileMapObject
            float x = object.getX();
            float y = object.getY();
            TextureRegion textureRegion = object.getTextureRegion();
            // use variables to draw SpriteBatch
        }
        default -> System.out.println("Some other type");
    }
}

GameScreen.java

@Override
public void show() {
    camera = new OrthographicCamera();
    mapRenderer = new TiledMapLoader("tiled/map.tmx")
        .parseObjectByLayer("fence")
        // .parseAllObjects()
        .setupMap();
    worldWidth = (int) mapRenderer.getMap().getProperties().get("width") * Constants.PPM;
    worldHeight = (int) mapRenderer.getMap().getProperties().get("height") * Constants.PPM;
}