Godot Version
4.7
Question
I am new to mutiplayer and I try to create a multiplayer game where players are CharacterBody3D nodes with capsule collisions. I want to implement a custom mechanic where players can push each other.
Desired Behavior:
Host & Client: When Player A (host) pushes Player B (client), they should immediately “glue” together and move forward at the exact same speed as Player A.
Instant Stop: The moment Player A stops inputting movement, both players must stop completely and immediately. Player B should not slide or drift away.
Problem
Currently, when I stop pushing, both characters bounce or “push back” a little bit instead of coming to an immediate halt. Additionally, the client (Player B) frequently jitters back and forth while being pushed.
extends CharacterBody3D
@export var speed: float = 6.0
@export var jump_velocity: float = 4.5
@export_range(0.0, 1.0) var push_multiplier: float = 0.8
@onready var _input: PlayerInput = $Input
var _radius: float
func _ready() -> void:
_radius = ($PlayerCollision.shape as CapsuleShape3D).radius
$RollbackSynchronizer.process_settings()
func _rollback_tick(delta: float, _tick: int, _is_fresh: bool) -> void:
if not is_on_floor():
velocity.y += get_gravity().y * delta
elif _input.jump:
velocity.y = jump_velocity
var move_dir: Vector3 = transform.basis * _input.movement
move_dir.y = 0.0
move_dir = move_dir.normalized() if move_dir.length() > 0.001 else Vector3.ZERO
velocity.x = move_dir.x * speed
velocity.z = move_dir.z * speed
velocity *= NetworkTime.physics_factor
move_and_slide()
velocity /= NetworkTime.physics_factor
_resolve_player_push()
func _resolve_player_push() -> void:
for body in get_tree().get_nodes_in_group("Player"):
if body == self or not (body is CharacterBody3D):
continue
var away: Vector3 = global_position - body.global_position
away.y = 0.0
var dist := away.length()
var min_sep := _radius * 2.0
if dist >= min_sep or dist < 0.001:
continue
var dir := away / dist
var my_push := _push_intent(self, -dir)
var their_push := _push_intent(body, dir)
var total := my_push + their_push
var ratio := 0.5 if total < 0.001 else their_push / total
var my_share := lerpf(1.0 - push_multiplier, push_multiplier, ratio)
global_position += dir * (min_sep - dist) * my_share
func _push_intent(player: Node, dir: Vector3) -> float:
var node_input: PlayerInput = player.get_node_or_null("Input")
if node_input == null:
return 0.0
var move: Vector3 = player.transform.basis * node_input.movement
move.y = 0.0
if move.length() < 0.001:
return 0.0
return clampf(move.normalized().dot(dir), 0.0, 1.0)