Search code examples
godotgdscript

Godot Multiplayer. Animations are not syncing between the host and client even though I am sending the data


I know I did a similar post a day ago but I just ran into another problem. I have been making a 2 player multiplayer game in Godot. When I tried to sync animations with the host and the client, nothing is happening even though I am sending all the right info through to the client. I send the animation state to the client and then the client should play the animation state.

I don't know really anything I can do to troubleshoot this problem.

Player.gd:

extends KinematicBody2D


const ACCELERAION = 512
const MAX_SPEED = 64
const FRICTION = 0.25
const GRAVIY = 200
const JUMP_FORCE = 128

var motion = Vector2.ZERO

var animationState = ""

onready var sprite = $Sprite
onready var animationPlayer = $AnimationPlayer

remote func _set_positon_and_animation_state(pos, hFlip, animState):
    global_transform.origin = pos
    sprite.flip_h = hFlip
    animationState = animState

func _physics_process(delta):
    var xInput = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
    
    print(xInput)
    
    if xInput != 0:
        animationState = "Running"
        motion.x += xInput * ACCELERAION * delta
        motion.x = clamp(motion.x, -MAX_SPEED, MAX_SPEED)
        if is_network_master():
            sprite.flip_h = xInput < 0
    else:
        animationState = "Standing"
    
    motion.y += GRAVIY * delta
    
    if is_on_floor():
        if xInput == 0:
            motion.x = lerp(motion.x, 0, FRICTION)
        if Input.is_action_just_pressed("ui_up"):
            motion.y = -JUMP_FORCE
    else:
        animationState = "Jumping"
        
        if Input.is_action_just_released("ui_up") and motion.y < -JUMP_FORCE / 2:
            motion.y = -JUMP_FORCE / 2
        if xInput == 0:
            motion.x = lerp(motion.x, 0, 0.2)
    if is_network_master():
        motion = move_and_slide(motion, Vector2.UP)
        rpc_unreliable("_set_positon_and_animation_state", global_transform.origin, sprite.flip_h, animationState)
    animationPlayer.play(animationState)

World.gd

extends Node2D

onready var playerSpawn = $SpawnPoint
onready var playerSpawn2 = $SpawnPoint2


func _ready():
    var player1 = preload("res://Player.tscn").instance()
    player1.set_name(str(get_tree().get_network_unique_id())) #Gives the host player a unique id when they join the server
    player1.set_network_master(get_tree().get_network_unique_id()) # Specifies this player owns a different character controller than the other player
    player1.set_global_transform(playerSpawn.global_transform)
    add_child(player1)
    
    var player2 = preload("res://Player.tscn").instance()
    player2.set_name(str(Globals.player2id)) #Gets the id the player got when they joined the game cant use the same function as player 1 because it will give us the same id and crash the game
    player2.set_network_master(Globals.player2id) # Specifies this player owns a different character controller than the other player
    player2.set_global_transform(playerSpawn2.global_transform)
    add_child(player2)

What am I doing wrong everything else seems to be working just fine!


Solution

  • You make an RPC to _set_positon_and_animation_state passing animationState:

    rpc_unreliable("_set_positon_and_animation_state", global_transform.origin, sprite.flip_h, animationState)
    

    And then in the code for _set_positon_and_animation_state you take that animationState argument in the animState parameter, and set the animationState field with it:

    remote func _set_positon_and_animation_state(pos, hFlip, animState):
        global_transform.origin = pos
        sprite.flip_h = hFlip
        animationState = animState
    

    But you don't use that value of animationState. Instead what I see is that you overwrite it in _physics_process (depending on input), before calling play:

        animationPlayer.play(animationState)
    

    That also begs the question of how you will handle input.


    I think you would want to call play in _set_positon_and_animation_state:

    remote func _set_positon_and_animation_state(pos, hFlip, animState):
        global_transform.origin = pos
        sprite.flip_h = hFlip
        # animationState = animState
        animationPlayer.play(animState)
    

    In my mind synchronizing animation also implies timing. There would be some delay due to the network to account for if you want to handle that. But get the animation playing first.