美文网首页征服Unity3dUnity技术分享unity3D技术分享
Jtro的源码分享:客户端通过服务器同步位置消息

Jtro的源码分享:客户端通过服务器同步位置消息

作者: UnityPlane | 来源:发表于2017-12-02 15:01 被阅读50次

    之前的源码讲过如何使用sql留言的功能,今天讲解两个客户端如何通过服务器来同步每个玩家的位置。
    首先是服务器端,用到之前的异步socket的服务器了,如果没有的同学可以查看之前我写的这个链接:http://www.jianshu.com/p/e7078bf391fb,是讲异步Socket的的。
    我只修改其中的一小段代码:

    public void HandleMsg(Conn conn, string str)
        {
            byte[] bytes = System.Text.Encoding.Default.GetBytes(str);
            //广播消息
            for(int i=0;i < conns.Length; i++)
            {
                if(conns[i] == null) continue;
                if(!conns[i].isUse)  continue;
                Console.WriteLine("将消息转播给 " + conns[i].GetAdress());
                conns[i].socket.Send(bytes);
            }
        }
    

    目的就是让服务器只起到消息的广播,消息处理还是由客户端来实现
    接下来就是创建一个空的场景,下图是我的参考:


    参考.png

    场景中新建一个plan,一个cube,带有3dtext组件。然后将这个cube拉成预设。
    然后新建一个脚本命名为:MOVE

    输入一下的代码:

    //初创日期  :2017.12.02
    //脚本功能  :客户端类,存放所有玩家信息。msgList是消息列表,收到服务器消息之后客户端会将消息保存在msgList中,等到Update逐一处理
    //            处理添加玩家,发送协议和移动玩家的方法
    //脚本挂载在:     
    //作者:Jtro
    //第一次修改:
    /*
    */
    using UnityEngine;
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Net;
    using System.Net.Sockets;
    using UnityEngine.UI;
    
    public class MOVE : MonoBehaviour
    {
        //socket和缓冲区
        Socket socket;
        const int BUFFER_SIZE = 1024;
        public byte[] readBuff = new byte[BUFFER_SIZE];
        //玩家列表
        Dictionary<string, GameObject> players = new Dictionary<string, GameObject>();
        //消息列表
        List<string> msgList = new List<string>();
        //Player预设
        public GameObject prefab;
        //自己的IP和端口
        string id;
    
        //添加玩家
        void AddPlayer(string id, Vector3 pos)
        {
            GameObject player = (GameObject)Instantiate(prefab, pos, Quaternion.identity);
            TextMesh textMesh = player.GetComponentInChildren<TextMesh>();
            textMesh.text = id;
            players.Add(id, player);
        }
    
        //发送位置协议
        void SendPos()
        {
            GameObject player = players[id];
            Vector3 pos = player.transform.position;
            //组装协议
            string str = "POS ";
            str += id + " ";
            str += pos.x.ToString() + " ";
            str += pos.y.ToString() + " ";
            str += pos.z.ToString() + " ";
    
            byte[] bytes = System.Text.Encoding.Default.GetBytes(str);
            socket.Send(bytes);
            Debug.Log("发送 " + str);
        }
    
        //发送离开协议
        void SendLeave()
        {
            //组装协议
            string str = "LEAVE ";
            str += id + " ";
            byte[] bytes = System.Text.Encoding.Default.GetBytes(str);
            socket.Send(bytes);
            Debug.Log("发送 " + str);
        }
    
        //移动
        void Move()
        {
            if (id == "")
                return;
    
            GameObject player = players[id];
            //上
            if (Input.GetKey(KeyCode.UpArrow))
            {
                player.transform.position += new Vector3(0, 0, 1);
                SendPos();
            }
            //下
            else if (Input.GetKey(KeyCode.DownArrow))
            {
                player.transform.position += new Vector3(0, 0, -1); ;
                SendPos();
            }
            //左
            else if (Input.GetKey(KeyCode.LeftArrow))
            {
                player.transform.position += new Vector3(-1, 0, 0);
                SendPos();
            }
            //右
            else if (Input.GetKey(KeyCode.RightArrow))
            {
                player.transform.position += new Vector3(1, 0, 0);
                SendPos();
            }
        }
    
        //离开
        void OnDestory()
        {
            SendLeave();
        }
    
        //开始
        void Start()
        {
            Connect();
            //请求其他玩家列表,略
            //把自己放在一个随机位置
            UnityEngine.Random.seed = (int)DateTime.Now.Ticks;
            float x = 100 + UnityEngine.Random.Range(-30, 30);
            float y = 0;
            float z = 100 + UnityEngine.Random.Range(-30, 30);
            Vector3 pos = new Vector3(x, y, z);
            AddPlayer(id, pos);
            //同步
            SendPos();
        }
    
        //链接
        void Connect()
        {
            //Socket
            socket = new Socket(AddressFamily.InterNetwork,
                                     SocketType.Stream, ProtocolType.Tcp);
            //Connect
            socket.Connect("127.0.0.1", 1234);
            id = socket.LocalEndPoint.ToString();
            //Recv
            socket.BeginReceive(readBuff, 0, BUFFER_SIZE, SocketFlags.None, ReceiveCb, null);
        }
    
        //接收回调
        private void ReceiveCb(IAsyncResult ar)
        {
            try
            {
                int count = socket.EndReceive(ar);
                //数据处理
                string str = System.Text.Encoding.UTF8.GetString(readBuff, 0, count);
                msgList.Add(str);
                //继续接收  
                socket.BeginReceive(readBuff, 0, BUFFER_SIZE, SocketFlags.None, ReceiveCb, null);
            }
            catch (Exception e)
            {
                socket.Close();
            }
        }
    
        void Update()
        {
            //处理消息列表
            for (int i = 0; i < msgList.Count; i++)
                HandleMsg();
            //移动
            Move();
        }
    
        //处理消息列表
        void HandleMsg()
        {
            //获取一条消息
            if (msgList.Count <= 0)
                return;
            string str = msgList[0];
            msgList.RemoveAt(0);
            //根据协议做不同的消息处理
            string[] args = str.Split(' ');
            if (args[0] == "POS")
            {
                OnRecvPos(args[1], args[2], args[3], args[4]);
            }
            else if (args[0] == "LEAVE")
            {
                OnRecvLeave(args[1]);
            }
        }
    
        //处理更新位置的协议
        public void OnRecvPos(string id, string xStr, string yStr, string zStr)
        {
            //不更新自己的位置
            if (id == this.id)
                return;
            //解析协议
            float x = float.Parse(xStr);
            float y = float.Parse(yStr);
            float z = float.Parse(zStr);
            Vector3 pos = new Vector3(x, y, z);
            //已经初始化该玩家
            if (players.ContainsKey(id))
            {
                players[id].transform.position = pos;
            }
            //尚未初始化该玩家
            else
            {
                AddPlayer(id, pos);
            }
        }
    
        //处理玩家离开的协议
        public void OnRecvLeave(string id)
        {
            if (players.ContainsKey(id))
            {
                Destroy(players[id]);
                players[id] = null;
            }
        }
    }
    

    然后场景中新建一个空物体,将此脚本挂在上面,拖入预设体,打包成exe
    然后打开服务器与多个客户端


    打开服务器与多个客户端.PNG

    注意此时的客户端已经被服务器同步,只是窗口不在活跃状态,看不到。
    按一下下键:


    按了一个下键.PNG
    服务器记录位置信息并广播出去,同步到其他客户端

    要想实时看到效果还是加上那一句:runinbackground,我这次又忘了加了-_-||

    相关文章

      网友评论

        本文标题:Jtro的源码分享:客户端通过服务器同步位置消息

        本文链接:https://www.haomeiwen.com/subject/zjynbxtx.html