Skip to content

[WONTFIX?] Blend the player upper body by speed like classic NEOTOKYO - #2240

Draft
sunzenshen wants to merge 2 commits into
NeotokyoRebuild:masterfrom
sunzenshen:anim-upper-body-blend
Draft

sunzenshen wants to merge 2 commits into
NeotokyoRebuild:masterfrom
sunzenshen:anim-upper-body-blend

Conversation

@sunzenshen

@sunzenshen sunzenshen commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Description

Case 2: tiny movements swap the torso to the walk pose.
Review page: exported review page (Dropbox) · live review page (opening it lets you request access with your email or Claude account).

A player who moves slowly (drifting, peeking, easing to a stop) bobs their torso at the full walk rate in neo, where classic NEOTOKYO keeps the idle breathing torso and only brings the walk in as speed rises. This PR blends the upper body by speed the way classic does.

In neo the upper-body aim layer plays the sequence of the current main activity at full weight, so from the first 0.5 u/s of movement the torso switches to Walk_Upper_<weapon> (a 0.73 s loop) instead of Idle_Upper_<weapon> (2 s), with a 0.2 s crossfade at every start and stop. Classic NEOTOKYO's player anim state is the 2006 SDK template (CBasePlayerAnimState with CSDKPlayerAnimState's top speeds): it always plays the idle upper body and blends the walk, sprint or crouch walk upper body over it at a weight of 2D speed / top speed, with top speeds of 100 u/s walking, 250 sprinting and 85 crouched (ANIM_TOPSPEED_* in sdk_playeranimstate.cpp, and the same three constants in classic's client.dll). A walker at 30 u/s shows 70 % idle torso; a sprinter at about 205 u/s keeps 18 % idle.

The change, in CMultiPlayerAnimState (shared, NEO block only):

  • UpdateAimSequenceLayers now honors its bForceIdle argument, so the existing aim pair (GESTURE_SLOT_AIM/AIM2) always plays the idle or crouch idle upper body, as its one caller already asks for.
  • A second pair, GESTURE_SLOT_AIM_MOVE/AIM_MOVE2, with its own sequence transitioner, plays the walk, run or crouch walk upper body at min(speed2D / top speed, 1) while walking, sprinting or crouch walking; the idle pair takes the rest of the weight.
  • A player who stops in one frame (hitting a wall, releasing walk) fades the moving torso out over 0.2 s instead of snapping to idle. Classic snaps here; the old crossfade hid it in neo, so this keeps that smoothness. A normal friction stop is slower than the fade and follows the speed blend.

Leg animation, the main activity choice and the idle torso of a standing player are unchanged. Players are animated client-side (layers are not networked); the server runs the same shared code for its hitbox layers, so both stay in step.

How to test

  1. thirdperson, then tap a movement key briefly or walk slowly: the torso keeps the slow idle motion and the walk bob grows with speed, instead of switching to the full walk bob at once.
  2. Walk, run, sprint and crouch walk at full speed: same upper body as before, except sprinting keeps a little idle torso (as in classic).
  3. Stop dead against a wall: the torso settles over a fraction of a second, no snap.

Results

Logged per frame with a temporary client-side logger of the player's anim layers (not part of this PR), on the same scripted session in both games: nt_rise_ctg, Assault with ZR68C, listen server, thirdperson in neo; stand, walk, peek, run, short taps, sprint, crouch walk. Classic NEOTOKYO logged with cl_showanimstate (AnimStateClient.log). neo: Release client builds of master (f426c22) and this PR. "walk torso" = share of upper-body weight on the walk/run/crouch walk sequences; "off model" = mean distance of that share from the 2006 SDK blend min(speed2D / top speed, 1).

2D speed, u/s classic walk torso master this PR off model: classic / master / this PR
0.5-10 0.06 1.00 0.03 0.00 / 0.93 / 0.02
10-50 0.30 0.65 0.34 0.00 / 0.52 / 0.00
50-90 0.69 0.77 0.73 0.00 / 0.30 / 0.00
90-110 (walk 96) 0.96 1.00 0.97 0.00 / 0.04 / 0.00
110-140 (run 128) 1.00 0.98 1.00 0.00 / 0.02 / 0.00
over 140 (sprint 205) 0.82 0.99 0.82 0.00 / 0.17 / 0.00

Bin means depend on how the frames spread inside each bin, so the "off model" column is the per-frame comparison: classic follows the SDK blend exactly, this PR follows it within 0.02 (the 0.5-10 bin holds the fade-out frames after abrupt stops), master does not. Frames at 0.5-50 u/s with the torso at least 0.9 walk: classic 0 of 126, master 35 of 52, this PR 0 of 182. Largest one-frame change of the walk torso share while slowing down: classic 0.96 (abrupt stops), master 0.08, this PR 0.22 (at about 30 fps).

Risk

Two more animation overlays per player on client and server (11 of the engine's 15). Gesture slot indices after the aim slots shift by two; nothing addresses them by number.

How this was compared

The code came first: NT;RE's player animation code was read against the 2006 Source base class that classic NEOTOKYO is built on (still in this tree as shared/base_playeranimstate.cpp); classic's client.dll was read only for class names, console settings and constants. Then both games logged every frame (classic's built-in cl_showanimstate_log, an equivalent harness log in NT;RE) on the same map and spot, with the same class, weapon and scripted keys, and the same scripts read both logs. The clips only illustrate; the numbers above come from the logs.

neo plays the walk upper body at full weight from the first 0.5 u/s of
movement, so a player who drifts, peeks or eases to a stop bobs the
torso at the 0.73 s walk loop instead of the 2 s idle breathing, with a
0.2 s crossfade at each start and stop.

Classic NEOTOKYO, like the 2006 SDK player anim state it is built on,
always plays the idle upper body and blends the walk, sprint or crouch
walk upper body over it at 2D speed / 100, 250 or 85 u/s. Do the same
with a second pair of aim layers and its own sequence transitioner.
A player who stops in one frame (a wall, or releasing walk) dropped the
walk torso from near full weight to zero at once, a visible snap that
the old 0.2 s crossfade between the torso sequences used to hide.
Classic NT snaps here too; fade the last moving pose out over the same
0.2 s instead. A normal friction stop is slower than the fade, so the
speed blend is unchanged there.
@sunzenshen sunzenshen added the Animations Animation stuff. Including the syncronizing of animations between client and server based on state. label Oct 15, 2026
@sunzenshen

sunzenshen commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor Author

Demos (click gifs to animate):

r1-taps-triptych r1-taps

@sunzenshen
sunzenshen requested a review from a team October 15, 2026 05:26
@sunzenshen
sunzenshen marked this pull request as ready for review October 15, 2026 05:26
@sunzenshen
sunzenshen removed the request for review from a team October 15, 2026 07:01
@sunzenshen
sunzenshen marked this pull request as draft October 15, 2026 07:01
@sunzenshen

Copy link
Copy Markdown
Contributor Author

#2244 might actually accomplish the same and better than this, so waiting of the result of that review before deciding what to do with this PR.

(This PR edits multiplayer_animstate.*, which #2244 no longer uses and could even remove from the build.)

@sunzenshen sunzenshen added the wontfix This will not be worked on label Oct 16, 2026
@sunzenshen sunzenshen changed the title Blend the player upper body by speed like classic NEOTOKYO [WONTFIX?] Blend the player upper body by speed like classic NEOTOKYO Oct 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Animations Animation stuff. Including the syncronizing of animations between client and server based on state. wontfix This will not be worked on

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant