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)
`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
`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

View file

@ -20,11 +20,12 @@ function nv_crowd_drop(nav_st: mut NavState, kind: int) -> void {
nav_st.nv_crowds[kind] = null
}
# a walker at (x, y, z) with its width, height, top speed and taste: 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) -> int {
# a walker at (x, y, z) with its width, height, top speed, taste and how hard it keeps its distance
# 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)
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 {
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_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_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_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"

View file

@ -24,8 +24,9 @@ NAV_SHIM void nav_crowd_free(void *p) {
delete c;
}
// 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) {
// a walker at (x, y, z): its index, or -1 when the crowd is full or the point is off the mesh;
// 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);
dtCrowdAgentParams 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.collisionQueryRange = radius * 12.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.obstacleAvoidanceType = 3;
ap.queryFilterType = (unsigned char)(filter >= 0 && filter < NAV_FILTERS ? filter : 0);

View file

@ -25,7 +25,7 @@ program CrowdTest {
x0 = 36.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(nav_crowd_target(nav_st, NAV_PERSON, id, x1, 0.0, z))
push(ct.ids, id)
@ -73,12 +73,12 @@ program CrowdTest {
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_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)
let a = nav_crowd_add(nav_st, NAV_PERSON, 5.0, 0.0, 5.0, 0.35, 1.8, 1.4, 0)
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, 2.0)
expect(a >= 0)
expect(not nav_crowd_target(nav_st, NAV_PERSON, a, -50.0, 0.0, -50.0))
nav_drop(nav_st, NAV_PERSON)
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)
}
}