MCPcopy Index your code
hub / github.com/andriyDev/landmass

github.com/andriyDev/landmass @v0.4.0

Chat with this repo
repository ↗ · DeepWiki ↗ · release v0.4.0 ↗ · + Follow
165 symbols 428 edges 11 files 50 documented · 30%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

landmass

A Rust crate to provide a navigation system for video game characters to walk around levels.

What is a navigation system?

A navigation system is essentially the collection of tools needed for robust agent movement in video games. This generally involves 4 things:

  • Path finding (e.g. A-star)
  • Path simplification (e.g. SSFA)
  • Steering (e.g. boids)
  • Local collision avoidance

In addition, managing agents and the navigation meshes they walk on can be cumbersome, so a navigation system ideally will handle that for you.

Generally it is difficult to find a full, free system to handle all of these for you, and the goal is for landmass to work relatively easily with other languages so it can be used anywhere.

Overview

landmass has three major components: Archipelagos, Islands and Agents. An Archipelago is composed of several Islands, as well as the Agents that travel across those Islands. Each Island holds a single navigation mesh. Each game character (controlled by AI) should correspond to one Agent. To start using landmass:

  1. Create an Archipelago.
  2. Create an Island.
  3. Assign a ValidNavigationMesh to the Island.
  4. Add Agents to the Archipelago.

Each frame of the game:

  1. Set the position and velocity of each game character to its corresponding Agent.
  2. Call update on the Archipelago.
  3. Use the desired move from each Agent to inform the corresponding game character where it should move.

Note: landmass intentionally does not update the Agents position itself. Generally, characters are moved using some other method (like a physics simulation) rather than just moving the character, so moving the Agent would be confusing.

Example

use glam::Vec3;
use landmass::*;
use std::sync::Arc;

let mut archipelago = Archipelago::new();

let nav_mesh = NavigationMesh {
  mesh_bounds: None,
  vertices: vec![
    Vec3::new(0.0, 0.0, 0.0),
    Vec3::new(15.0, 0.0, 0.0),
    Vec3::new(15.0, 0.0, 15.0),
    Vec3::new(0.0, 0.0, 15.0),
  ],
  polygons: vec![vec![0, 1, 2, 3]],
};

let valid_nav_mesh = Arc::new(
  nav_mesh.validate().expect("Validation succeeds")
);

let island_id = archipelago.add_island();
archipelago
  .get_island_mut(island_id)
  .set_nav_mesh(
    Transform { translation: Vec3::ZERO, rotation: 0.0 },
    valid_nav_mesh,
  );

let agent_1 = archipelago.add_agent({
  let mut agent = Agent::create(
    /* position= */ Vec3::new(1.0, 0.0, 1.0),
    /* velocity= */ Vec3::ZERO,
    /* radius= */ 1.0,
    /* max_velocity= */ 1.0,
  );
  agent.current_target = Some(Vec3::new(11.0, 0.0, 1.1));
  agent.target_reached_condition = TargetReachedCondition::Distance(0.01);
  agent
});
let agent_2 = archipelago.add_agent({
  let mut agent = Agent::create(
    /* position= */ Vec3::new(11.0, 0.0, 1.1),
    /* velocity= */ Vec3::ZERO,
    /* radius= */ 1.0,
    /* max_velocity= */ 1.0,
  );
  agent.current_target = Some(Vec3::new(1.0, 0.0, 1.0));
  agent.target_reached_condition = TargetReachedCondition::Distance(0.01);
  agent
});

for i in 0..200 {
  let delta_time = 1.0 / 10.0;
  archipelago.update(delta_time);

  for agent_id in archipelago.get_agent_ids().collect::<Vec<_>>() {
    let agent = archipelago.get_agent_mut(agent_id);
    agent.velocity = agent.get_desired_velocity();
    agent.position += agent.velocity * delta_time;
  }
}

assert!(archipelago
  .get_agent(agent_1)
  .position
  .abs_diff_eq(Vec3::new(11.0, 0.0, 1.1), 0.1));
assert!(archipelago
  .get_agent(agent_2)
  .position
  .abs_diff_eq(Vec3::new(1.0, 0.0, 1.0), 0.1));

License

License under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Extension points exported contracts — how you extend this code

AStarProblem (Interface)
A generic A* problem. [2 implementers]
src/astar.rs
DebugDrawer (Interface)
Trait to "draw" Archipelago state to. Users should implement this to visualize the state of their Archipelago. [1 implementers]
src/debug.rs

Core symbols most depended-on inside this repo

validate
called by 32
src/nav_mesh.rs
apply
called by 27
src/util.rs
set_nav_mesh
called by 24
src/island.rs
add_island
called by 16
src/lib.rs
get_island_mut
called by 13
src/lib.rs
add_agent
called by 8
src/lib.rs
get_agent_mut
called by 8
src/lib.rs
partial_cmp
called by 8
src/astar.rs

Shape

Method 69
Function 61
Class 24
Enum 9
Interface 2

Languages

Rust100%

Modules by API surface

src/nav_mesh.rs30 symbols
src/lib.rs25 symbols
src/astar.rs22 symbols
src/util.rs21 symbols
src/debug.rs13 symbols
src/avoidance.rs13 symbols
src/pathfinding.rs10 symbols
src/path.rs10 symbols
src/agent.rs9 symbols
src/island.rs7 symbols
src/nav_data.rs5 symbols

For agents

$ claude mcp add landmass \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact