mirror of
https://github.com/thegatesbrowser/godot-multiplayer.git
synced 2025-09-22 11:26:56 -04:00
copy from thegates-jam repo
This commit is contained in:
parent
c1a7ad74e1
commit
1a335de566
523 changed files with 22408 additions and 0 deletions
191
player/player.gd
Normal file
191
player/player.gd
Normal file
|
@ -0,0 +1,191 @@
|
|||
extends CharacterBody3D
|
||||
class_name Player
|
||||
|
||||
## Character maximum run speed on the ground.
|
||||
@export var move_speed := 8.0
|
||||
## Forward impulse after a melee attack.
|
||||
@export var attack_impulse := 10.0
|
||||
## Movement acceleration (how fast character achieve maximum speed)
|
||||
@export var acceleration := 6.0
|
||||
## Jump impulse
|
||||
@export var jump_initial_impulse := 12.0
|
||||
## Jump impulse when player keeps pressing jump
|
||||
@export var jump_additional_force := 4.5
|
||||
## Player model rotation speed
|
||||
@export var rotation_speed := 12.0
|
||||
## Minimum horizontal speed on the ground. This controls when the character's animation tree changes
|
||||
## between the idle and running states.
|
||||
@export var stopping_speed := 1.0
|
||||
## Clamp sync delta for faster interpolation
|
||||
@export var sync_delta_max := 0.2
|
||||
|
||||
@onready var _rotation_root: Node3D = $CharacterRotationRoot
|
||||
@onready var _camera_controller: CameraController = $CameraController
|
||||
@onready var _ground_shapecast: ShapeCast3D = $GroundShapeCast
|
||||
@onready var _character_skin: CharacterSkin = $CharacterRotationRoot/CharacterSkin
|
||||
@onready var _synchronizer: MultiplayerSynchronizer = $MultiplayerSynchronizer
|
||||
|
||||
@onready var _move_direction := Vector3.ZERO
|
||||
@onready var _last_strong_direction := Vector3.FORWARD
|
||||
@onready var _gravity: float = -30.0
|
||||
@onready var _ground_height: float = 0.0
|
||||
|
||||
## Sync properties
|
||||
@export var _position: Vector3
|
||||
@export var _velocity: Vector3
|
||||
@export var _direction: Vector3 = Vector3.ZERO
|
||||
@export var _strong_direction: Vector3 = Vector3.FORWARD
|
||||
|
||||
var position_before_sync: Vector3
|
||||
|
||||
var last_sync_time_ms: int
|
||||
var sync_delta: float
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if is_multiplayer_authority():
|
||||
_camera_controller.setup(self)
|
||||
else:
|
||||
rotation_speed /= 1.5
|
||||
_synchronizer.delta_synchronized.connect(on_synchronized)
|
||||
_synchronizer.synchronized.connect(on_synchronized)
|
||||
on_synchronized()
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if not is_multiplayer_authority(): interpolate_client(delta); return
|
||||
|
||||
# Calculate ground height for camera controller
|
||||
if _ground_shapecast.get_collision_count() > 0:
|
||||
for collision_result in _ground_shapecast.collision_result:
|
||||
_ground_height = max(_ground_height, collision_result.point.y)
|
||||
else:
|
||||
_ground_height = global_position.y + _ground_shapecast.target_position.y
|
||||
if global_position.y < _ground_height:
|
||||
_ground_height = global_position.y
|
||||
|
||||
# Get input and movement state
|
||||
var is_just_jumping := Input.is_action_just_pressed("jump") and is_on_floor()
|
||||
var is_air_boosting := Input.is_action_pressed("jump") and not is_on_floor() and velocity.y > 0.0
|
||||
|
||||
_move_direction = _get_camera_oriented_input()
|
||||
|
||||
if EditMode.is_enabled:
|
||||
is_just_jumping = false
|
||||
is_air_boosting = false
|
||||
_move_direction = Vector3.ZERO
|
||||
|
||||
# To not orient quickly to the last input, we save a last strong direction,
|
||||
# this also ensures a good normalized value for the rotation basis.
|
||||
if _move_direction.length() > 0.2:
|
||||
_last_strong_direction = _move_direction.normalized()
|
||||
|
||||
_orient_character_to_direction(_last_strong_direction, delta)
|
||||
|
||||
# We separate out the y velocity to not interpolate on the gravity
|
||||
var y_velocity := velocity.y
|
||||
velocity.y = 0.0
|
||||
velocity = velocity.lerp(_move_direction * move_speed, acceleration * delta)
|
||||
if _move_direction.length() == 0 and velocity.length() < stopping_speed:
|
||||
velocity = Vector3.ZERO
|
||||
velocity.y = y_velocity
|
||||
|
||||
# Update position
|
||||
|
||||
velocity.y += _gravity * delta
|
||||
|
||||
if is_just_jumping:
|
||||
velocity.y += jump_initial_impulse
|
||||
elif is_air_boosting:
|
||||
velocity.y += jump_additional_force * delta
|
||||
|
||||
# Set character animation
|
||||
if is_just_jumping:
|
||||
_character_skin.jump.rpc()
|
||||
elif not is_on_floor() and velocity.y < 0:
|
||||
_character_skin.fall.rpc()
|
||||
elif is_on_floor():
|
||||
var xz_velocity := Vector3(velocity.x, 0, velocity.z)
|
||||
if xz_velocity.length() > stopping_speed:
|
||||
_character_skin.set_moving.rpc(true)
|
||||
_character_skin.set_moving_speed.rpc(inverse_lerp(0.0, move_speed, xz_velocity.length()))
|
||||
else:
|
||||
_character_skin.set_moving.rpc(false)
|
||||
|
||||
var position_before := global_position
|
||||
move_and_slide()
|
||||
var position_after := global_position
|
||||
|
||||
# If velocity is not 0 but the difference of positions after move_and_slide is,
|
||||
# character might be stuck somewhere!
|
||||
var delta_position := position_after - position_before
|
||||
var epsilon := 0.001
|
||||
if delta_position.length() < epsilon and velocity.length() > epsilon:
|
||||
global_position += get_wall_normal() * 0.1
|
||||
|
||||
set_sync_properties()
|
||||
|
||||
|
||||
func set_sync_properties() -> void:
|
||||
_position = position
|
||||
_velocity = velocity
|
||||
_direction = _move_direction
|
||||
_strong_direction = _last_strong_direction
|
||||
|
||||
|
||||
func on_synchronized() -> void:
|
||||
velocity = _velocity
|
||||
position_before_sync = position
|
||||
|
||||
var sync_time_ms = Time.get_ticks_msec()
|
||||
sync_delta = clampf(float(sync_time_ms - last_sync_time_ms) / 1000, 0, sync_delta_max)
|
||||
last_sync_time_ms = sync_time_ms
|
||||
|
||||
|
||||
func interpolate_client(delta: float) -> void:
|
||||
_orient_character_to_direction(_strong_direction, delta)
|
||||
|
||||
if _direction.length() == 0:
|
||||
# Don't interpolate to avoid small jitter when stopping
|
||||
if (_position - position).length() > 1.0 and _velocity.is_zero_approx():
|
||||
position = _position # Fix misplacement
|
||||
else:
|
||||
# Interpolate between position_before_sync and _position
|
||||
# and add to ongoing movement to compensate misplacement
|
||||
var t = 1.0 if is_zero_approx(sync_delta) else delta / sync_delta
|
||||
sync_delta = clampf(sync_delta - delta, 0, sync_delta_max)
|
||||
|
||||
var less_misplacement = position_before_sync.move_toward(_position, t)
|
||||
position += less_misplacement - position_before_sync
|
||||
position_before_sync = less_misplacement
|
||||
|
||||
velocity.y += _gravity * delta
|
||||
move_and_slide()
|
||||
|
||||
|
||||
func _get_camera_oriented_input() -> Vector3:
|
||||
var raw_input := Input.get_vector("move_left", "move_right", "move_up", "move_down")
|
||||
|
||||
var input := Vector3.ZERO
|
||||
# This is to ensure that diagonal input isn't stronger than axis aligned input
|
||||
input.x = -raw_input.x * sqrt(1.0 - raw_input.y * raw_input.y / 2.0)
|
||||
input.z = -raw_input.y * sqrt(1.0 - raw_input.x * raw_input.x / 2.0)
|
||||
|
||||
input = _camera_controller.global_transform.basis * input
|
||||
input.y = 0.0
|
||||
return input
|
||||
|
||||
|
||||
func _orient_character_to_direction(direction: Vector3, delta: float) -> void:
|
||||
var left_axis := Vector3.UP.cross(direction)
|
||||
var rotation_basis := Basis(left_axis, Vector3.UP, direction).get_rotation_quaternion()
|
||||
var model_scale := _rotation_root.transform.basis.get_scale()
|
||||
_rotation_root.transform.basis = Basis(_rotation_root.transform.basis.get_rotation_quaternion().slerp(rotation_basis, delta * rotation_speed)).scaled(
|
||||
model_scale
|
||||
)
|
||||
|
||||
|
||||
@rpc("any_peer", "call_remote", "reliable")
|
||||
func respawn(spawn_position: Vector3) -> void:
|
||||
global_position = spawn_position
|
||||
velocity = Vector3.ZERO
|
Loading…
Add table
Add a link
Reference in a new issue