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.
|
## Godot 4.7 GDScript 2.0.
|
||||||
|
|
||||||
const ARMS_GLB := "res://assets/viewmodel/fps_arms.glb"
|
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_R := "mixamorig_RightHand"
|
||||||
const BONE_HAND_L := "mixamorig_LeftHand"
|
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 _palm_local_l := Vector3.ZERO
|
||||||
var _weapon_node: Node3D = null
|
var _weapon_node: Node3D = null
|
||||||
var _weapon: Weapon = 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
|
# ---------------------------------------------------------------- motion state
|
||||||
var _sway := Vector2.ZERO # smoothed look-delta the rig lags by
|
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:
|
func setup(cam: Camera3D) -> void:
|
||||||
if cam == null:
|
if cam == null:
|
||||||
return
|
return
|
||||||
|
_cam = cam
|
||||||
cam.add_child(self)
|
cam.add_child(self)
|
||||||
scale = Vector3.ONE * vm_scale
|
scale = Vector3.ONE * vm_scale
|
||||||
_rig = Node3D.new()
|
_rig = Node3D.new()
|
||||||
@ -147,6 +154,7 @@ func setup(cam: Camera3D) -> void:
|
|||||||
|
|
||||||
_arm_r = _spawn_arm(true)
|
_arm_r = _spawn_arm(true)
|
||||||
_arm_l = _spawn_arm(false)
|
_arm_l = _spawn_arm(false)
|
||||||
|
_init_clip_arms()
|
||||||
|
|
||||||
## One instance of the arms rig, with the other side's meshes hidden. Returns null if
|
## 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).
|
## 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):
|
for mi in _mesh_nodes(_weapon_node):
|
||||||
mi.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
mi.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||||
_pose_hands()
|
_pose_hands()
|
||||||
|
_set_clip_mode(w != null and w.id == "fists")
|
||||||
|
|
||||||
## Start a swap: the current weapon lowers, then `w` is equipped and raised.
|
## Start a swap: the current weapon lowers, then `w` is equipped and raised.
|
||||||
func request_swap(w: Weapon) -> void:
|
func request_swap(w: Weapon) -> void:
|
||||||
@ -410,6 +419,8 @@ func _curl_fingers(skel: Skeleton3D, prefixes: Array, thumb_prefix: String,
|
|||||||
# ---------------------------------------------------------------- driving
|
# ---------------------------------------------------------------- driving
|
||||||
## Player calls this every frame with its state.
|
## Player calls this every frame with its state.
|
||||||
func drive(look_delta: Vector2, planar_speed: float, on_floor: bool, dt: float) -> void:
|
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
|
var heft: float = _weapon.heft if _weapon != null else 1.0
|
||||||
|
|
||||||
# --- sway: the rig lags the camera, then springs back ---
|
# --- sway: the rig lags the camera, then springs back ---
|
||||||
@ -490,6 +501,10 @@ func _sample(kf: Array, t: float) -> Array:
|
|||||||
# ---------------------------------------------------------------- events
|
# ---------------------------------------------------------------- events
|
||||||
func start_swing() -> void:
|
func start_swing() -> void:
|
||||||
_swing_t = 0.0
|
_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:
|
func is_swinging() -> bool:
|
||||||
return _swing_t >= 0.0
|
return _swing_t >= 0.0
|
||||||
@ -499,3 +514,62 @@ func is_swapping() -> bool:
|
|||||||
|
|
||||||
func land(force: float) -> void:
|
func land(force: float) -> void:
|
||||||
_dip = clampf(_dip + force * 0.06, 0.0, 0.11)
|
_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