game: sci-fi clip arms skin the fists slot — FAB Sci Fi Hands rig (Idle_Fight stance, Attack_01-04 punches, Walk/Run locomotion) shown instead of the procedural mixamo rig when bare hands equipped; other weapons unchanged. Headless probe: dev/probe_scifi.gd
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
c2fad7eb53
commit
43c8c99b1f
BIN
game/assets/viewmodel/scifi_hands.glb
Normal file
BIN
game/assets/viewmodel/scifi_hands.glb
Normal file
Binary file not shown.
44
game/dev/probe_scifi.gd
Normal file
44
game/dev/probe_scifi.gd
Normal file
@ -0,0 +1,44 @@
|
||||
extends SceneTree
|
||||
|
||||
## Verify the sci-fi clip arms wire-up: found, animated, toggled by the fists slot.
|
||||
## godot --headless --path game --script dev/probe_scifi.gd
|
||||
|
||||
func _initialize() -> void:
|
||||
var main: Node = (load("res://main.tscn") as PackedScene).instantiate()
|
||||
get_root().add_child(main)
|
||||
await process_frame
|
||||
await process_frame
|
||||
var p := _find(main, "Player") as Player
|
||||
if p == null:
|
||||
print("FAIL no player"); quit(); return
|
||||
var vm := p.viewmodel
|
||||
var arms: Node3D = p.camera.get_node_or_null("SciFiArms")
|
||||
print("arms found: ", arms != null)
|
||||
if arms == null: quit(); return
|
||||
var ap := _anim(arms)
|
||||
print("anims: ", ap.get_animation_list() if ap else [])
|
||||
vm.equip(p.loadout()[0]) # fists
|
||||
await process_frame
|
||||
print("fists -> arms visible: ", arms.visible, " rig visible: ", vm.get_node("Rig").visible, " playing: ", ap.current_animation)
|
||||
vm.start_swing()
|
||||
await process_frame
|
||||
print("swing -> playing: ", ap.current_animation)
|
||||
vm.equip(p.loadout()[1]) # a weapon
|
||||
await process_frame
|
||||
print("weapon -> arms visible: ", arms.visible, " rig visible: ", vm.get_node("Rig").visible)
|
||||
print("PROBE OK")
|
||||
quit()
|
||||
|
||||
func _anim(n: Node) -> AnimationPlayer:
|
||||
if n is AnimationPlayer: return n
|
||||
for c in n.get_children():
|
||||
var r := _anim(c)
|
||||
if r != null: return r
|
||||
return null
|
||||
|
||||
func _find(n: Node, name_: String) -> Node:
|
||||
if (name_ == "Player" and n is Player) or n.name == name_: return n
|
||||
for c in n.get_children():
|
||||
var r := _find(c, name_)
|
||||
if r != null: return r
|
||||
return null
|
||||
@ -24,6 +24,8 @@ class_name ViewModel
|
||||
## Godot 4.7 GDScript 2.0.
|
||||
|
||||
const ARMS_GLB := "res://assets/viewmodel/fps_arms.glb"
|
||||
const SCIFI_GLB := "res://assets/viewmodel/scifi_hands.glb"
|
||||
const SCIFI_ATTACKS := ["Attack_01", "Attack_02", "Attack_03", "Attack_04"]
|
||||
|
||||
const BONE_HAND_R := "mixamorig_RightHand"
|
||||
const BONE_HAND_L := "mixamorig_LeftHand"
|
||||
@ -63,6 +65,10 @@ var _palm_local_r := Vector3.ZERO # wrist->middle knuckle, in hand-bo
|
||||
var _palm_local_l := Vector3.ZERO
|
||||
var _weapon_node: Node3D = null
|
||||
var _weapon: Weapon = null
|
||||
var _cam: Camera3D = null
|
||||
var _clip_arms: Node3D = null # sci-fi FAB hands (fists slot only)
|
||||
var _clip_anim: AnimationPlayer = null
|
||||
var _clip_active := false
|
||||
|
||||
# ---------------------------------------------------------------- motion state
|
||||
var _sway := Vector2.ZERO # smoothed look-delta the rig lags by
|
||||
@ -132,6 +138,7 @@ const IDLE_OFF_SHAFT := Vector3(0.35, 0.55, -0.75) # a virtual "shaft" for the
|
||||
func setup(cam: Camera3D) -> void:
|
||||
if cam == null:
|
||||
return
|
||||
_cam = cam
|
||||
cam.add_child(self)
|
||||
scale = Vector3.ONE * vm_scale
|
||||
_rig = Node3D.new()
|
||||
@ -147,6 +154,7 @@ func setup(cam: Camera3D) -> void:
|
||||
|
||||
_arm_r = _spawn_arm(true)
|
||||
_arm_l = _spawn_arm(false)
|
||||
_init_clip_arms()
|
||||
|
||||
## One instance of the arms rig, with the other side's meshes hidden. Returns null if
|
||||
## the asset is missing so the game still runs (you just get no hands).
|
||||
@ -291,6 +299,7 @@ func equip(w: Weapon) -> void:
|
||||
for mi in _mesh_nodes(_weapon_node):
|
||||
mi.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
_pose_hands()
|
||||
_set_clip_mode(w != null and w.id == "fists")
|
||||
|
||||
## Start a swap: the current weapon lowers, then `w` is equipped and raised.
|
||||
func request_swap(w: Weapon) -> void:
|
||||
@ -410,6 +419,8 @@ func _curl_fingers(skel: Skeleton3D, prefixes: Array, thumb_prefix: String,
|
||||
# ---------------------------------------------------------------- driving
|
||||
## Player calls this every frame with its state.
|
||||
func drive(look_delta: Vector2, planar_speed: float, on_floor: bool, dt: float) -> void:
|
||||
if _clip_active:
|
||||
_clip_locomotion(planar_speed)
|
||||
var heft: float = _weapon.heft if _weapon != null else 1.0
|
||||
|
||||
# --- sway: the rig lags the camera, then springs back ---
|
||||
@ -490,6 +501,10 @@ func _sample(kf: Array, t: float) -> Array:
|
||||
# ---------------------------------------------------------------- events
|
||||
func start_swing() -> void:
|
||||
_swing_t = 0.0
|
||||
if _clip_active and _clip_anim != null:
|
||||
var pick: String = SCIFI_ATTACKS[randi() % SCIFI_ATTACKS.size()]
|
||||
if _clip_anim.has_animation(pick):
|
||||
_clip_anim.play(pick, 0.05)
|
||||
|
||||
func is_swinging() -> bool:
|
||||
return _swing_t >= 0.0
|
||||
@ -499,3 +514,62 @@ func is_swapping() -> bool:
|
||||
|
||||
func land(force: float) -> void:
|
||||
_dip = clampf(_dip + force * 0.06, 0.0, 0.11)
|
||||
|
||||
|
||||
# ------------------------------------------------- sci-fi clip arms (fists skin)
|
||||
# The FAB "Sci Fi Hands" rig, clip-driven (Idle_Fight stance, Attack_01-04 punches,
|
||||
# Walk/Run locomotion baked in the pack). Shown INSTEAD of the procedural rig when
|
||||
# bare hands are equipped; every other weapon uses the mixamo arms as before.
|
||||
func _init_clip_arms() -> void:
|
||||
if not ResourceLoader.exists(SCIFI_GLB):
|
||||
return
|
||||
var packed := load(SCIFI_GLB) as PackedScene
|
||||
if packed == null:
|
||||
return
|
||||
_clip_arms = packed.instantiate()
|
||||
_clip_arms.name = "SciFiArms"
|
||||
_cam.add_child(_clip_arms)
|
||||
_clip_arms.position = Vector3(0.0, -1.55, 0.25)
|
||||
_clip_arms.rotate_y(PI)
|
||||
_clip_anim = _find_anim_player(_clip_arms)
|
||||
if _clip_anim != null:
|
||||
for n in ["Idle", "Idle_Fight", "Walk_01", "Run_01"]:
|
||||
if _clip_anim.has_animation(n):
|
||||
_clip_anim.get_animation(n).loop_mode = Animation.LOOP_LINEAR
|
||||
_clip_anim.animation_finished.connect(func(anim: StringName) -> void:
|
||||
if _clip_active and String(anim).begins_with("Attack"):
|
||||
_clip_anim.play("Idle_Fight", 0.15))
|
||||
for mi in _mesh_nodes(_clip_arms):
|
||||
mi.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
_clip_arms.visible = false
|
||||
|
||||
func _find_anim_player(n: Node) -> AnimationPlayer:
|
||||
if n is AnimationPlayer:
|
||||
return n
|
||||
for c in n.get_children():
|
||||
var r := _find_anim_player(c)
|
||||
if r != null:
|
||||
return r
|
||||
return null
|
||||
|
||||
func _set_clip_mode(on: bool) -> void:
|
||||
_clip_active = on and _clip_arms != null
|
||||
if _clip_arms != null:
|
||||
_clip_arms.visible = _clip_active
|
||||
if _rig != null:
|
||||
_rig.visible = not _clip_active
|
||||
if _clip_active and _clip_anim != null and _clip_anim.has_animation("Idle_Fight"):
|
||||
_clip_anim.play("Idle_Fight", 0.15)
|
||||
|
||||
func _clip_locomotion(planar_speed: float) -> void:
|
||||
if _clip_anim == null:
|
||||
return
|
||||
if _clip_anim.current_animation.begins_with("Attack"):
|
||||
return
|
||||
var want := "Idle_Fight"
|
||||
if planar_speed > 4.0 and _clip_anim.has_animation("Run_01"):
|
||||
want = "Run_01"
|
||||
elif planar_speed > 0.4 and _clip_anim.has_animation("Walk_01"):
|
||||
want = "Walk_01"
|
||||
if _clip_anim.current_animation != String(want):
|
||||
_clip_anim.play(want, 0.2)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user