feat: added animations for movement
This commit is contained in:
+167
-18
@@ -4,14 +4,25 @@ class_name HumanoidModel
|
||||
@export var color: Color = Color.WHITE
|
||||
@export var shadows_only: bool = false
|
||||
|
||||
# Internal Pivots
|
||||
var root_pivot: Node3D
|
||||
var torso: MeshInstance3D
|
||||
var head_pivot: Node3D
|
||||
var head: MeshInstance3D
|
||||
var arm_l_pivot: Node3D
|
||||
var arm_l: MeshInstance3D
|
||||
var arm_r_pivot: Node3D
|
||||
var arm_r: MeshInstance3D
|
||||
var leg_l_pivot: Node3D
|
||||
var leg_l: MeshInstance3D
|
||||
var leg_r_pivot: Node3D
|
||||
var leg_r: MeshInstance3D
|
||||
|
||||
# Simple programmatic humanoid
|
||||
# Animation State Variables
|
||||
var current_state: String = "idle"
|
||||
var movement_speed: float = 0.0
|
||||
var _anim_time: float = 0.0
|
||||
|
||||
func _ready() -> void:
|
||||
var mat := StandardMaterial3D.new()
|
||||
mat.albedo_color = color
|
||||
@@ -19,6 +30,9 @@ func _ready() -> void:
|
||||
|
||||
var shadow_setting = GeometryInstance3D.SHADOW_CASTING_SETTING_SHADOWS_ONLY if shadows_only else GeometryInstance3D.SHADOW_CASTING_SETTING_ON
|
||||
|
||||
root_pivot = Node3D.new()
|
||||
add_child(root_pivot)
|
||||
|
||||
# Torso
|
||||
torso = MeshInstance3D.new()
|
||||
var t_mesh = BoxMesh.new()
|
||||
@@ -27,46 +41,181 @@ func _ready() -> void:
|
||||
torso.mesh = t_mesh
|
||||
torso.position = Vector3(0, 1.15, 0)
|
||||
torso.cast_shadow = shadow_setting
|
||||
add_child(torso)
|
||||
root_pivot.add_child(torso)
|
||||
|
||||
# Head
|
||||
head_pivot = Node3D.new()
|
||||
head_pivot.position = Vector3(0, 1.5, 0)
|
||||
root_pivot.add_child(head_pivot)
|
||||
|
||||
head = MeshInstance3D.new()
|
||||
var h_mesh = BoxMesh.new()
|
||||
h_mesh.size = Vector3(0.25, 0.25, 0.25)
|
||||
h_mesh.material = mat
|
||||
head.mesh = h_mesh
|
||||
head.position = Vector3(0, 1.65, 0)
|
||||
head.position = Vector3(0, 0.15, 0) # Offset from neck
|
||||
head.cast_shadow = shadow_setting
|
||||
add_child(head)
|
||||
head_pivot.add_child(head)
|
||||
|
||||
# Arms
|
||||
arm_l = MeshInstance3D.new()
|
||||
var a_mesh = BoxMesh.new()
|
||||
a_mesh.size = Vector3(0.12, 0.65, 0.12)
|
||||
a_mesh.material = mat
|
||||
arm_l.mesh = a_mesh
|
||||
arm_l.position = Vector3(-0.28, 1.15, 0)
|
||||
arm_l.cast_shadow = shadow_setting
|
||||
add_child(arm_l)
|
||||
|
||||
arm_l_pivot = Node3D.new()
|
||||
arm_l_pivot.position = Vector3(-0.28, 1.45, 0) # Shoulder
|
||||
root_pivot.add_child(arm_l_pivot)
|
||||
arm_l = MeshInstance3D.new()
|
||||
arm_l.mesh = a_mesh
|
||||
arm_l.position = Vector3(0, -0.3, 0) # Offset down from shoulder
|
||||
arm_l.cast_shadow = shadow_setting
|
||||
arm_l_pivot.add_child(arm_l)
|
||||
|
||||
arm_r_pivot = Node3D.new()
|
||||
arm_r_pivot.position = Vector3(0.28, 1.45, 0) # Shoulder
|
||||
root_pivot.add_child(arm_r_pivot)
|
||||
arm_r = MeshInstance3D.new()
|
||||
arm_r.mesh = a_mesh
|
||||
arm_r.position = Vector3(0.28, 1.15, 0)
|
||||
arm_r.position = Vector3(0, -0.3, 0) # Offset down from shoulder
|
||||
arm_r.cast_shadow = shadow_setting
|
||||
add_child(arm_r)
|
||||
arm_r_pivot.add_child(arm_r)
|
||||
|
||||
# Legs
|
||||
leg_l = MeshInstance3D.new()
|
||||
var l_mesh = BoxMesh.new()
|
||||
l_mesh.size = Vector3(0.15, 0.8, 0.15)
|
||||
l_mesh.material = mat
|
||||
leg_l.mesh = l_mesh
|
||||
leg_l.position = Vector3(-0.12, 0.4, 0)
|
||||
leg_l.cast_shadow = shadow_setting
|
||||
add_child(leg_l)
|
||||
|
||||
leg_l_pivot = Node3D.new()
|
||||
leg_l_pivot.position = Vector3(-0.12, 0.8, 0) # Hip
|
||||
root_pivot.add_child(leg_l_pivot)
|
||||
leg_l = MeshInstance3D.new()
|
||||
leg_l.mesh = l_mesh
|
||||
leg_l.position = Vector3(0, -0.4, 0) # Offset down from hip
|
||||
leg_l.cast_shadow = shadow_setting
|
||||
leg_l_pivot.add_child(leg_l)
|
||||
|
||||
leg_r_pivot = Node3D.new()
|
||||
leg_r_pivot.position = Vector3(0.12, 0.8, 0) # Hip
|
||||
root_pivot.add_child(leg_r_pivot)
|
||||
leg_r = MeshInstance3D.new()
|
||||
leg_r.mesh = l_mesh
|
||||
leg_r.position = Vector3(0.12, 0.4, 0)
|
||||
leg_r.position = Vector3(0, -0.4, 0) # Offset down from hip
|
||||
leg_r.cast_shadow = shadow_setting
|
||||
add_child(leg_r)
|
||||
leg_r_pivot.add_child(leg_r)
|
||||
|
||||
func update_state(state: String, speed: float, is_crouching: bool = false) -> void:
|
||||
current_state = state
|
||||
movement_speed = speed
|
||||
if is_crouching and current_state == "ground":
|
||||
current_state = "crouch"
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
# Advance time. For walk, speed it up based on movement speed.
|
||||
var anim_speed = 1.0
|
||||
if current_state == "ground" and movement_speed > 1.0:
|
||||
anim_speed = movement_speed * 0.4
|
||||
|
||||
_anim_time += delta * anim_speed
|
||||
|
||||
# Target values
|
||||
var t_root_pos := Vector3.ZERO
|
||||
var t_root_rot := Vector3.ZERO
|
||||
var t_arm_l_rot := Vector3.ZERO
|
||||
var t_arm_r_rot := Vector3.ZERO
|
||||
var t_leg_l_rot := Vector3.ZERO
|
||||
var t_leg_r_rot := Vector3.ZERO
|
||||
|
||||
match current_state:
|
||||
"ground", "idle":
|
||||
if movement_speed > 1.0:
|
||||
# Walk / Run
|
||||
var swing = sin(_anim_time * 15.0)
|
||||
t_arm_l_rot.x = swing * 0.8
|
||||
t_arm_r_rot.x = -swing * 0.8
|
||||
t_leg_l_rot.x = -swing * 0.8
|
||||
t_leg_r_rot.x = swing * 0.8
|
||||
# Bounce slightly
|
||||
t_root_pos.y = abs(cos(_anim_time * 15.0)) * 0.1
|
||||
else:
|
||||
# Idle
|
||||
var breath = sin(_anim_time * 2.0)
|
||||
t_root_pos.y = breath * 0.02
|
||||
t_arm_l_rot.z = 0.05 + breath * 0.01
|
||||
t_arm_r_rot.z = -0.05 - breath * 0.01
|
||||
|
||||
"crouch":
|
||||
t_root_pos.y = -0.5
|
||||
# Bend legs to simulate squat
|
||||
t_leg_l_rot.x = -1.0
|
||||
t_leg_r_rot.x = -1.0
|
||||
# Arms tucked
|
||||
t_arm_l_rot.x = 0.5
|
||||
t_arm_r_rot.x = 0.5
|
||||
|
||||
"slide":
|
||||
t_root_pos.y = -0.6
|
||||
t_root_rot.x = deg_to_rad(-60) # Lean back
|
||||
# Legs extended
|
||||
t_leg_l_rot.x = deg_to_rad(-45)
|
||||
t_leg_r_rot.x = deg_to_rad(60)
|
||||
# Arms swept back for balance
|
||||
t_arm_l_rot.x = deg_to_rad(60)
|
||||
t_arm_r_rot.x = deg_to_rad(60)
|
||||
|
||||
"air":
|
||||
# Falling / Jumping
|
||||
t_leg_l_rot.x = -0.2
|
||||
t_leg_r_rot.x = 0.2
|
||||
t_arm_l_rot.x = -0.5
|
||||
t_arm_r_rot.x = -0.5
|
||||
t_arm_l_rot.z = 0.3
|
||||
t_arm_r_rot.z = -0.3
|
||||
|
||||
"wall_run":
|
||||
# Tilt away from wall slightly
|
||||
t_root_rot.z = deg_to_rad(-15)
|
||||
# Fast cycle legs
|
||||
var cycle = sin(_anim_time * 25.0)
|
||||
t_leg_l_rot.x = cycle * 0.6
|
||||
t_leg_r_rot.x = -cycle * 0.6
|
||||
t_arm_l_rot.x = -cycle * 0.6
|
||||
t_arm_r_rot.x = cycle * 0.6
|
||||
|
||||
"wall_cling":
|
||||
# Hanging onto the wall
|
||||
t_leg_l_rot.x = deg_to_rad(-45)
|
||||
t_leg_r_rot.x = deg_to_rad(30)
|
||||
t_arm_l_rot.x = deg_to_rad(-160)
|
||||
t_arm_r_rot.x = deg_to_rad(-160)
|
||||
|
||||
"grapple":
|
||||
# One arm forward toward target, legs dangling
|
||||
t_arm_l_rot.x = deg_to_rad(-120)
|
||||
t_arm_r_rot.x = deg_to_rad(10)
|
||||
t_leg_l_rot.x = deg_to_rad(15)
|
||||
t_leg_r_rot.x = deg_to_rad(-10)
|
||||
|
||||
"dash":
|
||||
# Superman pose
|
||||
t_root_rot.x = deg_to_rad(-70)
|
||||
t_arm_l_rot.x = deg_to_rad(-160)
|
||||
t_arm_r_rot.x = deg_to_rad(-160)
|
||||
t_leg_l_rot.x = deg_to_rad(20)
|
||||
t_leg_r_rot.x = deg_to_rad(20)
|
||||
|
||||
# Smoothly interpolate towards target values
|
||||
var lerp_speed = 15.0 * delta
|
||||
root_pivot.position = root_pivot.position.lerp(t_root_pos, lerp_speed)
|
||||
root_pivot.rotation = _lerp_vec3(root_pivot.rotation, t_root_rot, lerp_speed)
|
||||
|
||||
arm_l_pivot.rotation = _lerp_vec3(arm_l_pivot.rotation, t_arm_l_rot, lerp_speed)
|
||||
arm_r_pivot.rotation = _lerp_vec3(arm_r_pivot.rotation, t_arm_r_rot, lerp_speed)
|
||||
leg_l_pivot.rotation = _lerp_vec3(leg_l_pivot.rotation, t_leg_l_rot, lerp_speed)
|
||||
leg_r_pivot.rotation = _lerp_vec3(leg_r_pivot.rotation, t_leg_r_rot, lerp_speed)
|
||||
|
||||
func _lerp_vec3(a: Vector3, b: Vector3, t: float) -> Vector3:
|
||||
return Vector3(
|
||||
lerp_angle(a.x, b.x, t),
|
||||
lerp_angle(a.y, b.y, t),
|
||||
lerp_angle(a.z, b.z, t)
|
||||
)
|
||||
|
||||
@@ -278,6 +278,12 @@ func _physics_process(_delta: float) -> void:
|
||||
sm.input_dir = raw_input
|
||||
sm.wish_dir_world = world_dir
|
||||
|
||||
# Update humanoid model animation state
|
||||
var humanoid = get_node_or_null("HumanoidModel")
|
||||
if humanoid:
|
||||
var h_speed = Vector2(velocity.x, velocity.z).length()
|
||||
humanoid.update_state(sm.current_state, h_speed, sm.input_crouch)
|
||||
|
||||
|
||||
func _on_movement_event(ev: String, data: Dictionary) -> void:
|
||||
if ev == "chain_updated":
|
||||
|
||||
Reference in New Issue
Block a user