fix: 3rd person and first person views
This commit is contained in:
@@ -167,6 +167,50 @@ func update_state(state: String, speed: float, is_crouching: bool = false) -> vo
|
||||
if is_crouching and current_state == "ground":
|
||||
current_state = "crouch"
|
||||
|
||||
var is_holding_weapon: bool = false
|
||||
|
||||
func set_weapon(script_path: String) -> void:
|
||||
# Clear existing weapons from root_pivot
|
||||
for child in root_pivot.get_children():
|
||||
if child.has_meta("is_third_person_weapon"):
|
||||
child.queue_free()
|
||||
|
||||
if script_path == "":
|
||||
is_holding_weapon = false
|
||||
return
|
||||
|
||||
is_holding_weapon = true
|
||||
|
||||
var script = load(script_path)
|
||||
if not script:
|
||||
return
|
||||
|
||||
var w = script.new()
|
||||
w.name = "ThirdPersonWeapon"
|
||||
w.set_meta("is_third_person_weapon", true)
|
||||
|
||||
# Connect to ready so we can override the weapon's own _ready() calls
|
||||
w.ready.connect(func():
|
||||
w.set_process(false)
|
||||
w.set_process_input(false)
|
||||
if shadows_only:
|
||||
_set_shadows_recursive(w)
|
||||
|
||||
# Force position after the weapon's _build_model() sets it for 1st person
|
||||
# root_pivot is rotated 180 deg around Y, so local +Z is FORWARD and local -X is RIGHT
|
||||
w.position = Vector3(-0.15, 1.0, 0.4)
|
||||
w.rotation_degrees = Vector3(0, 180, 0)
|
||||
)
|
||||
|
||||
# Attach to root_pivot so it stays steady and points perfectly forward
|
||||
root_pivot.add_child(w)
|
||||
|
||||
func _set_shadows_recursive(node: Node) -> void:
|
||||
if node is GeometryInstance3D:
|
||||
node.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_SHADOWS_ONLY
|
||||
for child in node.get_children():
|
||||
_set_shadows_recursive(child)
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
var anim_speed = 1.0
|
||||
if current_state == "ground" and movement_speed > 1.0:
|
||||
@@ -320,6 +364,18 @@ func _process(delta: float) -> void:
|
||||
t_thigh_r_rot.x = deg_to_rad(10)
|
||||
t_calf_r_rot.x = 0.0
|
||||
|
||||
# Override right arm (and left) if holding a weapon
|
||||
if is_holding_weapon and current_state != "death":
|
||||
# Pose the right arm to look like it's holding the weapon handle
|
||||
t_upper_arm_r_rot.x = -0.6
|
||||
t_upper_arm_r_rot.z = 0.15
|
||||
t_lower_arm_r_rot.x = -1.0
|
||||
|
||||
# Pose the left arm to look like it's holding the foregrip
|
||||
t_upper_arm_l_rot.x = -0.5
|
||||
t_upper_arm_l_rot.z = -0.15
|
||||
t_lower_arm_l_rot.x = -1.1
|
||||
|
||||
var lerp_speed = 40.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)
|
||||
|
||||
@@ -12,6 +12,7 @@ var camera: Camera3D
|
||||
var muzzle_flash: OmniLight3D
|
||||
var fire_sound: AudioStreamPlayer3D
|
||||
var reload_sound: AudioStreamPlayer3D
|
||||
var model_root: Node3D
|
||||
|
||||
func _ready() -> void:
|
||||
set_process_input(true)
|
||||
@@ -22,6 +23,9 @@ func _build_model() -> void:
|
||||
# Simple FPS weapon placement (bottom right)
|
||||
position = Vector3(0.3, -0.3, -0.6)
|
||||
|
||||
model_root = Node3D.new()
|
||||
add_child(model_root)
|
||||
|
||||
# Stock
|
||||
var stock = MeshInstance3D.new()
|
||||
var stock_mesh = BoxMesh.new()
|
||||
@@ -31,7 +35,7 @@ func _build_model() -> void:
|
||||
stock_mesh.material = stock_mat
|
||||
stock.mesh = stock_mesh
|
||||
stock.position = Vector3(0, 0, 0.2)
|
||||
add_child(stock)
|
||||
model_root.add_child(stock)
|
||||
|
||||
# Left Barrel
|
||||
var barrel_l = MeshInstance3D.new()
|
||||
@@ -47,14 +51,14 @@ func _build_model() -> void:
|
||||
barrel_l.mesh = barrel_mesh
|
||||
barrel_l.rotation.x = deg_to_rad(90)
|
||||
barrel_l.position = Vector3(-0.04, 0.03, -0.1)
|
||||
add_child(barrel_l)
|
||||
model_root.add_child(barrel_l)
|
||||
|
||||
# Right Barrel
|
||||
var barrel_r = MeshInstance3D.new()
|
||||
barrel_r.mesh = barrel_mesh
|
||||
barrel_r.rotation.x = deg_to_rad(90)
|
||||
barrel_r.position = Vector3(0.04, 0.03, -0.1)
|
||||
add_child(barrel_r)
|
||||
model_root.add_child(barrel_r)
|
||||
|
||||
# Muzzle Flash Light
|
||||
muzzle_flash = OmniLight3D.new()
|
||||
@@ -62,7 +66,7 @@ func _build_model() -> void:
|
||||
muzzle_flash.light_energy = 0.0
|
||||
muzzle_flash.omni_range = 4.0
|
||||
muzzle_flash.position = Vector3(0, 0.03, -0.45)
|
||||
add_child(muzzle_flash)
|
||||
model_root.add_child(muzzle_flash)
|
||||
|
||||
# Audio Players
|
||||
fire_sound = AudioStreamPlayer3D.new()
|
||||
@@ -83,12 +87,12 @@ func _process(delta: float) -> void:
|
||||
|
||||
# Spin vertically (flip) over the duration of the reload
|
||||
var spin_speed = (PI * 2.0) / reload_time
|
||||
rotation.x += spin_speed * delta
|
||||
model_root.rotation.x += spin_speed * delta
|
||||
|
||||
if reload_timer <= 0.0:
|
||||
shells = 2
|
||||
reloading = false
|
||||
rotation.x = 0.0 # Snap back to perfectly level
|
||||
model_root.rotation.x = 0.0 # Snap back to perfectly level
|
||||
print("Shotgun Reloaded!")
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
|
||||
@@ -55,7 +55,7 @@ func _setup_viewmodel_viewport() -> void:
|
||||
dir_light.light_energy = 0.5
|
||||
add_child(dir_light)
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
func _process(_delta: float) -> void:
|
||||
if is_instance_valid(vm_camera) and is_instance_valid(camera):
|
||||
vm_camera.global_transform = camera.global_transform
|
||||
vm_camera.fov = camera.fov
|
||||
@@ -97,12 +97,18 @@ func _spawn_weapon(slot: int, weapon_id: String) -> void:
|
||||
weapons[slot] = w
|
||||
w.visible = false
|
||||
w.set_process_input(false)
|
||||
w.set_meta("script_path", script_path)
|
||||
|
||||
_add_procedural_arms(w)
|
||||
|
||||
# Set weapons to viewmodel layer
|
||||
_set_layer_recursive(w, 1 << 19)
|
||||
|
||||
func _set_layer_recursive(node: Node, layer_mask: int) -> void:
|
||||
if node is VisualInstance3D:
|
||||
if node is GeometryInstance3D:
|
||||
node.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
|
||||
if node is Light3D:
|
||||
# Lights (e.g. muzzle flash) illuminate both world and viewmodel
|
||||
node.layers = 1 | layer_mask
|
||||
@@ -112,6 +118,50 @@ func _set_layer_recursive(node: Node, layer_mask: int) -> void:
|
||||
for child in node.get_children():
|
||||
_set_layer_recursive(child, layer_mask)
|
||||
|
||||
func _add_procedural_arms(weapon: Node3D) -> void:
|
||||
# Attach to weapon instead of model_root to avoid arms spinning on reload
|
||||
|
||||
var mat = StandardMaterial3D.new()
|
||||
mat.albedo_color = Color(0.2, 0.4, 0.8) # Player color
|
||||
mat.roughness = 0.8
|
||||
|
||||
# Right Arm (Main Grip)
|
||||
var r_arm = MeshInstance3D.new()
|
||||
var r_mesh = BoxMesh.new()
|
||||
r_mesh.size = Vector3(0.08, 0.7, 0.08)
|
||||
r_mesh.material = mat
|
||||
r_arm.mesh = r_mesh
|
||||
|
||||
var r_pivot = Node3D.new()
|
||||
var r_shoulder = Vector3(0.25, -0.3, 0.5)
|
||||
var r_hand = Vector3(0.04, -0.05, 0.05)
|
||||
r_pivot.position = r_shoulder
|
||||
r_pivot.look_at_from_position(r_shoulder, r_hand, Vector3.UP)
|
||||
|
||||
r_arm.rotation_degrees.x = 90 # Mesh points UP by default, we need it to point forward along -Z
|
||||
r_arm.position.z = -0.35
|
||||
r_pivot.add_child(r_arm)
|
||||
weapon.add_child(r_pivot)
|
||||
|
||||
# Left Arm (Foregrip)
|
||||
if "weapon_name" in weapon and weapon.weapon_name != "Knife":
|
||||
var l_arm = MeshInstance3D.new()
|
||||
var l_mesh = BoxMesh.new()
|
||||
l_mesh.size = Vector3(0.08, 0.7, 0.08)
|
||||
l_mesh.material = mat
|
||||
l_arm.mesh = l_mesh
|
||||
|
||||
var l_pivot = Node3D.new()
|
||||
var l_shoulder = Vector3(-0.25, -0.3, 0.4)
|
||||
var l_hand = Vector3(-0.02, -0.02, -0.3)
|
||||
l_pivot.position = l_shoulder
|
||||
l_pivot.look_at_from_position(l_shoulder, l_hand, Vector3.UP)
|
||||
|
||||
l_arm.rotation_degrees.x = 90
|
||||
l_arm.position.z = -0.35
|
||||
l_pivot.add_child(l_arm)
|
||||
weapon.add_child(l_pivot)
|
||||
|
||||
func _equip_slot(slot: int) -> void:
|
||||
if weapons.has(active_slot):
|
||||
var w = weapons[active_slot]
|
||||
@@ -128,9 +178,12 @@ func _equip_slot(slot: int) -> void:
|
||||
var w = weapons[active_slot]
|
||||
w.visible = true
|
||||
w.set_process_input(true)
|
||||
print("Equipped slot ", slot)
|
||||
else:
|
||||
print("Slot ", slot, " is empty!")
|
||||
|
||||
# Sync 3rd person weapon
|
||||
if player and player.has_node("HumanoidModel"):
|
||||
var humanoid = player.get_node("HumanoidModel")
|
||||
if humanoid.has_method("set_weapon") and w.has_meta("script_path"):
|
||||
humanoid.set_weapon(w.get_meta("script_path"))
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if Input.get_mouse_mode() != Input.MOUSE_MODE_CAPTURED:
|
||||
|
||||
Reference in New Issue
Block a user