Search code examples
c#unity-game-enginenetwork-programmingunity-networkingunity3d-unet

Unity Networking 2d game, ClientRpc not working correctly


I am networking a 2d game for my school project and I have come across a problem when attempting to make a player "Evolve" in a network scenario. The player will only evolve correctly on the client and not on the host. The code that causes the problem is somewhere in the Evolve script but I don't know how to show the problem through the code, as the issue is in enactment of the code. Hence, I have attached both the code :

using System.Collections;
using System.Collections.Generic;
using UnityEngine.Networking;
using UnityEngine;

public class Evolve : NetworkBehaviour {

    public bool canEvolve;
    public bool isEvolving;
    public int evoTimer;
    public int timeToEvolve;
    public int currentEvo;
    public GameObject Evo0;
    public GameObject Evo1;
    public GameObject Evo2;
    public GameObject nextEvo;
    public GameObject nextA_Atk;
    public GameObject nextB_Atk;

    // Use this for initialization

    void Start() {
        canEvolve = true;
        isEvolving = false;
        evoTimer = 0;
        timeToEvolve = GameObject.FindGameObjectWithTag("Settings").GetComponent<GameSettings>().timeToEvolve;
        currentEvo = -1;
        RpcCallEvolve();
    }

    // Update is called once per frame
    void Update() {
        if (!isLocalPlayer)
        {
            return;
        }
        if ((Input.GetButton("Evolve")) && (canEvolve == true))
        {
            isEvolving = true;
            evoTimer += 1;
            if (evoTimer >= timeToEvolve)
            {
                RpcCallEvolve();
            }
        }
        else
        {
            isEvolving = false;
            evoTimer = 0;
        }
    }

    [ClientRpc(channel = 0)]
    void RpcCallEvolve()
    {
        currentEvo += 1;
        switch (currentEvo)
        {
            case 0:
                nextEvo = Instantiate(Evo0) as GameObject;
                break;
            case 1:
                nextEvo = Instantiate(Evo1) as GameObject;
                break;
            case 2:
                nextEvo = Instantiate(Evo2) as GameObject;
                break;
            case 3:
                Win(name);
                break;
        }
        GetComponent<SpriteRenderer>().sprite = nextEvo.GetComponent<SpriteRenderer>().sprite;
        GetComponent<PolygonCollider2D>().points = nextEvo.GetComponent<PolygonCollider2D>().points;
        canEvolve = true;
        evoTimer = 0;
        Destroy(nextEvo);
    }
    void Win(string player)
    {
        Debug.Log("Winner is " + player);
        gameObject.SetActive(false);
    }
}

And a link to download the full project if that helps people figure out the problem. https://1drv.ms/u/s!ArLjF5CAV1VwgbxZdCQkb_9PZ_j5kw

I don't need the rest of the code fixed or neatened, just info on what might work for the above problem.

Thanks so much to anyone who helps.


Solution

  • After looking at your script, it appears that there are a few problems. First, let's go over some of the rules for using a ClientRPC:

    • ClientRPCs are called on the server/host and the corresponding method is called on all clients (including the host since a host is a server and a client combined)
    • ClientRPCs should ONLY be called on the server, or else they won't work properly. ClientRPCs can be called by the client, but they won't be sending any data to the server and it will just work like a normal method.

    Now let's look at your script. Your RpcCallEvolve method is called in the Update method, but the first thing you check is whether or not the script is on the local player. This introduces some problems, mainly that the RpcCallEvolve method would only be called on a client that owns that player. If this happens to be called on the host's player, the script should work correctly, since the ClientRPC would be called on the server anyways. However, if it is called on your client, it would work like a regular, local method (as we mentioned earlier). What I would suggest is to use a Command, in order to guarantee that the ClientRPC is being called on the server. Commands only work on player objects, so keep in mind that this Evolve script must be on the player object.

    [Command]
    public void CmdServerEvolve () 
    {
        RpcCallEvolve();
    }
    

    So instead of calling RpcCallEvolve in your Update function, call CmdServerEvolve. Feel free to ask questions if any of this wasn't clear. Hope this helps!