despawn runner or chaser on disconnect

This commit is contained in:
2025-02-21 17:59:23 +09:00
parent 591225996d
commit ca63d4594c
15 changed files with 85 additions and 97 deletions
+9 -1
View File
@@ -77,5 +77,13 @@ walk={
rendering_device/fallback_to_vulkan=false rendering_device/fallback_to_vulkan=false
rendering_device/fallback_to_d3d12=false rendering_device/fallback_to_d3d12=false
renderer/rendering_method="mobile" lights_and_shadows/directional_shadow/size.mobile=4096
lights_and_shadows/directional_shadow/soft_shadow_filter_quality=3
lights_and_shadows/directional_shadow/soft_shadow_filter_quality.mobile=3
lights_and_shadows/positional_shadow/soft_shadow_filter_quality=3
lights_and_shadows/positional_shadow/soft_shadow_filter_quality.mobile=3
environment/defaults/default_clear_color=Color(0, 0, 0, 1) environment/defaults/default_clear_color=Color(0, 0, 0, 1)
anti_aliasing/quality/msaa_2d=1
anti_aliasing/quality/msaa_3d=1
anti_aliasing/quality/use_debanding=true
lights_and_shadows/positional_shadow/atlas_size.mobile=4096
+1 -1
View File
@@ -44,7 +44,7 @@ height = 1.8
[sub_resource type="SphereShape3D" id="SphereShape3D_wsx1k"] [sub_resource type="SphereShape3D" id="SphereShape3D_wsx1k"]
[node name="Runner" type="CharacterBody3D" node_paths=PackedStringArray("state_machine")] [node name="Chaser" type="CharacterBody3D" node_paths=PackedStringArray("state_machine")]
script = ExtResource("1_hjhpa") script = ExtResource("1_hjhpa")
state_machine = NodePath("StateMachine") state_machine = NodePath("StateMachine")
+2 -2
View File
@@ -143,12 +143,12 @@ mesh = SubResource("BoxMesh_lm401")
[node name="FirstFloorLight" type="OmniLight3D" parent="Lights"] [node name="FirstFloorLight" type="OmniLight3D" parent="Lights"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 4, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 4, 0)
light_energy = 0.5
shadow_enabled = true shadow_enabled = true
omni_range = 40.0 omni_range = 40.0
omni_attenuation = 1.5
[node name="SecondFloorLight" type="OmniLight3D" parent="Lights"] [node name="SecondFloorLight" type="OmniLight3D" parent="Lights"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 9, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 9, 0)
light_energy = 0.5
shadow_enabled = true shadow_enabled = true
omni_range = 40.0 omni_range = 40.0
omni_attenuation = 1.5
+5
View File
@@ -10,9 +10,14 @@ background_mode = 1
ambient_light_source = 2 ambient_light_source = 2
ambient_light_color = Color(1, 1, 1, 1) ambient_light_color = Color(1, 1, 1, 1)
ambient_light_energy = 0.01 ambient_light_energy = 0.01
tonemap_mode = 3
sdfgi_read_sky_light = false sdfgi_read_sky_light = false
glow_enabled = true glow_enabled = true
glow_normalized = true glow_normalized = true
volumetric_fog_enabled = true
volumetric_fog_albedo = Color(0.381703, 0.381703, 0.381703, 1)
volumetric_fog_emission = Color(0.314009, 2.89988e-06, 1.52815e-06, 1)
volumetric_fog_emission_energy = 0.25
[sub_resource type="WorldBoundaryShape3D" id="WorldBoundaryShape3D_1l61b"] [sub_resource type="WorldBoundaryShape3D" id="WorldBoundaryShape3D_1l61b"]
+11 -5
View File
@@ -15,18 +15,24 @@ properties/0/spawn = true
properties/0/replication_mode = 2 properties/0/replication_mode = 2
properties/1/path = NodePath(".:server_position") properties/1/path = NodePath(".:server_position")
properties/1/spawn = true properties/1/spawn = true
properties/1/replication_mode = 2 properties/1/replication_mode = 1
properties/2/path = NodePath(".:server_rotation") properties/2/path = NodePath(".:server_rotation")
properties/2/spawn = true properties/2/spawn = true
properties/2/replication_mode = 2 properties/2/replication_mode = 1
[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_1agtp"] [sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_1agtp"]
properties/0/path = NodePath("Input:direction") properties/0/path = NodePath("Input:direction")
properties/0/spawn = true properties/0/spawn = true
properties/0/replication_mode = 2 properties/0/replication_mode = 1
properties/1/path = NodePath("Input:walking") properties/1/path = NodePath("Input:walk")
properties/1/spawn = true properties/1/spawn = true
properties/1/replication_mode = 2 properties/1/replication_mode = 1
properties/2/path = NodePath("Input:primary_interact")
properties/2/spawn = true
properties/2/replication_mode = 1
properties/3/path = NodePath("Input:secondary_interact")
properties/3/spawn = true
properties/3/replication_mode = 1
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_j6tb3"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_j6tb3"]
radius = 0.3 radius = 0.3
+15 -7
View File
@@ -9,16 +9,24 @@ var _ping := 0.0
@onready var _runners_node: Node3D = %Runners @onready var _runners_node: Node3D = %Runners
func spawn_player(player_id: int, runner: bool) -> void: func spawn_player(player_id: int, runner: bool) -> void:
if multiplayer.is_server(): if not multiplayer.is_server(): return
var player = runner_scene.instantiate() if runner else chaser_scene.instantiate()
player.player_id = player_id
player.name = str(player_id)
_runners_node.add_child(player) if runner else _chasers_node.add_child(player) var player = runner_scene.instantiate() if runner else chaser_scene.instantiate()
player.player_id = player_id
player.name = str(player_id)
_runners_node.add_child(player) if runner else _chasers_node.add_child(player)
func despawn_player(player_id: int) -> void:
if not multiplayer.is_server(): return
var player_id_str := str(player_id)
for runner_or_chaser in _runners_node.get_children() + _chasers_node.get_children():
if runner_or_chaser.name == player_id_str:
runner_or_chaser.queue_free()
func _process(_delta: float) -> void: func _process(_delta: float) -> void:
if DisplayServer.get_name() == "headless": if DisplayServer.get_name() == "headless": return
return
%StatsLabel.text = statsTemplate % [ %StatsLabel.text = statsTemplate % [
Engine.get_frames_per_second(), Engine.get_frames_per_second(),
+3
View File
@@ -5,3 +5,6 @@ var gravity_vector: Vector3 = ProjectSettings.get_setting("physics/3d/default_gr
var physics_tickrate = Engine.get_physics_ticks_per_second() var physics_tickrate = Engine.get_physics_ticks_per_second()
var terminal_velocity: float = 150.0 var terminal_velocity: float = 150.0
var gravity_velocity: Vector3 = (gravity_vector * gravity) / (physics_tickrate as float) var gravity_velocity: Vector3 = (gravity_vector * gravity) / (physics_tickrate as float)
func is_server_or_predicting(player_id: int, client_prediction: bool = false):
return multiplayer.is_server() or (multiplayer.get_unique_id() == player_id and client_prediction)
+4 -3
View File
@@ -1,7 +1,9 @@
extends MultiplayerSynchronizer extends MultiplayerSynchronizer
@export var direction: Vector2 = Vector2.ZERO @export var direction: Vector2 = Vector2.ZERO
@export var walking: bool = false @export var walk: bool = false
@export var primary_interact: bool = false
@export var secondary_interact: bool = false
@onready var _camera_pivot: Node3D = %CameraPivot @onready var _camera_pivot: Node3D = %CameraPivot
@@ -9,10 +11,9 @@ func _ready() -> void:
if multiplayer.is_server() or get_multiplayer_authority() != multiplayer.get_unique_id(): if multiplayer.is_server() or get_multiplayer_authority() != multiplayer.get_unique_id():
set_process(false) set_process(false)
set_physics_process(false) set_physics_process(false)
return
func _physics_process(_delta: float) -> void: func _physics_process(_delta: float) -> void:
var directional_input := Input.get_vector("move_right", "move_left", "move_forward", "move_back") var directional_input := Input.get_vector("move_right", "move_left", "move_forward", "move_back")
var camera_adjusted: Vector3 = (directional_input.x * _camera_pivot.global_basis.z) + (directional_input.y * _camera_pivot.global_basis.x) var camera_adjusted: Vector3 = (directional_input.x * _camera_pivot.global_basis.z) + (directional_input.y * _camera_pivot.global_basis.x)
direction = Vector2(camera_adjusted.x, camera_adjusted.z).rotated(PI / 2.0).normalized() direction = Vector2(camera_adjusted.x, camera_adjusted.z).rotated(PI / 2.0).normalized()
walking = Input.is_action_pressed("walk") walk = Input.is_action_pressed("walk")
+5 -8
View File
@@ -26,8 +26,7 @@ func _ready() -> void:
get_tree().change_scene_to_packed.call_deferred(main_menu_scene) get_tree().change_scene_to_packed.call_deferred(main_menu_scene)
func connect_to_ip(runner: bool = true, ip: String = "") -> void: func connect_to_ip(runner: bool = true, ip: String = "") -> void:
if ip.is_empty(): if ip.is_empty(): ip = IP_ADDRESS
ip = IP_ADDRESS
runner_client_selection = runner runner_client_selection = runner
@@ -46,25 +45,23 @@ func _on_connect(id: int) -> void:
func _on_disconnect(id: int) -> void: func _on_disconnect(id: int) -> void:
print("Client ID #%s disconnected" % id) print("Client ID #%s disconnected" % id)
pass if not multiplayer.is_server(): return
get_tree().get_current_scene().despawn_player(id)
func _on_connect_client() -> void: func _on_connect_client() -> void:
request_is_runner.rpc_id(1, runner_client_selection) request_is_runner.rpc_id(1, runner_client_selection)
print("[%s] Connected to server" % multiplayer.get_unique_id()) print("[%s] Connected to server" % multiplayer.get_unique_id())
pass
func _on_disconnect_client() -> void: func _on_disconnect_client() -> void:
print("[%s] Disconnected" % multiplayer.get_unique_id()) print("[%s] Disconnected" % multiplayer.get_unique_id())
pass
func _on_server_closed_client() -> void: func _on_server_closed_client() -> void:
print("[%s] Server closed" % multiplayer.get_unique_id()) print("[%s] Server closed" % multiplayer.get_unique_id())
pass
@rpc("any_peer", "call_remote", "reliable") @rpc("any_peer", "call_remote", "reliable")
func request_is_runner(runner: bool) -> void: func request_is_runner(runner: bool) -> void:
if not multiplayer.is_server(): if not multiplayer.is_server(): return
return
var id := multiplayer.get_remote_sender_id() var id := multiplayer.get_remote_sender_id()
runner_dict[id] = runner runner_dict[id] = runner
+9 -11
View File
@@ -43,8 +43,7 @@ var predicted_rotation := Vector3.ZERO
func _ready() -> void: func _ready() -> void:
state_machine.set_subject(self) state_machine.set_subject(self)
if multiplayer.is_server(): if multiplayer.is_server(): return
return
if multiplayer.get_unique_id() == player_id: if multiplayer.get_unique_id() == player_id:
%Camera.make_current() %Camera.make_current()
@@ -52,8 +51,7 @@ func _ready() -> void:
%Camera.clear_current(false) %Camera.clear_current(false)
func _input(event: InputEvent) -> void: func _input(event: InputEvent) -> void:
if multiplayer.get_unique_id() != player_id: if multiplayer.get_unique_id() != player_id: return
return
if event.is_action_pressed("mouse_capture"): if event.is_action_pressed("mouse_capture"):
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
@@ -65,13 +63,14 @@ func _input(event: InputEvent) -> void:
camera_input_direction += event.screen_relative * mouse_sensitivity camera_input_direction += event.screen_relative * mouse_sensitivity
func _process(delta: float) -> void: func _process(delta: float) -> void:
if multiplayer.get_unique_id() == player_id: if multiplayer.get_unique_id() != player_id: return
_camera_platform.global_rotation = _camera_pivot.global_rotation
if smooth_camera: _camera_platform.global_rotation = _camera_pivot.global_rotation
_camera_platform.global_position = lerp(_camera_platform.global_position, _camera_pivot.global_position, camera_follow_speed * delta)
else: if smooth_camera:
_camera_platform.global_position = _camera_pivot.global_position _camera_platform.global_position = lerp(_camera_platform.global_position, _camera_pivot.global_position, camera_follow_speed * delta)
else:
_camera_platform.global_position = _camera_pivot.global_position
func _physics_process(delta: float) -> void: func _physics_process(delta: float) -> void:
state_machine.process_physics(delta) state_machine.process_physics(delta)
@@ -100,7 +99,6 @@ func _physics_process(delta: float) -> void:
if multiplayer.is_server(): if multiplayer.is_server():
server_position = position server_position = position
server_rotation = skin.global_rotation server_rotation = skin.global_rotation
pass
func _on_sync_delta_synchronized() -> void: func _on_sync_delta_synchronized() -> void:
if client_prediction and server_position.distance_to(predicted_position) > client_prediction_tolerance: if client_prediction and server_position.distance_to(predicted_position) > client_prediction_tolerance:
+5 -13
View File
@@ -1,22 +1,14 @@
extends State extends State
func process_physics(delta: float) -> State: func process_physics(delta: float) -> State:
if not multiplayer.is_server() and (multiplayer.get_unique_id() != subject.player_id or not subject.client_prediction): if not Main.is_server_or_predicting(subject.player_id, subject.client_prediction): return
return
var target_velocity = Vector3(0, subject.velocity.y, 0) var target_velocity = Vector3(0, subject.velocity.y, 0)
subject.velocity = subject.velocity.move_toward(target_velocity, subject.air_deceleration * delta) subject.velocity = subject.velocity.move_toward(target_velocity, subject.air_deceleration * delta)
subject.velocity += Main.gravity_velocity subject.velocity += Main.gravity_velocity
subject.move_and_slide() subject.move_and_slide()
if subject.is_on_floor(): if not subject.is_on_floor(): return
var input_direction = %Input.direction if %Input.direction.length() < 0.05: return %Idle
if input_direction.length() < 0.05: if %Input.walk: return %Walk
return %Idle return %Run
if %Input.walking:
return %Walk
return %Run
return
+3 -6
View File
@@ -1,19 +1,16 @@
extends State extends State
func process_physics(delta: float) -> State: func process_physics(delta: float) -> State:
if not multiplayer.is_server() and (multiplayer.get_unique_id() != subject.player_id or not subject.client_prediction): if not Main.is_server_or_predicting(subject.player_id, subject.client_prediction): return
return
var target_velocity = Vector3(0, subject.velocity.y, 0) var target_velocity = Vector3(0, subject.velocity.y, 0)
subject.velocity = subject.velocity.move_toward(target_velocity, subject.acceleration * delta) subject.velocity = subject.velocity.move_toward(target_velocity, subject.acceleration * delta)
subject.velocity += Main.gravity_velocity subject.velocity += Main.gravity_velocity
subject.move_and_slide() subject.move_and_slide()
if not subject.is_on_floor(): if not subject.is_on_floor(): return %Fall
return %Fall
var input_direction = %Input.direction var input_direction = %Input.direction
if input_direction.length() < 0.05: if input_direction.length() < 0.05: return
return
return %Run return %Run
+4 -10
View File
@@ -1,12 +1,10 @@
extends State extends State
func process_physics(delta: float) -> State: func process_physics(delta: float) -> State:
if not multiplayer.is_server() and (multiplayer.get_unique_id() != subject.player_id or not subject.client_prediction): if not Main.is_server_or_predicting(subject.player_id, subject.client_prediction): return
return
var input_direction = %Input.direction var input_direction = %Input.direction
if input_direction.length() < 0.05: if input_direction.length() < 0.05: return %Idle
return %Idle
var move_direction = Vector3(input_direction.x, 0, input_direction.y) var move_direction = Vector3(input_direction.x, 0, input_direction.y)
var target_velocity = Vector3(move_direction.x * subject.run_speed, subject.velocity.y, move_direction.z * subject.run_speed) var target_velocity = Vector3(move_direction.x * subject.run_speed, subject.velocity.y, move_direction.z * subject.run_speed)
@@ -19,10 +17,6 @@ func process_physics(delta: float) -> State:
var target_angle := Vector3.FORWARD.signed_angle_to(subject.last_direction, Vector3.UP) var target_angle := Vector3.FORWARD.signed_angle_to(subject.last_direction, Vector3.UP)
subject.skin.global_rotation.y = lerp_angle(subject.skin.rotation.y, target_angle, subject.rotation_speed * delta) subject.skin.global_rotation.y = lerp_angle(subject.skin.rotation.y, target_angle, subject.rotation_speed * delta)
if %Input.walking: if %Input.walk: return %Walk
return %Walk if not subject.is_on_floor(): return %Fall
if not subject.is_on_floor():
return %Fall
return return
+4 -19
View File
@@ -9,42 +9,27 @@ func set_subject(new_subject: Node) -> void:
state.subject = subject state.subject = subject
func _change_state(new_state: State) -> void: func _change_state(new_state: State) -> void:
if current_state: if current_state: current_state.exit()
#print("Exiting: %s" % current_state.name)
current_state.exit()
current_state = new_state current_state = new_state
#print("Entering: %s" % current_state.name)
current_state.enter() current_state.enter()
func process_physics(delta: float) -> void: func process_physics(delta: float) -> void:
if not current_state: if not current_state or not current_state.subject: return
return
if not current_state.subject:
return
var new_state = current_state.process_physics(delta) var new_state = current_state.process_physics(delta)
if new_state: if new_state:
_change_state(new_state) _change_state(new_state)
func process_input(event: InputEvent) -> void: func process_input(event: InputEvent) -> void:
if not current_state: if not current_state or not current_state.subject: return
return
if not current_state.subject:
return
var new_state = current_state.process_input(event) var new_state = current_state.process_input(event)
if new_state: if new_state:
_change_state(new_state) _change_state(new_state)
func process_frame(delta: float) -> void: func process_frame(delta: float) -> void:
if not current_state: if not current_state or not current_state.subject: return
return
if not current_state.subject:
return
var new_state = current_state.process_frame(delta) var new_state = current_state.process_frame(delta)
if new_state: if new_state:
+4 -10
View File
@@ -1,12 +1,10 @@
extends State extends State
func process_physics(delta: float) -> State: func process_physics(delta: float) -> State:
if not multiplayer.is_server() and (multiplayer.get_unique_id() != subject.player_id or not subject.client_prediction): if not Main.is_server_or_predicting(subject.player_id, subject.client_prediction): return
return
var input_direction = %Input.direction var input_direction = %Input.direction
if input_direction.length() < 0.05: if input_direction.length() < 0.05: return %Idle
return %Idle
var move_direction = Vector3(input_direction.x, 0, input_direction.y) var move_direction = Vector3(input_direction.x, 0, input_direction.y)
var target_velocity = Vector3(move_direction.x * subject.walk_speed, subject.velocity.y, move_direction.z * subject.walk_speed) var target_velocity = Vector3(move_direction.x * subject.walk_speed, subject.velocity.y, move_direction.z * subject.walk_speed)
@@ -19,10 +17,6 @@ func process_physics(delta: float) -> State:
var target_angle := Vector3.FORWARD.signed_angle_to(subject.last_direction, Vector3.UP) var target_angle := Vector3.FORWARD.signed_angle_to(subject.last_direction, Vector3.UP)
subject.skin.global_rotation.y = lerp_angle(subject.skin.rotation.y, target_angle, subject.rotation_speed * delta) subject.skin.global_rotation.y = lerp_angle(subject.skin.rotation.y, target_angle, subject.rotation_speed * delta)
if not %Input.walking: if not %Input.walk: return %Run
return %Run if not subject.is_on_floor(): return %Fall
if not subject.is_on_floor():
return %Fall
return return