The package keeps the rules and hands them over each step: speed and heading, whether a jump leaves, CHAR_STEP and the boots' slope limit. The walker (Jolt CharacterVirtual) does gravity, landing, steps, slopes and following the ground down. Too steep is ground too steep facing the way the body wanted to go; stepping off an edge is not. ludic.physics: phys_walker_move (follow the body if something else moved it, then step), the slope limit, the jump always honoured. The character's tests run on a real Jolt walker over the same fake world: 15 pass, a knee-high boulder now a step. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
104 lines
4.3 KiB
Text
104 lines
4.3 KiB
Text
# walker_test.ludic - a walker on the real Jolt: it stands, walks, steps onto a low stone, is held
|
|
# by a tall one, jumps and lands, shoves a crate aside; and a boat-shaped body turns under a torque
|
|
import "ludic.physics"
|
|
import "ludic.base"
|
|
program WalkerTest {
|
|
numbers float
|
|
|
|
function flat(physics_st: mut PhysicsState) -> void {
|
|
expect(phys_open(physics_st, 1024, 1))
|
|
let h = floats(64 * 64)
|
|
for i in 0 .. 64 * 64 { h[i] = 0.0 }
|
|
phys_ground_add(physics_st, phys_heightfield(physics_st, h, 64, -32.0, -32.0, 1.0))
|
|
}
|
|
|
|
function walk(physics_st: mut PhysicsState, id: int, seconds: float, vx: float, vz: float) -> void {
|
|
for i in 0 .. int(seconds * 60.0) { phys_walker_step(physics_st, id, 1.0 / 60.0, vx, vz, 0.0) }
|
|
}
|
|
|
|
test "a walker falls to the ground, stands, and walks at the speed it asks" (physics_st: mut PhysicsState) {
|
|
flat(physics_st)
|
|
let me = phys_walker_add(physics_st, 0.35, 1.8, 0.0, 1.0, 0.0, 50.0, 70.0)
|
|
walk(physics_st, me, 1.0, 0.0, 0.0)
|
|
let p = phys_walker_pose(physics_st, me)
|
|
expect_eq(p.ground, PHYS_ON_GROUND)
|
|
expect(Math.abs(p.y) < 0.05)
|
|
walk(physics_st, me, 2.0, 3.0, 0.0)
|
|
let q = phys_walker_pose(physics_st, me)
|
|
expect(Math.abs(q.x - 6.0) < 0.2)
|
|
expect(q.body >= 0)
|
|
phys_close(physics_st)
|
|
}
|
|
|
|
test "a low stone is a step, a tall one is a wall" (physics_st: mut PhysicsState) {
|
|
flat(physics_st)
|
|
let stone = phys_offset(physics_st, phys_box(physics_st, 1.0, 0.15, 3.0), 0.0, 0.15, 0.0, 0.0)
|
|
phys_static_add(physics_st, stone, 3.0, 0.0, 0.0, 0.0)
|
|
let wall = phys_offset(physics_st, phys_box(physics_st, 1.0, 1.5, 3.0), 0.0, 1.5, 0.0, 0.0)
|
|
phys_static_add(physics_st, wall, 3.0, 0.0, 10.0, 0.0)
|
|
phys_settle(physics_st)
|
|
let a = phys_walker_add(physics_st, 0.35, 1.8, 0.0, 0.0, 0.0, 50.0, 70.0)
|
|
phys_walker_limits(physics_st, a, 0.55, 0.5, 50.0)
|
|
walk(physics_st, a, 1.5, 2.0, 0.0)
|
|
let pa = phys_walker_pose(physics_st, a)
|
|
expect(pa.x > 2.5)
|
|
expect(Math.abs(pa.y - 0.3) < 0.05)
|
|
let b = phys_walker_add(physics_st, 0.35, 1.8, 0.0, 0.0, 10.0, 50.0, 70.0)
|
|
walk(physics_st, b, 2.0, 2.0, 0.0)
|
|
let pb = phys_walker_pose(physics_st, b)
|
|
expect(pb.x < 1.7)
|
|
expect(Math.abs(pb.y) < 0.05)
|
|
phys_close(physics_st)
|
|
}
|
|
|
|
test "a jump leaves the ground and comes back down" (physics_st: mut PhysicsState) {
|
|
flat(physics_st)
|
|
let me = phys_walker_add(physics_st, 0.35, 1.8, 0.0, 0.0, 0.0, 50.0, 70.0)
|
|
walk(physics_st, me, 0.5, 0.0, 0.0)
|
|
phys_walker_step(physics_st, me, 1.0 / 60.0, 0.0, 0.0, 5.0)
|
|
walk(physics_st, me, 0.25, 0.0, 0.0)
|
|
let up = phys_walker_pose(physics_st, me)
|
|
expect_eq(up.ground, PHYS_IN_AIR)
|
|
expect(up.y > 0.5)
|
|
walk(physics_st, me, 1.5, 0.0, 0.0)
|
|
let down = phys_walker_pose(physics_st, me)
|
|
expect_eq(down.ground, PHYS_ON_GROUND)
|
|
expect(Math.abs(down.y) < 0.05)
|
|
phys_close(physics_st)
|
|
}
|
|
|
|
test "walking into a crate shoves it, and a placed walker stands where it was put" (physics_st: mut PhysicsState) {
|
|
flat(physics_st)
|
|
let crate = phys_body_add(physics_st, phys_box(physics_st, 0.25, 0.25, 0.25), 2.0, 0.25, 0.0, 0.0, 4.0)
|
|
let me = phys_walker_add(physics_st, 0.35, 1.8, 0.0, 0.0, 0.0, 50.0, 70.0)
|
|
for i in 0 .. 150 {
|
|
phys_walker_step(physics_st, me, 1.0 / 60.0, 2.0, 0.0, 0.0)
|
|
phys_step(physics_st)
|
|
}
|
|
expect(phys_pose(physics_st, crate).x > 2.5)
|
|
phys_walker_place(physics_st, me, -10.0, 0.0, -10.0)
|
|
let p = phys_walker_pose(physics_st, me)
|
|
expect(Math.abs(p.x + 10.0) < 0.01)
|
|
phys_walker_remove(physics_st, me)
|
|
expect(phys_walker_pose(physics_st, me) == null)
|
|
phys_close(physics_st)
|
|
}
|
|
|
|
test "a torque turns a body and a force carries it" (physics_st: mut PhysicsState) {
|
|
expect(phys_open(physics_st, 64, 1))
|
|
phys_gravity(physics_st, 0.0)
|
|
let hull = phys_body_add(physics_st, phys_box(physics_st, 0.6, 0.2, 1.8), 0.0, 0.0, 0.0, 0.0, 120.0)
|
|
for i in 0 .. 60 {
|
|
phys_torque(physics_st, hull, 0.0, 200.0, 0.0)
|
|
phys_force(physics_st, hull, 0.0, 0.0, 240.0)
|
|
phys_step(physics_st)
|
|
}
|
|
expect(phys_spin_y(physics_st, hull) > 0.1)
|
|
expect(phys_pose(physics_st, hull).z > 0.5)
|
|
let p = phys_pose(physics_st, hull)
|
|
p.x = 5.0
|
|
phys_set_pose(physics_st, hull, p)
|
|
expect(Math.abs(phys_pose(physics_st, hull).x - 5.0) < 0.01)
|
|
phys_close(physics_st)
|
|
}
|
|
}
|