extends CharacterBody3D @export var player_id := 69: set(id): player_id = id %Input.set_multiplayer_authority(id) @export var state_machine: Node @export_group("Camera") @export_range(0.0, 1.0) var mouse_sensitivity := 0.2 @export var camera_follow_speed := 20.0 @export var smooth_camera := false @export_group("Movement") @export var run_speed := 8.0 @export var walk_speed := 4.0 @export var acceleration := 45.0 @export var air_deceleration := 5.0 @export var rotation_speed := 20.0 @export_group("Client Smoothing") @export var client_smoothing := false @export var client_smoothing_speed := 10.0 @export var client_smoothing_rotation_speed := 25.0 @export var client_prediction := false @export var client_prediction_tolerance := 0.3 @export_group("Server variables") @export var server_position := Vector3.ZERO @export var server_rotation := Vector3.ZERO @export var player := true @export var dead := false @onready var _camera_pivot: Node3D = %CameraPivot @onready var _camera_platform: Node3D = %CameraPlatform @onready var _camera: Node3D = %Camera @onready var rotation_base: Node3D = %RotationBase @onready var collider: Node3D = %Collider var camera_input_direction := Vector2.ZERO var last_direction := Vector3.FORWARD var predicted_position := Vector3.ZERO var predicted_rotation := Vector3.ZERO func _ready() -> void: state_machine.set_subject(self) if multiplayer.is_server(): return if multiplayer.get_unique_id() == player_id: _camera.make_current() else: _camera.clear_current(false) func _input(event: InputEvent) -> void: if multiplayer.get_unique_id() != player_id: return if event.is_action_pressed("mouse_capture"): Input.mouse_mode = Input.MOUSE_MODE_CAPTURED elif event.is_action_pressed("mouse_release"): Input.mouse_mode = Input.MOUSE_MODE_VISIBLE if event is InputEventMouseMotion: if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED: camera_input_direction += event.screen_relative * mouse_sensitivity func _process(delta: float) -> void: if multiplayer.get_unique_id() != player_id: return _camera_platform.global_rotation = _camera_pivot.global_rotation if smooth_camera: _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: state_machine.process_physics(delta) if multiplayer.get_unique_id() == player_id: _camera_pivot.rotation.x = clamp( _camera_pivot.rotation.x - camera_input_direction.y * delta, -PI / 3.0, PI / 6.0, ) _camera_pivot.rotation.y -= camera_input_direction.x * delta camera_input_direction = Vector2.ZERO if not multiplayer.is_server(): if multiplayer.get_unique_id() == player_id and client_prediction: predicted_position = position predicted_rotation = rotation_base.global_rotation if multiplayer.get_unique_id() != player_id or not client_prediction: if client_smoothing: position = lerp(position, server_position, client_smoothing_speed * delta) rotation_base.global_rotation.x = server_rotation.x rotation_base.global_rotation.y = lerp_angle(rotation_base.global_rotation.y, server_rotation.y, client_smoothing_rotation_speed * delta) rotation_base.global_rotation.z = server_rotation.z else: position = server_position rotation_base.global_rotation = server_rotation # if multiplayer.is_server(): server_position = position server_rotation = rotation_base.global_rotation func _on_sync_delta_synchronized() -> void: if client_prediction and server_position.distance_to(predicted_position) > client_prediction_tolerance: print("%v VS %v" % [server_position, predicted_position]) position = server_position func apply_input_velocity(delta: float, input_direction: Vector2, speed: float, acc: float) -> void: var target_velocity = Vector3(input_direction.x * speed, velocity.y, input_direction.y * speed) velocity = velocity.move_toward(target_velocity, acc * delta) + Main.gravity_velocity move_and_slide() if input_direction.length() >= 0.1: last_direction = Vector3(input_direction.x, 0, input_direction.y) var target_angle := Vector3.FORWARD.signed_angle_to(last_direction, Vector3.UP) rotation_base.global_rotation.y = lerp_angle(rotation_base.rotation.y, target_angle, rotation_speed * delta)