AgentStack
SKILL verified MIT Self-run

Libgdx Gdx Ai

skill-kyu-n-gdx-claude-skills-libgdx-gdx-ai · by kyu-n

Use when writing libGDX Java/Kotlin code involving gdx-ai — steering behaviors (Steerable, Arrive, Seek, Flee, Wander, Pursue, Evade, BlendedSteering, PrioritySteering), A* pathfinding (IndexedAStarPathFinder, Graph, Heuristic), behavior trees (BehaviorTree, LeafTask, Sequence, Selector, .tree text format, BehaviorTreeParser), finite state machines (State, DefaultStateMachine, StackStateMachine,…

No reviews yet
0 installs
10 views
0.0% view→install

Install

$ agentstack add skill-kyu-n-gdx-claude-skills-libgdx-gdx-ai

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Libgdx Gdx Ai? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

libGDX gdx-ai Extension

Reference for steering behaviors, A* pathfinding, behavior trees, finite state machines, and scheduling in the gdx-ai extension.

Dependency

// In core module only — pure Java, no native dependencies
implementation "com.badlogicgames.gdx:gdx-ai:$aiVersion"

Works on all backends including headless. No platform-specific jars needed.

Steering Behaviors

Steerable Interface

Steerable> extends Location and Limiter. Your entity implements it, providing: getPosition(), getOrientation(), setOrientation(), newLocation(), vectorToAngle(), angleToVector(), getLinearVelocity(), getAngularVelocity(), getBoundingRadius(), isTagged()/setTagged(), and all Limiter getters/setters for max linear/angular speed/acceleration.

vectorToAngle/angleToVector convention: vectorToAngle(v) = atan2(-v.x, v.y), angleToVector(out, a) sets out.x = -sin(a), out.y = cos(a). Getting this wrong causes steering to point in the wrong direction.

SteeringAcceleration — The Output Container

SteeringAcceleration has public fields T linear and float angular. Create ONE per entity, reuse each frame: new SteeringAcceleration<>(new Vector2()).

calculateSteering() only fills the output container — it does NOT move the entity. You must apply the acceleration to velocity and position yourself each frame.

Common Setup Pattern

// 1. Create behavior — target via constructor or setter
Arrive arrive = new Arrive<>(steerable, targetLocation)
    .setArrivalTolerance(0.5f)        // stop within this distance
    .setDecelerationRadius(3f)        // start slowing here
    .setTimeToTarget(0.1f);           // response speed (lower = snappier)
// Or set target separately:
// Arrive arrive = new Arrive<>(steerable)
//     .setTarget(targetLocation)
//     ...;

// 2. Each frame — calculate then apply
arrive.calculateSteering(steeringOutput);
linearVelocity.mulAdd(steeringOutput.linear, deltaTime);
if (linearVelocity.len2() > maxLinearSpeed * maxLinearSpeed)
    linearVelocity.setLength(maxLinearSpeed);
position.mulAdd(linearVelocity, deltaTime);

Target is Location, not Steerable. Any Location implementation works. Passing another Steerable works because Steerable extends Location.

Individual Behaviors

Most-used: Seek (move toward target), Flee (move away), Arrive (decelerate to stop at target), Wander (random-looking movement), Pursue/Evade (predict moving target's future position).

Also available: Face, LookWhereYouGoing, ReachOrientation, FollowPath, FollowFlowField, RaycastObstacleAvoidance, Hide (extends Arrive — uses Proximity to find obstacles but is an individual behavior).

Group Behaviors

Require a Proximity to detect nearby agents: Separation, Alignment, Cohesion, CollisionAvoidance.

PrioritySteering and BlendedSteering

PrioritySteering — try behaviors in order, use first that produces non-zero output:

PrioritySteering priority = new PrioritySteering<>(steerable, 0.001f);
//                                                          epsilon ↑ threshold
// below this magnitude, output considered "zero" → try next behavior
priority.add(obstacleAvoidance);  // highest priority
priority.add(arrive);             // fallback

BlendedSteering — weighted sum of multiple behaviors:

BlendedSteering blended = new BlendedSteering<>(steerable);
blended.add(arrive, 0.7f);         // 70% arrive
blended.add(separation, 0.3f);     // 30% separation

Limiter Interface

Steerable extends Limiter (caps linear/angular speed/acceleration). Behaviors read limits from the owner. Override per-behavior via behavior.setLimiter(customLimiter).

Pathfinding

Graph Setup

Implement IndexedGraph with three methods: getConnections(node) returns Array>, getIndex(node) returns a unique int index, getNodeCount() returns total node count.

Connection has three methods: getCost(), getFromNode(), getToNode(). For simple weighted edges, use DefaultConnection.

Heuristic and Pathfinding

Heuristic heuristic = (node, end) ->                       // Manhattan for 4-dir
    Math.abs(end.x - node.x) + Math.abs(end.y - node.y);
IndexedAStarPathFinder pathFinder = new IndexedAStarPathFinder<>(graph);
DefaultGraphPath outPath = new DefaultGraphPath<>();
boolean found = pathFinder.searchNodePath(startNode, endNode, heuristic, outPath);
outPath.clear();                                                      // MUST clear before reuse

HierarchicalPathFinder — for large maps; searches at multiple abstraction levels. Use when standard A* is too slow.

NavMesh

NavMesh is NOT included in gdx-ai. Use a third-party library or build your own graph from a nav mesh. gdx-ai pathfinding works with any graph you implement — tile grids, waypoint graphs, or nav mesh triangulations — but provides no mesh generation or triangle-based nav classes.

Behavior Trees

BehaviorTree and Task

BehaviorTree is the root container. E is the blackboard type (your data object shared by all tasks). Call tree.step() each frame.

BehaviorTree tree = parser.parse(Gdx.files.internal("ai/enemy.tree"), enemy);
// In render/update:
tree.step();

No GL context needed. BehaviorTree is pure logic — works in headless, server, or testing environments.

LeafTask

Extend LeafTask for custom logic. Override execute() returning Status:

public class IsEnemyVisible extends LeafTask {
    @Override
    public Status execute() {
        Enemy e = getObject();  // blackboard access
        return e.canSeePlayer() ? Status.SUCCEEDED : Status.FAILED;
    }

    @Override
    protected Task copyTo(Task task) { return task; }
}

Status values: RUNNING (continue next frame), SUCCEEDED, FAILED.

Branch Tasks

Sequence = AND (run children in order, fail on first failure). Selector = OR (run children in order, succeed on first success). Also: Parallel (run all simultaneously), RandomSelector, RandomSequence.

Decorator Tasks

AlwaysFail, AlwaysSucceed, Invert (flip SUCCEEDED/FAILED), Repeat (N times or indefinitely), UntilFail, UntilSuccess, SemaphoreGuard (limits concurrent subtree access).

Note: The class is SemaphoreGuard, not Semaphore. Also available: Wait (leaf — pauses for a duration) and Include (decorator — includes an external subtree).

Text Format (.tree files)

BehaviorTreeParser loads .tree files. Indentation defines hierarchy. Without import, task names must be fully-qualified. With import com.mygame.ai.tasks.*, unqualified names work.

import com.mygame.ai.tasks.*
selector
  sequence
    isEnemyVisible
    attack damage:20
  wander

Task attributes use @TaskAttribute annotation on fields. Set in .tree as taskName attrName:value.

BehaviorTreeParser parser = new BehaviorTreeParser<>();
BehaviorTree tree = parser.parse(Gdx.files.internal("ai/enemy.tree"), enemy);

Blackboard Pattern

E is your blackboard — all tasks access it via getObject(). Can be the entity itself (BehaviorTree) or a dedicated data object (BehaviorTree).

Finite State Machines

State Interface

State has four methods: enter(E), update(E), exit(E), onMessage(E, Telegram). Idiomatic pattern: implement as an enum (enum EnemyState implements State).

DefaultStateMachine vs StackStateMachine

DefaultStateMachine — single active state, changeState() replaces it. StackStateMachine — each changeState() pushes current state onto stack; revertToPreviousState() pops. Arbitrary depth — use for interruptible states (stun, cutscene, pause).

DefaultStateMachine fsm = new DefaultStateMachine<>(entity, EnemyState.IDLE);
// or StackStateMachine fsm = new StackStateMachine<>(entity, EnemyState.IDLE);

Update each frame: fsm.update();

MessageManager

Inter-entity communication with optional delay. Entities implement Telegraph with handleMessage(Telegram) (typically delegates to fsm.handleMessage(telegram)).

Sending messages — Telegraph references, NOT integer IDs:

MessageManager dispatcher = MessageManager.getInstance();
dispatcher.dispatchMessage(sender, receiver, MSG_SPOTTED);         // immediate
dispatcher.dispatchMessage(sender, receiver, MSG_DAMAGE, 25f);     // with extra data
dispatcher.dispatchMessage(2.0f, sender, receiver, MSG_BACKUP);    // delayed 2s
dispatcher.addListener(receiver, MSG_SPOTTED);                     // register for type

You MUST update each frame for delayed messages: GdxAI.getTimepiece().update(deltaTime); then MessageManager.getInstance().update();

Scheduling

LoadBalancingScheduler distributes AI updates across frames. Entities implement Schedulable (method run(long nanoTimeToRun)). Add via scheduler.addWithAutomaticPhasing(entity, framesPerRun). Call scheduler.run(nanoBudget) each frame.

Common Mistakes

  1. Forgetting to set a target on Arrive/Seek — Set via constructor new Arrive<>(steerable, targetLocation) or via .setTarget(location). Without a target, calculateSteering() throws NullPointerException.
  2. Claiming NavMesh is built-in — gdx-ai has NO NavMesh classes. Build your own graph from a nav mesh.
  3. Confusing Sequence and Selector — Sequence = AND (all must succeed). Selector = OR (first success wins).
  4. Using Semaphore instead of SemaphoreGuard — The decorator class is SemaphoreGuard.
  5. Not calling tree.step() each frame — Behavior trees don't update automatically.
  6. Dispatching messages with integer IDsMessageManager.dispatchMessage() takes Telegraph object references as sender/receiver, not integer IDs.
  7. Not updating GdxAI timepiece — Call GdxAI.getTimepiece().update(deltaTime) each frame, or delayed messages and scheduling won't work.
  8. Mixing Vector2/Vector3 generics — All steering types for one entity must use the same vector type consistently.
  9. Thinking BehaviorTree needs GL — BT, FSM, pathfinding, and scheduling are all pure logic. They work in headless environments.
  10. Forgetting copyTo() in LeafTask — Required for tree cloning. Minimal implementation: return task;
  11. Calling calculateSteering() without applying the result — Behaviors only compute acceleration. You must integrate it into velocity and position yourself each frame.
  12. Reusing DefaultGraphPath without calling clear() — Old path nodes accumulate alongside new ones. Always call outPath.clear() before a new searchNodePath call.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.