feat: real gunshots, localized 3D audio, seamless music, better animations
Audio: - All ballistic weapons now fire real firearm recordings (The Free Firearm Sound Library, CC0): AK-47 uses actual AK-47 shots, M4=AR-15, MP7=PPSh, AWP=Mosin Nagant, DMR=Savage 10, shotgun=Mossberg/Nova, nailgun=Ruger .22, rocket/mortar layered with real booms. Plasma keeps its energy sound. - Reload is real lever-action foley instead of interface clicks - Projectile flight loops localized: unit_size 50->6, max_db 6->0, max_distance 45 — no more mortar/rocket drone in every player's ear; loops also got proper loop_end (was a zero-length loop) - Flight loops lowpassed into soft whooshes instead of sci-fi engine drones - Menu music converted to OGG with native full-track looping (47s seamless, replaces the broken manual WAV loop points) Animations (movement-shooter pass): - Rebaked miku.glb with 4 new clips from the CC0 library: Grapple (swim reach — reads as a swing), PistolIdle (armed idle with weapon up), PistolShoot, PistolReload - Armed idle: standing with a weapon now uses PistolIdle instead of arms-at-sides - Slide pose overhaul: legs kick out front (lead/trail thighs, knees straighten) — feet-first slide instead of "sitting in a crouch" - Wall run: forward drive pitch + inner arm reaches out to the wall - Per-transition blend times: dash/hit/jump cut fast (0.05-0.08s), locomotion cross-fades smoothly (0.2-0.25s); CrouchWalk speed-scales Fixes: - Third-person camera no longer renders the first-person viewmodel layer (the gun/arms floated in front of the third-person view) Verified: 11/11 movement tests, smoke test 0 failures (17 clips resolved), third-person run screenshot shows grounded animated model, no floating gun. Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Fable 5
parent
6c8b0ddd28
commit
a9b0775de3
@@ -46,12 +46,22 @@ const CLIP_FALLBACKS := {
|
||||
"Death": ["Death", "Fall"],
|
||||
"Hit": ["Hit", "Idle"],
|
||||
"Dance": ["Dance", "Idle"],
|
||||
"PistolIdle": ["PistolIdle", "Idle"],
|
||||
"PistolShoot": ["PistolShoot"],
|
||||
"PistolReload": ["PistolReload"],
|
||||
}
|
||||
|
||||
const LOOPING_CLIPS := ["Idle", "Walk", "Run", "Sprint", "Fall", "Crouch",
|
||||
"CrouchIdle", "CrouchWalk", "Slide", "WallRun", "WallCling", "Grapple", "Dance"]
|
||||
"CrouchIdle", "CrouchWalk", "Slide", "WallRun", "WallCling", "Grapple",
|
||||
"Dance", "PistolIdle"]
|
||||
|
||||
const BLEND_TIME := 0.15
|
||||
## Per-clip blend overrides: snappy moves cut fast, locomotion cross-fades.
|
||||
const BLEND_TIMES := {
|
||||
"Dash": 0.06, "Jump": 0.08, "Hit": 0.05, "Land": 0.08,
|
||||
"Slide": 0.1, "Death": 0.1,
|
||||
"Idle": 0.25, "PistolIdle": 0.25, "Walk": 0.2, "Run": 0.2, "Sprint": 0.2,
|
||||
}
|
||||
|
||||
var skeleton: Skeleton3D
|
||||
var animation_player: AnimationPlayer
|
||||
@@ -246,6 +256,9 @@ func update_state(state: String, speed: float, is_crouching: bool = false) -> vo
|
||||
clip = "Run"
|
||||
elif speed > 0.5:
|
||||
clip = "Walk"
|
||||
elif is_holding_weapon and _resolved_clips.has("PistolIdle"):
|
||||
# Armed idle: weapon actually held ready instead of arms-down
|
||||
clip = "PistolIdle"
|
||||
"air":
|
||||
# Rising = jump, falling = the fall loop.
|
||||
clip = "Jump" if _vertical_speed() > 0.5 else "Fall"
|
||||
@@ -270,7 +283,7 @@ func update_state(state: String, speed: float, is_crouching: bool = false) -> vo
|
||||
|
||||
# Scale locomotion playback so feet keep up with actual movement speed.
|
||||
match clip:
|
||||
"Walk":
|
||||
"Walk", "CrouchWalk":
|
||||
animation_player.speed_scale = clampf(speed / walk_anim_reference_speed, 0.7, 1.6)
|
||||
"Run", "Sprint", "WallRun":
|
||||
animation_player.speed_scale = clampf(speed / run_anim_reference_speed, 0.7, 1.8)
|
||||
@@ -320,7 +333,7 @@ func _play_clip(canonical: String, restart: bool = false) -> void:
|
||||
return
|
||||
if restart:
|
||||
animation_player.stop()
|
||||
animation_player.play(clip_name, BLEND_TIME)
|
||||
animation_player.play(clip_name, BLEND_TIMES.get(canonical, BLEND_TIME))
|
||||
_current_clip = clip_name
|
||||
|
||||
|
||||
@@ -432,6 +445,10 @@ class ShooterPoseModifier extends SkeletonModifier3D:
|
||||
const LEAN_PITCH := 0.18
|
||||
const SLIDE_BACK := 0.75 # torso lean-back during slide
|
||||
const SLIDE_HEAD_UP := 0.7 # head pitch to keep looking forward
|
||||
const SLIDE_LEG_FWD := 0.95 # thighs swing forward so feet lead the slide
|
||||
const SLIDE_KNEE := 0.55 # shins straighten against the crouch clip's bend
|
||||
const WALL_PITCH := 0.2 # forward drive lean during a wall run
|
||||
const WALL_ARM_OUT := 0.9 # inner arm reaches out to touch the wall
|
||||
const ADS_LIFT := 1.05 # upper-arm raise toward aim at full ADS
|
||||
const ADS_SWING := 0.6 # swing arms in toward centre-front on ADS
|
||||
const ADS_FOREARM := 0.55 # forearm bend to bring the weapon up on ADS
|
||||
@@ -445,7 +462,8 @@ class ShooterPoseModifier extends SkeletonModifier3D:
|
||||
var skel := get_skeleton()
|
||||
var names := SPINE + ["DEF-neck", "DEF-head",
|
||||
"DEF-upper_arm.R", "DEF-forearm.R", "DEF-hand.R",
|
||||
"DEF-upper_arm.L", "DEF-forearm.L", "DEF-hand.L"]
|
||||
"DEF-upper_arm.L", "DEF-forearm.L", "DEF-hand.L",
|
||||
"DEF-thigh.R", "DEF-shin.R", "DEF-thigh.L", "DEF-shin.L"]
|
||||
for n in names:
|
||||
_idx[n] = skel.find_bone(n)
|
||||
_resolved = true
|
||||
@@ -476,14 +494,23 @@ class ShooterPoseModifier extends SkeletonModifier3D:
|
||||
for n in SPINE:
|
||||
_add_space(skel, _idx.get(n, -1), per)
|
||||
|
||||
# Wall run: roll the torso into the wall (+wall = wall on the right).
|
||||
# Wall run: roll into the wall, drive forward, inner arm reaches the wall.
|
||||
func _apply_wall_lean(skel: Skeleton3D) -> void:
|
||||
var roll := Quaternion(Vector3(0, 0, 1), wall * 0.35)
|
||||
var per := Quaternion.IDENTITY.slerp(roll, 1.0 / SPINE.size())
|
||||
var q := Quaternion(Vector3(0, 0, 1), wall * 0.35) \
|
||||
* Quaternion(Vector3(1, 0, 0), WALL_PITCH * absf(wall))
|
||||
var per := Quaternion.IDENTITY.slerp(q, 1.0 / SPINE.size())
|
||||
for n in SPINE:
|
||||
_add_space(skel, _idx.get(n, -1), per)
|
||||
# Reach the wall-side arm out sideways (character-right = -X, so a
|
||||
# negative Z rotation swings the down arm toward the right side).
|
||||
var reach := Quaternion(Vector3(0, 0, 1), -WALL_ARM_OUT * wall)
|
||||
if wall > 0.0:
|
||||
_add_space(skel, _idx.get("DEF-upper_arm.R", -1), reach)
|
||||
else:
|
||||
_add_space(skel, _idx.get("DEF-upper_arm.L", -1), reach)
|
||||
|
||||
# Slide: lean the whole torso back, then pitch the head up to look forward.
|
||||
# Slide: torso leans back, head looks forward, legs kick out in front so
|
||||
# it reads feet-first instead of "sitting in a crouch".
|
||||
func _apply_slide(skel: Skeleton3D) -> void:
|
||||
var back := Quaternion(Vector3(1, 0, 0), -SLIDE_BACK * slide)
|
||||
var per := Quaternion.IDENTITY.slerp(back, 1.0 / SPINE.size())
|
||||
@@ -492,6 +519,14 @@ class ShooterPoseModifier extends SkeletonModifier3D:
|
||||
var up := Quaternion(Vector3(1, 0, 0), SLIDE_HEAD_UP * slide)
|
||||
_add_space(skel, _idx.get("DEF-neck", -1), Quaternion.IDENTITY.slerp(up, 0.5))
|
||||
_add_space(skel, _idx.get("DEF-head", -1), Quaternion.IDENTITY.slerp(up, 0.5))
|
||||
# Legs: thighs swing forward (lead leg further), knees straighten.
|
||||
var lead := Quaternion(Vector3(1, 0, 0), -SLIDE_LEG_FWD * slide)
|
||||
var trail := Quaternion(Vector3(1, 0, 0), -SLIDE_LEG_FWD * 0.7 * slide)
|
||||
_add_space(skel, _idx.get("DEF-thigh.R", -1), lead)
|
||||
_add_space(skel, _idx.get("DEF-thigh.L", -1), trail)
|
||||
var straighten := Quaternion(Vector3(1, 0, 0), SLIDE_KNEE * slide)
|
||||
_add_space(skel, _idx.get("DEF-shin.R", -1), straighten)
|
||||
_add_space(skel, _idx.get("DEF-shin.L", -1), straighten)
|
||||
|
||||
# Weapon hold. At the hip the base clip already keeps the arms down with the
|
||||
# weapon (attached to the hand) at the side, so we leave it alone. On ADS we
|
||||
|
||||
Reference in New Issue
Block a user