Feat/3 adding playermodel #8

Merged
Dotts merged 7 commits from feat/3-adding-playermodel into main 2026-06-05 21:23:53 -07:00
2 changed files with 173 additions and 18 deletions
Showing only changes of commit 8984739334 - Show all commits
+167 -18
View File
@@ -4,14 +4,25 @@ class_name HumanoidModel
@export var color: Color = Color.WHITE @export var color: Color = Color.WHITE
@export var shadows_only: bool = false @export var shadows_only: bool = false
# Internal Pivots
var root_pivot: Node3D
var torso: MeshInstance3D var torso: MeshInstance3D
var head_pivot: Node3D
var head: MeshInstance3D var head: MeshInstance3D
var arm_l_pivot: Node3D
var arm_l: MeshInstance3D var arm_l: MeshInstance3D
var arm_r_pivot: Node3D
var arm_r: MeshInstance3D var arm_r: MeshInstance3D
var leg_l_pivot: Node3D
var leg_l: MeshInstance3D var leg_l: MeshInstance3D
var leg_r_pivot: Node3D
var leg_r: MeshInstance3D 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: func _ready() -> void:
var mat := StandardMaterial3D.new() var mat := StandardMaterial3D.new()
mat.albedo_color = color 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 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
torso = MeshInstance3D.new() torso = MeshInstance3D.new()
var t_mesh = BoxMesh.new() var t_mesh = BoxMesh.new()
@@ -27,46 +41,181 @@ func _ready() -> void:
torso.mesh = t_mesh torso.mesh = t_mesh
torso.position = Vector3(0, 1.15, 0) torso.position = Vector3(0, 1.15, 0)
torso.cast_shadow = shadow_setting torso.cast_shadow = shadow_setting
add_child(torso) root_pivot.add_child(torso)
# Head # Head
head_pivot = Node3D.new()
head_pivot.position = Vector3(0, 1.5, 0)
root_pivot.add_child(head_pivot)
head = MeshInstance3D.new() head = MeshInstance3D.new()
var h_mesh = BoxMesh.new() var h_mesh = BoxMesh.new()
h_mesh.size = Vector3(0.25, 0.25, 0.25) h_mesh.size = Vector3(0.25, 0.25, 0.25)
h_mesh.material = mat h_mesh.material = mat
head.mesh = h_mesh 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 head.cast_shadow = shadow_setting
add_child(head) head_pivot.add_child(head)
# Arms # Arms
arm_l = MeshInstance3D.new()
var a_mesh = BoxMesh.new() var a_mesh = BoxMesh.new()
a_mesh.size = Vector3(0.12, 0.65, 0.12) a_mesh.size = Vector3(0.12, 0.65, 0.12)
a_mesh.material = mat 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 = MeshInstance3D.new()
arm_r.mesh = a_mesh 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 arm_r.cast_shadow = shadow_setting
add_child(arm_r) arm_r_pivot.add_child(arm_r)
# Legs # Legs
leg_l = MeshInstance3D.new()
var l_mesh = BoxMesh.new() var l_mesh = BoxMesh.new()
l_mesh.size = Vector3(0.15, 0.8, 0.15) l_mesh.size = Vector3(0.15, 0.8, 0.15)
l_mesh.material = mat 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 = MeshInstance3D.new()
leg_r.mesh = l_mesh 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 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)
)
+6
View File
@@ -278,6 +278,12 @@ func _physics_process(_delta: float) -> void:
sm.input_dir = raw_input sm.input_dir = raw_input
sm.wish_dir_world = world_dir 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: func _on_movement_event(ev: String, data: Dictionary) -> void:
if ev == "chain_updated": if ev == "chain_updated":