Feat/3 adding playermodel #8
+167
-18
@@ -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)
|
||||||
|
)
|
||||||
|
|||||||
@@ -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":
|
||||||
|
|||||||
Reference in New Issue
Block a user