extends CharacterBody3D # We don't actually want to fully interpolate between states # since it may result in "jumpy" transitions const STATE_INTERP_MIN = 0.2 const STATE_INTERP_MAX = 0.8 var gravity = ProjectSettings.get_setting("physics/3d/default_gravity") var sync_state := PackedByteArray(): get = _get_sync_state, set = _set_sync_state @export var speed := 5.0 @export var jump_speed := 5.0 @onready var gametag := $Gametag @onready var target_origin := global_transform.origin var last_snapshot_tag := 0 var snapshot_interp_start := 0.0 var snapshot_interp_end := 0.0 func _process(_delta): gametag.text = "[%s %d]" % [name, get_multiplayer_authority()] func _physics_process(delta): if is_multiplayer_authority(): velocity.y += -gravity * delta var input := Input.get_vector("left", "right", "forward", "backward") var direction := transform.basis * Vector3(input.x, 0, input.y) velocity.x = direction.x * speed velocity.z = direction.z * speed move_and_slide() if is_on_floor() and Input.is_action_just_pressed("jump"): velocity.y = jump_speed else: var interp_value = remap_range( Time.get_ticks_msec(), snapshot_interp_start, snapshot_interp_end, STATE_INTERP_MIN, STATE_INTERP_MAX ) if interp_value >= 0.0 and interp_value <= 1.0: global_transform.origin = global_transform.origin.slerp( target_origin, interp_value ) else: global_transform.origin = target_origin func _enter_tree(): if name == str(multiplayer.get_unique_id()): set_multiplayer_authority(multiplayer.get_unique_id()) func _get_sync_state(): var encoder := PackedByteArray() encoder.resize(20) encoder.encode_u64(0, Time.get_ticks_msec()) encoder.encode_float(8, transform.origin.x) encoder.encode_float(12, transform.origin.y) encoder.encode_float(16, transform.origin.z) return encoder func _set_sync_state(value): assert( typeof(value) == TYPE_PACKED_BYTE_ARRAY and value.size() == 20, "Invalid `sync_state` array type or size (must be TYPE_RAW_ARRAY of size 20)" ) var time_tag: int = value.decode_u64(0) if time_tag > last_snapshot_tag: var snapshot_time_diff = time_tag - last_snapshot_tag if global_transform: global_transform.origin = target_origin # Override last origin snapshot_interp_start = Time.get_ticks_msec() snapshot_interp_end = Time.get_ticks_msec() + snapshot_time_diff last_snapshot_tag = time_tag target_origin = Vector3( value.decode_float(8), value.decode_float(12), value.decode_float(16), ) func remap_range(value, in_a, in_b, out_a, out_b): return (value - in_a) / (in_b - in_a) * (out_b - out_a) + out_a