[UNet]通过一个小测试了解Command和ClientRpc的功能

作者只是业余时间接触Unity 2个月的新人一枚,如有不对的地方,请各位大神指正!

Unity 5.以后使用了新的网络模块UNet(其实我接触的晚压根没用过老的- -!),但是在使用过程中确实关于UNet的资料和讨论挺少的,于是自己做了个小项目测试了Command和ClientRpc两个命令。

首先我们看看官方给出的定义:

Commands

Commands are sent from player objects on the client to player objects on the server. For security, Commands can only be sent from YOUR player object, so you cannot control the objects of other players. To make a function into a command, add the [Command] custom attribute to it, and add the “Cmd” prefix. This function will now be run on the server when it is called on the client. Any arguments will automatically be passed to the server with the command.

ClientRpc Calls

ClientRpc calls are sent from objects on the server to objects on clients. They can be sent from any server object with a NetworkIdentity that has been spawned. Since the server has authority, then there no security issues with server objects being able to send these calls. To make a function into a ClientRpc call, add the [ClientRpc] custom attribute to it, and add the “Rpc” prefix. This function will now be run on clients when it is called on the server. Any arguments will automatically be passed to the clients with the ClientRpc call..

Commands命令是由客户端(或具有客户端权限的其他GameObject)发送到服务端,并在服务端运行

ClientRpc命令是由服务端发送到连接到服务端的每一个客户端并在客户端运行

官方给的图解释的很到位


官方的解释是可以传递任何参数,但实际上,不知道是不是因为GameObject不算参数,只有带NetworkIdentity的GameObject才能作为参数传递

下面我们通过一个小例子来说明


我们新建一个小项目,Player我们设置为一个Text Mesh,并初始化为10(懒得重载NetworkManager了,我就手动移动数字好了)

<span style="white-space:pre">	</span>public int num = 10;
	void Start () {
        num = 10;
	}
	
	void Update () {
        GetComponent<TextMesh>().text = num.ToString();
        if (!isLocalPlayer)
            return;

        var x = Input.GetAxis("Horizontal") * Time.deltaTime * 5.0f;
        transform.Translate(x, 0, 0);

        if(Input.GetKeyDown(KeyCode.R))
        {
            normalPlus();
        }
        
	}

    void normalPlus()
    {
        num++;
    }

很显然,这样的话服务端和客户端的数据不会同步


现在我们使用Commands命令

    public int num = 10;
	void Start () {
        num = 10;
	}
	
	void Update () {
        GetComponent<TextMesh>().text = num.ToString();
        if (!isLocalPlayer)
            return;

        var x = Input.GetAxis("Horizontal") * Time.deltaTime * 5.0f;
        transform.Translate(x, 0, 0);

        if(Input.GetKeyDown(KeyCode.R))
        {
            CmdPlus();
        }
        
	}

    [Command]
    void CmdPlus()
    {
        num++;
    }
可以看到,客户端的数据同步到了服务端,但服务端的数据没同步到客户端,且客户端本身的数据也没有更新,因为+1的命令只在服务端运行

然后我们加上ClientRpc

    public int num = 10;
	void Start () {
        num = 10;
	}
	
	void Update () {
        GetComponent<TextMesh>().text = num.ToString();
        if (!isLocalPlayer)
            return;

        var x = Input.GetAxis("Horizontal") * Time.deltaTime * 5.0f;
        transform.Translate(x, 0, 0);

        if(Input.GetKeyDown(KeyCode.R))
        {
            CmdPlus();
        }
        
	}

    [Command]
    void CmdPlus()
    {
        num++;
        RpcPlus1();
    }

    [ClientRpc]
    void RpcPlus1()
    {
        num++;
    }
运行后我们可以看到,每次操作,服务端进行了两次+1,这是因为服务端是一个Listen server(Unity称之为Host),它既是服务端,也是客户端,所以Cmd函数中的+1和Rpc函数中的+1都执行了一次

所以我们去掉Cmd函数中的+1操作,两边的数据正常同步

    public int num = 10;
	void Start () {
        num = 10;
	}
	
	void Update () {
        GetComponent<TextMesh>().text = num.ToString();
        if (!isLocalPlayer)
            return;

        var x = Input.GetAxis("Horizontal") * Time.deltaTime * 5.0f;
        transform.Translate(x, 0, 0);

        if(Input.GetKeyDown(KeyCode.R))
        {
            CmdPlus();
        }
        
	}

    [Command]
    void CmdPlus()
    {
        RpcPlus1();
    }

    [ClientRpc]
    void RpcPlus1()
    {
        num++;
    }   


然后,我们开始进行参数传递,上面我们的Cmd和Rpc函数没有任何形参,我们将代码修改如下就可以看出一些端倪

    public int num;
<span style="white-space:pre">	</span>void Start () {
        num = Random.Range(1,100);
        GetComponent<TextMesh>().text = num.ToString();
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>void Update () {
        
        if (!isLocalPlayer)
            return;


        var x = Input.GetAxis("Horizontal") * Time.deltaTime * 5.0f;
        transform.Translate(x, 0, 0);


        if(Input.GetKeyDown(KeyCode.R))
        {
            CmdPlus();
        }
        
<span style="white-space:pre">	</span>}


    [Command]
    void CmdPlus()
    {
        RpcPlus1();
    }


    [ClientRpc]
    void RpcPlus1()
    {
        num++;
        GetComponent<TextMesh>().text = num.ToString();
    }
我们将num初始化为1-100之间的随机整数,可以看到,客户端和Host初始化的数据不一样了,在没有参数传递的情况下,客户端和Host各自调用了两次Start函数,即Rpc函数在发送到每一个客户端的时候都在本地进行了一次初始化,+1操作可以同步,但数据无法同步成一样的了



现在我们给函数加上形参

    public int num;
	void Start () {
        num = Random.Range(1,100);
        GetComponent<TextMesh>().text = num.ToString();
	}
	
	void Update () {
        
        if (!isLocalPlayer)
            return;

        var x = Input.GetAxis("Horizontal") * Time.deltaTime * 5.0f;
        transform.Translate(x, 0, 0);

        if(Input.GetKeyDown(KeyCode.R))
        {
            CmdPlus(num);
        }
        
	}

    [Command]
    void CmdPlus(int n)
    {
        RpcPlus1(n);
    }

    [ClientRpc]
    void RpcPlus1(int n)
    {
        n++;
        GetComponent<TextMesh>().text = n.ToString();
    }

我们可以看到,首次初始化后,两边的数字是不同步的


然后,在调用一次Rpc函数进行+1操作后,两个客户端和Host的数据通过参数传递实现了同步,且同步后的数据是发送端的数据



由于作者水平实在有限,只能通过这个小测试描述下这两个命令的功能,要解释原理的话就无能为力了,如果有什么不对之处,还请大神们指正!




猜你喜欢

转载自blog.csdn.net/notmz/article/details/51227942
今日推荐