ludic.nav, ludic.wildlife (18.4): a walker's separation, and a species' spacing

nav_crowd_add takes how hard a walker keeps its distance, and WildSpecies.spacing (default 2) is a species' room from others walking by, which the valley hands the crowd. Both libraries are rebuilt; nav 15/15 on the Mac and the PC, DLL KERNEL32 only. wildlife 26/26.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-28 00:45:14 +03:00
parent 95f2087148
commit d83eabfdf1
8 changed files with 17 additions and 14 deletions

View file

@ -75,7 +75,7 @@ the PC (Git Bash, the LLVM installer's clang). `native/LICENSE-recastnavigation`
## Crowds (phase 18) ## Crowds (phase 18)
`nav_crowd_start(kind, max, max_radius)` puts a DetourCrowd on a kind's mesh, with the mesh's tastes as `nav_crowd_start(kind, max, max_radius)` puts a DetourCrowd on a kind's mesh, with the mesh's tastes as
its filters. `nav_crowd_add(kind, x, y, z, radius, height, speed, filter)` adds a walker (snapped to its filters. `nav_crowd_add(kind, x, y, z, radius, height, speed, filter, separation)` adds a walker (snapped to
the mesh, -1 off it), `nav_crowd_target` and `nav_crowd_speed` say where and how fast, and the mesh, -1 off it), `nav_crowd_target` and `nav_crowd_speed` say where and how fast, and
`nav_crowd_step(dt)` steps every crowd. `nav_crowd_read` puts a walker into `nav_agent_x/y/z/vx/vz`, `nav_crowd_step(dt)` steps every crowd. `nav_crowd_read` puts a walker into `nav_agent_x/y/z/vx/vz`,
and `nav_crowd_us()` is what the stepping has cost. The mechanic decides and the crowd moves, and and `nav_crowd_us()` is what the stepping has cost. The mechanic decides and the crowd moves, and

View file

@ -20,11 +20,12 @@ function nv_crowd_drop(nav_st: mut NavState, kind: int) -> void {
nav_st.nv_crowds[kind] = null nav_st.nv_crowds[kind] = null
} }
# a walker at (x, y, z) with its width, height, top speed and taste: its index, or -1 # a walker at (x, y, z) with its width, height, top speed, taste and how hard it keeps its distance
export function nav_crowd_add(nav_st: NavState, kind: int, x: float, y: float, z: float, radius: float, height: float, speed: float, filter: int) -> int { # from the others (2 an ordinary walker): its index, or -1
export function nav_crowd_add(nav_st: NavState, kind: int, x: float, y: float, z: float, radius: float, height: float, speed: float, filter: int, separation: float) -> int {
let c = nv_crowd(nav_st, kind) let c = nv_crowd(nav_st, kind)
if c == null { return -1 } if c == null { return -1 }
return nvc_crowd_add(c, x, y, z, radius, height, speed, filter) return nvc_crowd_add(c, x, y, z, radius, height, speed, filter, separation)
} }
export function nav_crowd_remove(nav_st: NavState, kind: int, i: int) -> void { export function nav_crowd_remove(nav_st: NavState, kind: int, i: int) -> void {
let c = nv_crowd(nav_st, kind) let c = nv_crowd(nav_st, kind)

Binary file not shown.

Binary file not shown.

View file

@ -17,7 +17,7 @@ extern function nvc_random_near(h: pointer, f: int, x: float, y: float, z: float
extern function nvc_stage(size: int) -> pointer = "nav_stage" extern function nvc_stage(size: int) -> pointer = "nav_stage"
extern function nvc_crowd_new(h: pointer, max: int, max_radius: float) -> pointer = "nav_crowd_new" extern function nvc_crowd_new(h: pointer, max: int, max_radius: float) -> pointer = "nav_crowd_new"
extern function nvc_crowd_free(c: pointer) -> void = "nav_crowd_free" extern function nvc_crowd_free(c: pointer) -> void = "nav_crowd_free"
extern function nvc_crowd_add(c: pointer, x: float, y: float, z: float, radius: float, height: float, speed: float, filter: int) -> int = "nav_crowd_add" extern function nvc_crowd_add(c: pointer, x: float, y: float, z: float, radius: float, height: float, speed: float, filter: int, separation: float) -> int = "nav_crowd_add"
extern function nvc_crowd_remove(c: pointer, i: int) -> void = "nav_crowd_remove" extern function nvc_crowd_remove(c: pointer, i: int) -> void = "nav_crowd_remove"
extern function nvc_crowd_target(c: pointer, i: int, x: float, y: float, z: float) -> int = "nav_crowd_target" extern function nvc_crowd_target(c: pointer, i: int, x: float, y: float, z: float) -> int = "nav_crowd_target"
extern function nvc_crowd_speed(c: pointer, i: int, speed: float) -> void = "nav_crowd_speed" extern function nvc_crowd_speed(c: pointer, i: int, speed: float) -> void = "nav_crowd_speed"

View file

@ -24,8 +24,9 @@ NAV_SHIM void nav_crowd_free(void *p) {
delete c; delete c;
} }
// a walker at (x, y, z): its index, or -1 when the crowd is full or the point is off the mesh // a walker at (x, y, z): its index, or -1 when the crowd is full or the point is off the mesh;
NAV_SHIM int nav_crowd_add(void *p, float x, float y, float z, float radius, float height, float speed, int filter) { // separation is how hard it keeps its distance (a herd animal more than a loner)
NAV_SHIM int nav_crowd_add(void *p, float x, float y, float z, float radius, float height, float speed, int filter, float separation) {
Crowd *c = static_cast<Crowd *>(p); Crowd *c = static_cast<Crowd *>(p);
dtCrowdAgentParams ap; dtCrowdAgentParams ap;
memset(&ap, 0, sizeof(ap)); memset(&ap, 0, sizeof(ap));
@ -35,7 +36,7 @@ NAV_SHIM int nav_crowd_add(void *p, float x, float y, float z, float radius, flo
ap.maxAcceleration = speed * 4.0f; ap.maxAcceleration = speed * 4.0f;
ap.collisionQueryRange = radius * 12.0f; ap.collisionQueryRange = radius * 12.0f;
ap.pathOptimizationRange = radius * 30.0f; ap.pathOptimizationRange = radius * 30.0f;
ap.separationWeight = 2.0f; ap.separationWeight = separation;
ap.updateFlags = DT_CROWD_ANTICIPATE_TURNS | DT_CROWD_OBSTACLE_AVOIDANCE | DT_CROWD_SEPARATION | DT_CROWD_OPTIMIZE_TOPO | DT_CROWD_OPTIMIZE_VIS; ap.updateFlags = DT_CROWD_ANTICIPATE_TURNS | DT_CROWD_OBSTACLE_AVOIDANCE | DT_CROWD_SEPARATION | DT_CROWD_OPTIMIZE_TOPO | DT_CROWD_OPTIMIZE_VIS;
ap.obstacleAvoidanceType = 3; ap.obstacleAvoidanceType = 3;
ap.queryFilterType = (unsigned char)(filter >= 0 && filter < NAV_FILTERS ? filter : 0); ap.queryFilterType = (unsigned char)(filter >= 0 && filter < NAV_FILTERS ? filter : 0);

View file

@ -25,7 +25,7 @@ program CrowdTest {
x0 = 36.0 x0 = 36.0
x1 = 4.0 x1 = 4.0
} }
let id = nav_crowd_add(nav_st, NAV_PERSON, x0, 0.0, z, 0.35, 1.8, 1.4, 0) let id = nav_crowd_add(nav_st, NAV_PERSON, x0, 0.0, z, 0.35, 1.8, 1.4, 0, 2.0)
expect(id >= 0) expect(id >= 0)
expect(nav_crowd_target(nav_st, NAV_PERSON, id, x1, 0.0, z)) expect(nav_crowd_target(nav_st, NAV_PERSON, id, x1, 0.0, z))
push(ct.ids, id) push(ct.ids, id)
@ -73,12 +73,12 @@ program CrowdTest {
expect(not nav_crowd_start(nav_st, NAV_PERSON, 8, 0.6)) expect(not nav_crowd_start(nav_st, NAV_PERSON, 8, 0.6))
expect(nav_build(nav_st, NAV_PERSON, ground(40, 0), new NavConfig)) expect(nav_build(nav_st, NAV_PERSON, ground(40, 0), new NavConfig))
expect(nav_crowd_start(nav_st, NAV_PERSON, 8, 0.6)) expect(nav_crowd_start(nav_st, NAV_PERSON, 8, 0.6))
expect_eq(nav_crowd_add(nav_st, NAV_PERSON, -50.0, 0.0, -50.0, 0.35, 1.8, 1.4, 0), -1) expect_eq(nav_crowd_add(nav_st, NAV_PERSON, -50.0, 0.0, -50.0, 0.35, 1.8, 1.4, 0, 2.0), -1)
let a = nav_crowd_add(nav_st, NAV_PERSON, 5.0, 0.0, 5.0, 0.35, 1.8, 1.4, 0) let a = nav_crowd_add(nav_st, NAV_PERSON, 5.0, 0.0, 5.0, 0.35, 1.8, 1.4, 0, 2.0)
expect(a >= 0) expect(a >= 0)
expect(not nav_crowd_target(nav_st, NAV_PERSON, a, -50.0, 0.0, -50.0)) expect(not nav_crowd_target(nav_st, NAV_PERSON, a, -50.0, 0.0, -50.0))
nav_drop(nav_st, NAV_PERSON) nav_drop(nav_st, NAV_PERSON)
expect(not nav_crowd_read(nav_st, NAV_PERSON, a)) expect(not nav_crowd_read(nav_st, NAV_PERSON, a))
expect_eq(nav_crowd_add(nav_st, NAV_PERSON, 5.0, 0.0, 5.0, 0.35, 1.8, 1.4, 0), -1) expect_eq(nav_crowd_add(nav_st, NAV_PERSON, 5.0, 0.0, 5.0, 0.35, 1.8, 1.4, 0, 2.0), -1)
} }
} }

View file

@ -30,6 +30,7 @@ export property WildSpecies {
keep_away: float = 60.0 # how far its ranges keep from where the valley starts, m keep_away: float = 60.0 # how far its ranges keep from where the valley starts, m
range: float = 100.0 # how wide one of its ranges is, m range: float = 100.0 # how wide one of its ranges is, m
scale: float = 1.0 # its size, times scale: float = 1.0 # its size, times
spacing: float = 2.0 # how much room it keeps from others walking by (a herd more): a crowd's separation
} }
export state WildlifeState { export state WildlifeState {