Entropie git · main
Entropie festival game; first steps
C 88.5% Markdown 9.1% GLSL 2.3%git clone https://git.christianimmanuel.de/games/Entropie.gitwget https://git.christianimmanuel.de/games/Entropie/archive/Entropie.tar.gzentropie 1.54.0: no more elbow jumps folding the arms on an edge
- Limbs between folded and stretched blend the two fits (no jump at the switch): elbows on an edge popped up to 31 cm, now none - An elbow swung up over an edge stays solved that way, folded or not - Just taken hold of an edge: the elbow eases back in, the hand stays Includes 1.53.0 (if not committed separately): - Climbing: legs stay out of what you climb (the 0.8 m walk-in climb no longer puts the thigh into it); feet keep room for the knee at walls - Raised ladder aimed too low: leans as usual instead of refusing
entropie/Makefile | 2 +- entropie/STATE.md | 9 +++---- entropie/commit_message.txt | 18 +++++++------ entropie/src/game/game_actions.c | 34 +++++++++++++++++++++++-- entropie/src/people/body.c | 12 +++++++++ entropie/src/people/body_pose.c | 55 +++++++++++++++++++++++++--------------- entropie/src/world/ladder.c | 9 +++---- 7 files changed, 97 insertions(+), 42 deletions(-) diff --git a/entropie/Makefile b/entropie/Makefile index d1ed684..9b30905 100644 --- a/entropie/Makefile +++ b/entropie/Makefile @@ -1,5 +1,5 @@ APP := entropie -VERSION := 1.52.0 +VERSION := 1.54.0 PREFIX ?= /usr BINDIR ?= $(PREFIX)/bin diff --git a/entropie/STATE.md b/entropie/STATE.md index 304df12..671d1b0 100644 --- a/entropie/STATE.md +++ b/entropie/STATE.md @@ -70,14 +70,14 @@ Talk topics: offer, ask, suggest, where (new woodpile spot), ask for a hand, ask - **Moving**: walk, jog, sprint (a wave up and down with each stride), jumps off one foot, steps up and down, slopes, carrying, pushing, pulling, lifting together - **One end up**: a log (1.4 m and longer) too heavy for one person but not for half its weight: held within 0.8 m of an end, that end comes up to the hip (hands slide to it), the other drags on the ground and pivots there; you go at 75 %; a second person takes all of it (`hands.c`, endLift). Whoever can carry the whole weight lifts it all - **Arms out of things**: standing, an arm that would be in something solid (fixed or 40 kg and more; not ladders) moves back and out (eases at 2 m/s) and is fitted again; a hand holding an edge stays, the elbow swings back over the edge -- **Speed limit**: no joint moves faster than it can (hands 14, feet 12, knees and elbows 9, trunk 5 m/s); bones keep their length; a limb folded right up (foot by the hip, cross-legged) keeps its bend instead of being fitted again (that flipped the knee) +- **Speed limit**: no joint moves faster than it can (hands 14, feet 12, knees and elbows 9, trunk 5 m/s); bones keep their length; a limb folded right up (foot by the hip, cross-legged) keeps its bend instead of being fitted again (that flipped the knee); between folded and stretched (30-42 % of its reach) the two blend, so an arm folding up on an edge no longer jumps (elbows popped 31 cm); an elbow swung up over an edge stays solved that way - **Any frame rate**: springs are solved exactly per frame, people pushing each other ease apart gently, helpers stay put once there (or blocked close by): no flicker at low, high or uneven frame rates - **Approaching obstacles**: the sporty fit their steps to a good take-off spot; sprinting fit and fresh, they leap low narrow things; running into something: trip or headlong over it if low, stagger back or fall to the side if high (speed, fitness, energy, clumsiness) - **Over obstacles**: lean into it, the leg swings out a little and back in; lift the foot first, then over; the body waits for the back leg; knee turns out, or the foot comes up; sprinting, leaps; careless people just step on small things; trips and scrapes by speed, height, condition - **Bushes**: trying to get over one ends in it, twigs snap - **Half way over something**: from when a foot is well into a step over something (lifted 12 cm+, 15 % into the step) until the body is over there (6 cm behind where that foot came down, at most 20 cm past it), you're crossing (`body.c` crossOver): stopping (or slower than 0.3 m/s) the body carries on over it at 0.6 m/s (`physics.c` carryOver); the foot over there stays down, the other comes after. Walking back takes you back. Standing still half over costs energy (2.5 %/s, a leg held up 6 %/s; less sporty, more); worn out, held longer than 1-6 s (fitness, energy) or legs over 0.95 m apart: you're made to go over; still stuck 1.5 s later: you fall (`world.c` halfOver) - **Sure-footedness**: one factor (`world.c`, unsure: ~0.2 sporty and careful .. ~1 average .. ~2.7 clumsy and unfit) scales every trip, slip, scrape and run-into, and how bad it gets; clumsy and unfit, you also stumble over your own feet jogging or running -- **Climbing**: hand on it (up to 1.35 m), hang and pull up (higher, both hands free), jump for the edge; ladder up and down, over the edge at the top. Walking up to something 0.7–2 m high, the hands go onto its edge first; pulling up, the feet push against the face below the edge, then the right knee and foot come over onto it, then the left (scripted in `game_actions.c`, climbFeet); knees bend up. High pull-ups: pulled up till the arms press down (hips near the edge), then the knee comes up and you rise; the hands go on ahead onto the top and stay there, the back bends over the edge as far as it takes to keep them on it (body_pose.c, reach_bend). Jumping: holding Space (or W) grabs an edge the hands reach (about 2.4 m); up to 0.35 m higher, the fingertips touch it ("Just out of reach"); hands go up wide, then onto the edge +- **Climbing**: hand on it (up to 1.35 m), hang and pull up (higher, both hands free), jump for the edge; ladder up and down, over the edge at the top. Walking up to something 0.7–2 m high, the hands go onto its edge first; pulling up, the feet push against the face below the edge, then the right knee and foot come over onto it, then the left (scripted in `game_actions.c`, climbFeet); knees bend up. A foot below the edge goes back until its (forward bent) knee is 8 cm clear of the face (`game_actions.c`, kneeClear); onto something low (under 1 m) the body rises quickly first, so the knee comes up above the edge. Walking up to anything higher than 0.5 m, the feet stay 0.35 m back from it (room for the knee; `body.c`, footSpot). Climb test: 0.8-1.5 m, walking or jumping, legs at most 5 cm into it (was 19 cm at 0.8 m). High pull-ups: pulled up till the arms press down (hips near the edge), then the knee comes up and you rise; the hands go on ahead onto the top and stay there, the back bends over the edge as far as it takes to keep them on it (body_pose.c, reach_bend). Jumping: holding Space (or W) grabs an edge the hands reach (about 2.4 m); up to 0.35 m higher, the fingertips touch it ("Just out of reach"); hands go up wide, then onto the edge - **Drink and the rest**: sips, burps, sways, veers, sick, passes out, sleeps it off; pees (stream lands on the real ground, soaks in or puddles) - **Throwing**: underhand lob, overhand when wound up long enough - **Carrying one-handed**: hangs at the side by its top (a bag by its neck), wide things with the arm a little out; only long things (ladder, logs, axe) stop you at walls, bags and bottles squeeze by @@ -124,8 +124,7 @@ A cell grid of the site (2 m cells, `src/world/grid.c`, built once a frame) answ Most important first. **Bugs** -- Climbing onto low things (about 0.8 m) by walking into them: the leading thigh touches the face for a few frames (up to 19 cm); 1 m and higher is clean (see climbing rework) -- Arms brushing past things while walking ease out of them at 2 m/s: a few more small joint jumps in the audit (walk 116 -> 175) +- Arms brushing past things while walking ease out of them at 2 m/s: a few more small joint jumps in the audit (walk 116 -> 161, the worst 21 cm; walking into a wall and climbing it is part of that run) **Body and moving** - Obstacle approach, still to do: prefer a step in between on top of something stable (or unstable, if clumsy) over a long stride; too high to step over: pull up or jump and grab on their own (now only when pushing against it) @@ -152,4 +151,4 @@ Most important first. - The walking map rebuild takes ~9 ms when fixed things change (a felled tree) --- -State of `entropie 1.52.0 (build 618b0b4)` +State of `entropie 1.54.0 (build 5b985f0)` diff --git a/entropie/commit_message.txt b/entropie/commit_message.txt index bcad6bd..61700eb 100644 --- a/entropie/commit_message.txt +++ b/entropie/commit_message.txt @@ -1,9 +1,11 @@ -entropie 1.52.0: no stopping half over things, ladder held up and aimed +entropie 1.54.0: no more elbow jumps folding the arms on an edge -- Stopping half way over something: the body carries on over it (the foot - over there stays, the other comes after); walking back takes you back -- Holding still half over costs energy (a leg held up more); worn out, too - long or legs too far apart you're made to go over, else you fall -- A slid-out ladder is held near its foot, reaching up in front (steeper - looking up); a click leans it where you look: on the edge when aimed at - the top, against the face lower down; looking down slides it together +- Limbs between folded and stretched blend the two fits (no jump at the + switch): elbows on an edge popped up to 31 cm, now none +- An elbow swung up over an edge stays solved that way, folded or not +- Just taken hold of an edge: the elbow eases back in, the hand stays + +Includes 1.53.0 (if not committed separately): +- Climbing: legs stay out of what you climb (the 0.8 m walk-in climb no + longer puts the thigh into it); feet keep room for the knee at walls +- Raised ladder aimed too low: leans as usual instead of refusing diff --git a/entropie/src/game/game_actions.c b/entropie/src/game/game_actions.c index 6ad9bca..d29585d 100644 --- a/entropie/src/game/game_actions.c +++ b/entropie/src/game/game_actions.c @@ -115,6 +115,30 @@ static bool startClimb(Game *game, float top, Vec3 probe) { /* The feet while pulling up: against the face of it below the edge (pushing up), then one knee comes up and that foot goes over onto the top, then the other. Never through what you climb. */ +#define KNEE_LEG 0.45f /* thigh and shin (scaled) ... */ +#define KNEE_HIP 0.86f /* ... the hips this high above the feet ... */ +#define KNEE_GAP 0.08f /* ... and the knee kept this clear of the face */ +#define LOW_RISE 0.65f /* ... the body up on it by then (of the climb) ... */ +#define LOW_EASE 0.0f /* ... (0: rising fastest at the start) */ +#define LOW_BACK 0.25f /* climbing onto something low: the foot that pushes off stays this far back from its face */ + +/* A foot below the edge: where the knee would be (bent forward, in the plane + of the climb). Too close to the face under the edge: the foot goes back + until the knee is clear of it. */ +static float kneeClear(float along, float y, float root_along, float root_y, float face, float top, float s) { + const float leg = KNEE_LEG * s, hip_a = root_along + 0.04f * s, hip_y = root_y + KNEE_HIP * s; + for (int k = 0; k < 14; k++, along -= 0.03f * s) { + float dx = along - hip_a, dy = y - hip_y, d = sqrtf(dx * dx + dy * dy); + if (d < 1e-3f) continue; + float h = d < 2.0f * leg ? sqrtf(leg * leg - 0.25f * d * d) : 0.0f; + float px = dy / d, py = -dx / d; /* (perpendicular, the forward one) */ + if (px < 0.0f) { px = -px; py = -py; } + float ka = hip_a + 0.5f * dx + px * h, ky = hip_y + 0.5f * dy + py * h; + if (ka < face - KNEE_GAP * s || ky > top + 0.04f * s) break; + } + return along; +} + static void climbFeet(Game *game, Object *p, float t, bool high, float s) { const Vec3 a = game->climb_from, b = game->climb_to; Vec3 f = vec3(b.x - a.x, 0.0f, b.z - a.z); @@ -129,7 +153,7 @@ static void climbFeet(Game *game, Object *p, float t, bool high, float s) { float u = high ? (lead ? smoothstepf(0.7f, 0.86f, t) : smoothstepf(0.84f, 0.98f, t)) /* (once the hips are up near the edge) */ : (lead ? smoothstepf(0.12f, 0.4f, t) : smoothstepf(0.5f, 0.85f, t)); /* below: under you, but in front of the face, no higher than just under the edge */ - float along_below = fminf(root_along - 0.06f * s, face - 0.25f * s); /* (the knees bend forward: room for them) */ + float along_below = fminf(root_along - 0.06f * s, face - (!high && top - ground < 1.0f * s && !lead ? LOW_BACK : 0.25f) * s); /* (the knees bend forward: room for them; low, the push-off foot further back) */ float y_below = clampf(p->position.y + 0.08f * s, ground + 0.08f * s, top - 0.12f * s); /* on top: a little over the edge, or under you once you're up there */ float along_top = fmaxf(face + 0.2f * s, root_along); @@ -143,6 +167,7 @@ static void climbFeet(Game *game, Object *p, float t, bool high, float s) { float along = lerpf(along_below, along_top, across); float yy = lerpf(y_below, y_top, up) + 0.12f * s * sinf(PI_F * u); if (along > face - 0.05f * s) yy = fmaxf(yy, y_top); /* (over it: on it, not in it) */ + else along = kneeClear(along, yy, root_along, p->position.y, face, top, s); Vec3 ankle = vec3_add(a, vec3_add(vec3_scale(f, along), vec3_scale(side, sign * 0.11f * s))); ankle.y = yy; /* toes: at the wall pointing into it (on the toes), on top flat ahead */ @@ -203,7 +228,12 @@ void game_climb(Game *game, const Input *in, float dt) { over = smoothstepf(0.62f, 1.0f, t); knees = 0.7f * smoothstepf(0.6f, 0.8f, t); /* a knee comes up onto the edge */ } else { - y = lerpf(a.y, b.y + 0.02f, smoothstepf(0.0f, 0.7f, t)); + if (b.y - a.y < 1.0f * s) { /* low: up quickly first (the hips over the knee coming up, not the knee into the face) */ + float k = clampf(t / LOW_RISE, 0.0f, 1.0f); + y = lerpf(a.y, b.y + 0.02f, 1.0f - (1.0f - k) * (1.0f - k) * (1.0f - LOW_EASE * k)); + } else { + y = lerpf(a.y, b.y + 0.02f, smoothstepf(0.0f, 0.7f, t)); + } over = smoothstepf(0.45f, 1.0f, t); } p->position = vec3(lerpf(a.x, b.x, over), y, lerpf(a.z, b.z, over)); diff --git a/entropie/src/people/body.c b/entropie/src/people/body.c index a6509b2..3a75568 100644 --- a/entropie/src/people/body.c +++ b/entropie/src/people/body.c @@ -10,6 +10,8 @@ static const float FIDGET_LENGTH[FIDGET_COUNT] = { 0.0f, 1.6f, 2.4f, 2.6f, 2.0f, static Vec3 clearSpot(const BodyFrame *f, Foot *foot, Vec3 spot, Vec3 other, float reach, bool sloppy, float s); static Vec3 leapPast(const BodyFrame *f, Foot *foot, Vec3 aim, Vec3 other, float reach, float s); static void crossOver(Body *b, const BodyFrame *f, float s); +#define KNEE_ROOM 0.35f /* a foot this far back from anything higher than ... */ +#define KNEE_HIGH 0.5f /* ... this (scaled): room for the knee */ #define CROSS_OVER 0.12f /* a foot lifted this much over something ... */ #define CROSS_FROM 0.15f /* ... and this far into its step: half way over it */ #define CROSS_PAST 0.2f /* the body goes no further than this past where that foot came down */ @@ -74,6 +76,16 @@ static Vec3 footSpot(const Body *b, const BodyFrame *f, int side, float s) { } spot.y = groundUnder(f, spot.x, spot.z, s); + /* Something higher than a knee just ahead (a wall, a ledge): the foot stays back + far enough for the knee (it bends forward) */ + for (float d = 0.04f * s; d <= KNEE_ROOM * s; d += 0.04f * s) { + Vec3 q = vec3_add(spot, vec3_scale(f->facing, d)); + if (f->ground(f->ground_ctx, q.x, q.z, spot.y + 1.2f * s) - spot.y > KNEE_HIGH * s) { + spot = vec3_sub(spot, vec3_scale(f->facing, KNEE_ROOM * s - d + 0.04f * s)); + spot.y = groundUnder(f, spot.x, spot.z, s); + break; + } + } return spot; } diff --git a/entropie/src/people/body_pose.c b/entropie/src/people/body_pose.c index 704b8d2..0b28cbe 100644 --- a/entropie/src/people/body_pose.c +++ b/entropie/src/people/body_pose.c @@ -15,7 +15,8 @@ static Vec3 nearestOnSegment(Vec3 a, Vec3 b, Vec3 p); * lengths, end lies as close to target as possible and mid bends * towards the pole direction. */ -#define FOLDED 0.3f /* a limb shorter than this much of its reach is folded right up */ +#define FOLDED 0.3f /* a limb shorter than this much of its reach is folded right up ... */ +#define FOLD_BAND 0.12f /* ... and this much more: on its way there (blended) */ static void twoBone(Vec3 root, Vec3 target, float l1, float l2, Vec3 pole, Vec3 *mid, Vec3 *end) { Vec3 to = vec3_sub(target, root); @@ -45,6 +46,30 @@ static Vec3 keepLengths(Vec3 *j, int root, float l1, float l2) { return j[root + 2]; } +/* Puts a limb's bones back to their lengths, its end where it is: stretched + out it's solved again (the joint the way the pole says); folded right up + (end almost at the root: which way the joint points is anybody's guess, + solving would flip it) it keeps its bend. In between the two blend, so + going from one to the other never jumps. Returns where the end is. */ +static Vec3 refitLimb(Vec3 *j, int r, float l1, float l2, Vec3 pole) { + const Vec3 root = j[r], end_was = j[r + 2]; + float w = smoothstepf(FOLDED, FOLDED + FOLD_BAND, vec3_length(vec3_sub(end_was, root)) / (l1 + l2)); + Vec3 kept_end = keepLengths(j, r, l1, l2), kept_mid = j[r + 1]; + if (w <= 0.0f) return kept_end; + Vec3 mid, end; + twoBone(root, end_was, l1, l2, pole, &mid, &end); + if (w >= 1.0f) { + j[r + 1] = mid; + j[r + 2] = end; + return end; + } + Vec3 m = vec3_sub(vec3_lerp(kept_mid, mid, w), root); + j[r + 1] = vec3_length(m) > 1e-5f ? vec3_add(root, vec3_scale(m, l1 / vec3_length(m))) : mid; + Vec3 e = vec3_sub(vec3_lerp(kept_end, end, w), j[r + 1]); + j[r + 2] = vec3_length(e) > 1e-5f ? vec3_add(j[r + 1], vec3_scale(e, l2 / vec3_length(e))) : end; + return j[r + 2]; +} + /* Shoulder -> elbow -> wrist -> hand reaching for a palm position. */ static void reachArm(Vec3 *j, int shoulder, Vec3 palm, Vec3 pole) { Vec3 reach = vec3_normalize(vec3_sub(palm, j[shoulder])); @@ -1147,14 +1172,7 @@ static void limitSpeeds(Body *b, float s, float dt, Skeleton *out) { /* (folded right up, the end almost at the root: which way the knee points is anybody's guess, fitting again would flip it round) */ Vec3 tail = vec3_sub(j[r + 3], j[r + 2]); - Vec3 mid, end = j[r + 2]; - if (vec3_length(vec3_sub(j[r + 2], j[r])) >= FOLDED * (l1 + l2)) { - twoBone(j[r], j[r + 2], l1, l2, vec3_sub(j[r + 1], j[r]), &mid, &end); - j[r + 1] = mid; - j[r + 2] = end; - } else { - end = keepLengths(j, r, l1, l2); - } + Vec3 end = refitLimb(j, r, l1, l2, vec3_sub(j[r + 1], j[r])); float tl = vec3_length(tail); if (tl > 1e-4f) j[r + 3] = vec3_add(end, vec3_scale(tail, l3 / tl)); } @@ -1248,14 +1266,7 @@ static void keepBodyOut(Body *b, Vec3 position, Vec3 facing, float s, float dt, bool leg = k < 2; float l1 = (leg ? THIGH : UPPER_ARM) * s, l2 = (leg ? SHIN : FOREARM) * s, l3 = (leg ? footSpan() : HAND) * s; Vec3 tail = vec3_sub(j[r + 3], j[r + 2]); - Vec3 mid, end = j[r + 2]; - if (vec3_length(vec3_sub(j[r + 2], j[r])) >= FOLDED * (l1 + l2)) { - twoBone(j[r], j[r + 2], l1, l2, vec3_sub(j[r + 1], j[r]), &mid, &end); - j[r + 1] = mid; - j[r + 2] = end; - } else { /* (folded up, cross-legged: as it is, bones their length) */ - end = keepLengths(j, r, l1, l2); - } + Vec3 end = refitLimb(j, r, l1, l2, vec3_sub(j[r + 1], j[r])); /* (folded up, cross-legged: as it is) */ float tl = vec3_length(tail); if (tl > 1e-4f) j[r + 3] = vec3_add(end, vec3_scale(tail, l3 / tl)); } @@ -1304,16 +1315,18 @@ static void keepArmsOut(Body *b, Vec3 position, Vec3 facing, float s, float dt, b->arm_out[side] = dl > step ? vec3_add(b->arm_out[side], vec3_scale(d, step / dl)) : goal; if (vec3_length(b->arm_out[side]) < 1e-4f && b->elbow_up[side] < 1e-3f) continue; if (!holding) for (int k = 1; k <= 3; k++) j[sh + k] = vec3_add(j[sh + k], b->arm_out[side]); + else j[sh + 1] = vec3_add(j[sh + 1], b->arm_out[side]); /* (just taken hold: the hand stays, the elbow eases back in - no jump) */ float l1 = UPPER_ARM * s, l2 = FOREARM * s; Vec3 tail = vec3_sub(j[sh + 3], j[sh + 2]); - Vec3 mid, end = j[sh + 2]; - if (vec3_length(vec3_sub(j[sh + 2], j[sh])) >= FOLDED * (l1 + l2)) { - Vec3 pole = vec3_lerp(vec3_normalize(vec3_sub(j[sh + 1], j[sh])), vec3_normalize(vec3(sign * 0.45f, 0.6f, -0.65f)), b->elbow_up[side]); + Vec3 pole = vec3_lerp(vec3_normalize(vec3_sub(j[sh + 1], j[sh])), vec3_normalize(vec3(sign * 0.45f, 0.6f, -0.65f)), b->elbow_up[side]); + Vec3 end; + if (b->elbow_up[side] > 0.5f) { /* (elbow up and back: its own way, folded or not) */ + Vec3 mid; twoBone(j[sh], j[sh + 2], l1, l2, pole, &mid, &end); j[sh + 1] = mid; j[sh + 2] = end; } else { - end = keepLengths(j, sh, l1, l2); + end = refitLimb(j, sh, l1, l2, pole); } float tl = vec3_length(tail); if (tl > 1e-4f) j[sh + 3] = vec3_add(end, vec3_scale(tail, HAND * s / tl)); diff --git a/entropie/src/world/ladder.c b/entropie/src/world/ladder.c index 95eea47..55e1a85 100644 --- a/entropie/src/world/ladder.c +++ b/entropie/src/world/ladder.c @@ -254,11 +254,10 @@ int Ladder_lean(World *world, Object *who, Object *l, bool fit, const char **why reaches = false; against = fmaxf(aim_y - ground, MIN_HEIGHT); lean = asinf(clampf((against + 0.1f) / len, 0.0f, 1.0f)); - if (lean < FLATTEST * PI_F / 180.0f) { - static char said[96]; - snprintf(said, sizeof said, "Too long to lean that low - aim at least %.1f m up", len * sinf(FLATTEST * PI_F / 180.0f) - 0.1f); - *why = said; - return LEAN_NO; + if (lean < FLATTEST * PI_F / 180.0f) { /* (that low it can't go: as usual then, on the edge or as high as it reaches) */ + lean = angle; + reaches = h <= len * sinf(angle) - 0.15f; + against = len * sinf(angle) - 0.1f; } } Vec3 rest = reaches && eaves ? vec3_add(face, vec3_scale(n, EAVES_OUT)) : face;