Feat/visual audio overhaul #21
Binary file not shown.
@@ -3,6 +3,11 @@
|
||||
All current WAVs are forged (pitch/layer/trim via ffmpeg — see repo history)
|
||||
from these CC0 packs, no attribution required:
|
||||
|
||||
- The Free Firearm Sound Library (Ben Jaszczak et al.) — CC0,
|
||||
https://opengameart.org/content/the-free-firearm-sound-library
|
||||
→ all ballistic weapon fire sounds (AK-47/AR-15/PPSh/Mosin/Savage/
|
||||
Mossberg/Nova/Ruger/Carl Gustav recordings) + reload (Model 1894 lever)
|
||||
|
||||
- Kenney Impact Sounds — https://kenney.nl/assets/impact-sounds (CC0)
|
||||
- Kenney Sci-Fi Sounds — https://kenney.nl/assets/sci-fi-sounds (CC0)
|
||||
- Kenney Interface Sounds — https://kenney.nl/assets/interface-sounds (CC0)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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
|
||||
|
||||
@@ -26,6 +26,14 @@ func _process(_delta: float) -> bool:
|
||||
change_scene_to_file("res://scenes/maps/test_level/test_level.tscn")
|
||||
elif _frames == 240:
|
||||
_shot("level")
|
||||
# Third person + run forward, to eyeball model grounding and animation
|
||||
for p in root.find_children("*", "CharacterBody3D", true, false):
|
||||
if p.has_method("set_third_person") and p.is_multiplayer_authority():
|
||||
p.set_third_person(true)
|
||||
Input.action_press("move_forward")
|
||||
elif _frames == 320:
|
||||
_shot("tp_run")
|
||||
Input.action_release("move_forward")
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
uid://c3fpowkoyt67f
|
||||
@@ -174,6 +174,9 @@ func _setup_third_person_camera() -> void:
|
||||
_tp_camera.name = "ThirdPersonCamera"
|
||||
_tp_camera.fov = camera.fov
|
||||
_tp_camera.current = false
|
||||
# Never render the first-person viewmodel layer (20) — otherwise the gun
|
||||
# and arms float in front of the third-person view.
|
||||
_tp_camera.cull_mask &= ~(1 << 19)
|
||||
boom.add_child(_tp_camera)
|
||||
|
||||
|
||||
|
||||
@@ -61,6 +61,11 @@ LIBRARY_CLIP_MAP = {
|
||||
"Death01": "Death",
|
||||
"Hit_Chest": "Hit",
|
||||
"Dance_Loop": "Dance",
|
||||
# Movement-shooter extras:
|
||||
"Swim_Fwd_Loop": "Grapple", # superman reach reads as a swing pose
|
||||
"Pistol_Idle_Loop": "PistolIdle", # armed idle (weapon actually held up)
|
||||
"Pistol_Shoot": "PistolShoot",
|
||||
"Pistol_Reload": "PistolReload",
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -13,14 +13,14 @@ func _ready() -> void:
|
||||
# Comic/cel theme for every Control in the game (root window inherits down)
|
||||
UITheme.apply_global(get_tree())
|
||||
|
||||
# Menu music (scene-owned: stops automatically when a level loads)
|
||||
var music_path := "res://assets/sounds/music_menu.wav"
|
||||
# Menu music (scene-owned: stops automatically when a level loads).
|
||||
# OGG loops natively over the full track — no manual loop points.
|
||||
var music_path := "res://assets/sounds/music_menu.ogg"
|
||||
if ResourceLoader.exists(music_path):
|
||||
var music := AudioStreamPlayer.new()
|
||||
music.stream = load(music_path)
|
||||
if music.stream is AudioStreamWAV:
|
||||
music.stream.loop_mode = AudioStreamWAV.LOOP_FORWARD
|
||||
music.stream.loop_end = music.stream.data.size() / 4 # 16-bit stereo frames
|
||||
if music.stream is AudioStreamOggVorbis:
|
||||
music.stream.loop = true
|
||||
music.bus = "Music"
|
||||
music.volume_db = -8.0
|
||||
music.autoplay = true
|
||||
|
||||
@@ -65,9 +65,13 @@ func _setup_effects() -> void:
|
||||
flight_sound.autoplay = true
|
||||
if flight_sound.stream is AudioStreamWAV:
|
||||
flight_sound.stream.loop_mode = AudioStreamWAV.LOOP_FORWARD
|
||||
flight_sound.unit_size = 50.0
|
||||
flight_sound.max_db = 6.0
|
||||
flight_sound.volume_db = 6.0
|
||||
flight_sound.stream.loop_end = flight_sound.stream.data.size() / 2 # mono 16-bit frames
|
||||
# Localized: audible nearby, falls off with distance instead of droning
|
||||
# in every player's ear regardless of where the projectile is.
|
||||
flight_sound.unit_size = 6.0
|
||||
flight_sound.max_db = 0.0
|
||||
flight_sound.max_distance = 45.0
|
||||
flight_sound.volume_db = -8.0
|
||||
add_child(flight_sound)
|
||||
flight_sound.play()
|
||||
|
||||
|
||||
@@ -17,9 +17,11 @@ func _setup_effects() -> void:
|
||||
flight_sound.autoplay = true
|
||||
if flight_sound.stream is AudioStreamWAV:
|
||||
flight_sound.stream.loop_mode = AudioStreamWAV.LOOP_FORWARD
|
||||
flight_sound.unit_size = 50.0
|
||||
flight_sound.max_db = 6.0
|
||||
flight_sound.volume_db = 0.0
|
||||
flight_sound.stream.loop_end = flight_sound.stream.data.size() / 2 # mono 16-bit frames
|
||||
flight_sound.unit_size = 6.0
|
||||
flight_sound.max_db = 0.0
|
||||
flight_sound.max_distance = 45.0
|
||||
flight_sound.volume_db = -10.0
|
||||
add_child(flight_sound)
|
||||
flight_sound.play()
|
||||
|
||||
|
||||
@@ -24,9 +24,11 @@ func _start_homing_effects() -> void:
|
||||
flight_sound.autoplay = true
|
||||
if flight_sound.stream is AudioStreamWAV:
|
||||
flight_sound.stream.loop_mode = AudioStreamWAV.LOOP_FORWARD
|
||||
flight_sound.unit_size = 50.0
|
||||
flight_sound.max_db = 6.0
|
||||
flight_sound.volume_db = 6.0
|
||||
flight_sound.stream.loop_end = flight_sound.stream.data.size() / 2 # mono 16-bit frames
|
||||
flight_sound.unit_size = 6.0
|
||||
flight_sound.max_db = 0.0
|
||||
flight_sound.max_distance = 45.0
|
||||
flight_sound.volume_db = -10.0
|
||||
add_child(flight_sound)
|
||||
flight_sound.play()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user