Add multiplayer damage system and ragdolls

This commit is contained in:
2025-01-26 14:39:11 +01:00
parent 7da4a3d6bb
commit 06a1d28e42
53 changed files with 3969 additions and 61 deletions

View File

@ -3,7 +3,8 @@
# ---------------------------------------------------------------------
class_name PlayerInput extends CharacterBody3D
var health: int = 100
@export var health: int = 100
@export_category("PlayerInput")
@ -16,6 +17,7 @@ var mouse_captured: bool = false
@onready var first_view_camera_mount: Node3D = $FirstPersonCameraMount
@onready var first_view_camera: Camera3D = $FirstPersonCameraMount/Camera
@onready var model_mount: Node3D = $ModelMount
var model: Node3D = null
# -- This node is supposed to be spawned per player, and since each
# -- player has an id, it is used for giving a node a name. So we can
@ -38,32 +40,59 @@ func _ready() -> void:
var err := _add_first_view_model()
if err != OK:
print("Error occured: " + str(err))
_enable_camera()
else:
var err := _add_world_model()
err = _add_legs_to_first_view()
if err != OK:
print("Error occured: " + str(err))
var world_model := _add_world_model()
if world_model == null:
print("Error occured: " + "couldn't load the world model")
_enable_camera()
else:
var world_model := _add_world_model()
if world_model == null:
print("Error occured: " + "couldn't load the world model")
_hide_camera_mount()
_capture_mouse()
# -- Add a world model to the player, that should be seen by other players
# -- on the server
func _add_world_model() -> Error :
func _add_world_model() -> Node3D :
# -- TODO: It should not be hardcoded
var path := "res://scenes/characters/blue/dummy.tscn"
if not ResourceLoader.exists(path):
return ERR_DOES_NOT_EXIST
push_error(ERR_DOES_NOT_EXIST)
return null
var scene: PackedScene = ResourceLoader.load(path)
if not scene.can_instantiate():
return ERR_CANT_OPEN
push_error(ERR_CANT_OPEN)
return null
var node: Node3D = scene.instantiate()
model = node
model_mount.add_child(node)
return OK
if _is_current_player():
node.make_invisible()
first_view_camera.cull_mask &= ~(1 << 1)
return node
func _hide_camera_mount():
first_view_camera_mount.visible = 0
hud.visible = 0
func make_node_invisible_for_camera(node: Node3D, camera: Camera3D):
if node and camera:
# Set the node to Layer 2 (or any other layer you want)
node.visibility_layer = 1 << 1 # Assign the node to Layer 2
# Disable Layer 2 on the camera's culling mask (this makes it invisible to this camera)
camera.cull_mask &= ~(1 << 1) # Disable Layer 2 on this camera
var current_gun: String = "ak"
@onready var gun_mount: Node3D = $FirstPersonCameraMount/GunMount
@onready var gun_mount_anim: AnimationPlayer = $FirstPersonCameraMount/GunMount/AnimationPlayer
var gun_with_hands: Node3D = null
# -- Add the first person view to pthe player
func _add_first_view_model() -> Error :
# -- TODO: It should not be hardcoded
@ -81,6 +110,30 @@ func _add_first_view_model() -> Error :
gun_with_hands = node
gun_mount.add_child(node)
return OK
var first_view_legs_anim: AnimationPlayer = null
# -- Add a world model to the player, that should be seen by other players
# -- on the server
func _add_legs_to_first_view() -> Error :
# -- TODO: It should not be hardcoded
var path := "res://scenes/characters/blue/dummy.tscn"
if not ResourceLoader.exists(path):
return ERR_DOES_NOT_EXIST
var scene: PackedScene = ResourceLoader.load(path)
if not scene.can_instantiate():
return ERR_CANT_OPEN
var node: Node3D = scene.instantiate()
var skeleton: Skeleton3D = node.find_child("Skeleton3D")
var bone := skeleton.find_bone("mixamorig_Spine")
if bone != -1:
skeleton.set_bone_pose_scale(bone, Vector3(0, 0, 0))
model_mount.add_child(node)
var animation_node: AnimationPlayer = node.find_child("AnimationPlayer")
if animation_node != null:
first_view_legs_anim = animation_node
return OK
func _enable_camera():
first_view_camera.make_current()
@ -130,6 +183,23 @@ func _rotate_camera(sens_mod: float = 1.0) -> void:
rotation.y -= look_dir.x * camera_sens * sens_mod
first_view_camera_mount.rotation.x = clamp(first_view_camera_mount.rotation.x - look_dir.y * camera_sens * sens_mod, -1.5, 1.5)
@onready var hud = $FirstPersonCameraMount/HUD
@onready var health_indicator = $FirstPersonCameraMount/HUD/HealthIndicator
@onready var fps_indicator = $FirstPersonCameraMount/HUD/FPS
func _process(delta: float) -> void:
health_indicator.text = str(health)
fps_indicator.text = str(Engine.get_frames_per_second())
if health == 0:
alive = false
var world: Node3D = find_parent("ElTest")
var spawner: MultiplayerSpawner = world.find_child("ObjectsSpawner")
var objects_root: Node3D = world.find_child("Objects")
spawner.spawn(model)
model.reparent(get_tree().get_root())
model.die()
queue_free()
func _physics_process(delta: float) -> void:
# Add the gravity.
if not is_on_floor():
@ -148,15 +218,19 @@ func _physics_process(delta: float) -> void:
var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
if direction:
gun_mount_anim.play("move")
if first_view_legs_anim != null:
first_view_legs_anim.play("Run Forward")
velocity.x = direction.x * SPEED
velocity.z = direction.z * SPEED
else:
if gun_mount_anim.is_playing():
gun_mount_anim.stop()
if first_view_legs_anim != null and first_view_legs_anim.is_playing():
first_view_legs_anim.stop()
velocity.x = move_toward(velocity.x, 0, SPEED)
velocity.z = move_toward(velocity.z, 0, SPEED)
move_and_slide()
if alive:
move_and_slide()
# -- TODO: It shouldn't be hardcoded
var bullet = load("res://scenes/weapon/bullet.tscn")
@ -164,14 +238,28 @@ var bullet = load("res://scenes/weapon/bullet.tscn")
@onready var bullet_starting_point: Node3D = $FirstPersonCameraMount/BulletStartingPoint
@onready var aim_ray: RayCast3D = $FirstPersonCameraMount/BulletStartingPoint/AimRay
# --find the gun node and exec shoot
var cant_shoot: bool = false
func _shoot():
if aim_ray.is_colliding():
var collider := aim_ray.get_collider()
if collider != null and collider.is_in_group("target"):
aim_ray.get_collider().take_damage()
var root := get_tree().get_root()
gun_with_hands.shoot()
if not cant_shoot:
_send_shot_to_server.rpc_id(1, aim_ray.global_position)
if aim_ray.is_colliding():
var collider := aim_ray.get_collider()
if collider != null and collider.is_in_group("target"):
aim_ray.get_collider().take_damage()
if collider != null and collider.is_in_group("body"):
collider.hit()
var root := get_tree().get_root()
gun_with_hands.shoot()
cant_shoot = true
await get_tree().create_timer(0.2).timeout
cant_shoot = false
@rpc("any_peer", "call_local", "unreliable_ordered")
func _send_shot_to_server(start_position):
# -- TODO: Should not be hardcoded
var world: Node3D = find_parent("ElTest")
world.spawn_bullet(bullet_starting_point)
func _get_camera_collision():
var viewport = get_viewport().size
var ray_origin = first_view_camera.project_ray_origin(viewport / 2)
@ -198,3 +286,13 @@ func _hit_scan_collision(collision_point):
func _hit_scan_damage(Collider):
#if Collider.is_in_group("target") and Collider.has_method("take_damage"):
print("damaged")
func take_damage(dam: int):
var new_health = health - dam
set_health(new_health)
if multiplayer.is_server():
set_health.rpc(new_health)
@rpc("call_local", "reliable")
func set_health(val: int):
health = val