Automated NPC Pathfinding with TiledMap Object Layers in LibGDX

Integrating Visual Paths into Game Logic

Static or scripted movement patterns often lead to repetitive gameplay. A flexible approach involves defining navigation points directly within the level editor (Tiled) and consuming them at runtime. This method decouples level design from code logic, allowing designers to adjust routes without recompiling.

Configuring Tiled Maps

  1. Create a new Object Layer named patrolPath.
  2. Draw rectangles or circles at key intersection points on the map.
  3. Name each object sequentially (e.g., node_0, node_1, node_2) to establish order.
  4. The coordinates stored here represent the logical path points for the actor.

Runtime Data Parsing

Upon initialization, iterate through the loaded TiledMap to extract these wapyoints. Filter objects by their name prefix to ensure only valid path nodes are collected.

private void loadWaypoints(TiledMap map) {
    List<Vector2> routePoints = new ArrayList<>();
    float offsetY = map.getHeight() * TILE_SIZE; // Adjust based on coordinate system

    for (TiledObjectGroup group : map.objectGroups) {
        if (!group.name.equals("patrolPath")) continue;

        for (TiledObject obj : group.objects) {
            if (obj.name.startsWith("node_")) {
                // Convert tile coordinates to world/screen coordinates as needed
                routePoints.add(new Vector2(obj.x, offsetY - obj.y));
            }
        }
    }
    setRoute(routePoints);
}

Implementing the Patroller Class

Instead of hardcoding the distance check, use vector matehmatics to calculate direction and velocity. This ensures smooth interpolation between targets regardless of angle.

public class Patroller extends Image {
    private List<Vector2> waypoints;
    private int currentNodeIndex;
    private float speed;

    public Patroller(List<Vector2> route, TextureRegion region, float moveSpeed) {
        super(region);
        this.waypoints = route;
        this.currentNodeIndex = 0;
        this.speed = moveSpeed;
        setPosition(waypoints.get(0));
    }

    @Override
    public void act(float delta) {
        updatePosition(delta);
    }

    private void updatePosition(float dt) {
        if (currentNodeIndex >= waypoints.size() - 1) return;

        Vector2 currentPos = getPositionVector();
        Vector2 targetPos = waypoints.get(currentNodeIndex + 1);
        float distToTarget = currentPos.dst(targetPos);

        // Check if reached the specific node
        if (distToTarget <= 2.0f) {
            currentNodeIndex++;
            return;
        }

        // Calculate direction vector
        Vector2 dir = new Vector2();
        dir.set(targetPos).sub(currentPos).nor();
        
        // Move towards target
        dir.scl(speed * dt);
        addTranslation(dir.x, dir.y);
    }

    private Vector2 getPositionVector() {
        return new Vector2(getX(), getY());
    }
}

Main Application Flow

The game loop loads the map renderer and instantiates the actor once data is ready. Note that camera positioning should align with the map viewport to maintain visual consistency.

@Override
public void create() {
    // Load assets
    FileHandle tmxFile = Gdx.files.internal("map/level.tmx");
    TiledMap gameMap = TiledLoader.loadMap(tmxFile);
    TileMapRenderer renderer = new TileMapRenderer(gameMap, atlas);

    // Setup UI Stage
    stage = new Stage(new ScreenViewport());
    
    // Parse custom path data
    List<Vector2> path = new ArrayList<>();
    // ... call loadWaypoints logic ...

    // Create entity
    TextureRegion sprite = getSpriteFromPack();
    Patroller enemy = new Patroller(path, sprite, 50f); // Speed unit
    stage.addActor(enemy);
    
    Gdx.input.setInputProcessor(stage);
}

@Override
public void render() {
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    OrthographicCamera camera = getCamera();
    
    // Follow player or keep fixed
    camera.position.set(150, 150, 0);
    
    renderer.render(camera);
    stage.act(Gdx.graphics.getDeltaTime());
    stage.draw();
}

By separating path definition from execution logic, development becomes more modular. Designers can tweak routes in Tiled immediately, while the code handles general traversal mechanics.

Tags: libgdx tiled-map pathfinding game-development java

Posted on Thu, 07 May 2026 06:33:53 +0000 by fazlionline