2021-01-22 socket位置同步程序 (玩家在场景随意走动并看到其他玩家)

这篇博客介绍了Unity3D中实现客户端与服务器端实时通信的示例代码,包括玩家位置同步、消息处理和网络连接。通过Socket编程,创建了客户端的移动控制和位置发送,以及服务器端的接受、广播和处理玩家位置更新的机制。
摘要由CSDN通过智能技术生成

客户端

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using UnityEngine.UI;

public class Walk : 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);
        Vector3 pos = new Vector3(100, 0, 87);
        AddPlayer(id, pos);
        //同步
        SendPos();
    }

    //链接
    void Connect()
    {
        //Socket
        socket = new Socket(AddressFamily.InterNetwork,
                                 SocketType.Stream, ProtocolType.Tcp);
        //Connect
        socket.Connect("192.168.3.206", 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;
        }
    }
}

服务器端

using System;
using System.Net;
using System.Net.Sockets;

public class Serv
{
	//监听嵌套字
	public Socket listenfd;
	//客户端链接
	public Conn[] conns;
	//最大链接数
	public int maxConn = 50;
	
	//获取链接池索引,返回负数表示获取失败
	public int NewIndex()
	{
		if (conns == null)
			return -1;
		for (int i = 0; i < conns.Length; i++)
		{
			if (conns[i] == null)
			{
				conns[i] = new Conn();
				return i;
			}
			else if (conns[i].isUse == false)
			{
				return i;
			}
		}
		return -1;
	}
	
	//开启服务器
	public void Start(string host, int port)
	{
		//链接池
		conns = new Conn[maxConn];
		for (int i = 0; i < maxConn; i++)
		{
			conns[i] = new Conn();
		}
		//Socket
		listenfd = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);
		//Bind
		IPAddress ipAdr = IPAddress.Parse(host);
		IPEndPoint ipEp = new IPEndPoint(ipAdr, port);
		listenfd.Bind(ipEp);
		//Listen
		listenfd.Listen(maxConn);
		//Accept
		listenfd.BeginAccept(AcceptCb, null);
		Console.WriteLine("[服务器]启动成功");
	}


	//Accept回调
	private void AcceptCb(IAsyncResult ar)
	{
		try
		{
			Socket socket = listenfd.EndAccept(ar);
			int index = NewIndex();
			
			if(index < 0)
			{
				socket.Close();
				Console.Write("[警告]链接已满");
			}
			else
			{
				Conn conn = conns[index];
				conn.Init(socket);
				string adr = conn.GetAdress();
				Console.WriteLine("客户端连接 [" + adr +"] conn池ID:" + index);
				conn.socket.BeginReceive(conn.readBuff, conn.buffCount, conn.BuffRemain(),
				                         SocketFlags.None, ReceiveCb, conn);
			}
			listenfd.BeginAccept(AcceptCb,null);
		}
		catch(Exception e)
		{
			Console.WriteLine("AcceptCb失败:" + e.Message);
		}
	}

	private void ReceiveCb(IAsyncResult ar)
	{
		Conn conn = (Conn)ar.AsyncState;
		try
		{
			int count = conn.socket.EndReceive(ar);
			//关闭信号
			if(count <= 0)
			{
				Console.WriteLine("收到 [" + conn.GetAdress() +"] 断开链接");
				conn.Close();
				return;
			}
			//数据处理
			string str = System.Text.Encoding.UTF8.GetString(conn.readBuff, 0, count);
			Console.WriteLine("收到 [" + conn.GetAdress() +"] 数据:" + str);
			HandleMsg(conn, str);
			//继续接收	
			conn.socket.BeginReceive(conn.readBuff, conn.buffCount, conn.BuffRemain(), 
			                         SocketFlags.None, ReceiveCb, conn);
		}
		catch(Exception e)
		{
			Console.WriteLine("收到 [" + conn.GetAdress() +"] 断开链接");
			conn.Close();
		}
	}


	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);
		}
	}
}
using System;
using System.Net;
using System.Net.Sockets;
using System.Collections;
using System.Collections.Generic;

public class Conn
{
	//常量
	public const int BUFFER_SIZE = 1024;
	//Socket
	public Socket socket;
	//是否使用
	public bool isUse = false;
	//Buff
	public byte[] readBuff = new byte[BUFFER_SIZE];
	public int buffCount = 0;
	//构造函数
	public Conn()
	{
		readBuff = new byte[BUFFER_SIZE];
	}
	//初始化
	public void Init(Socket socket)
	{
		this.socket = socket;
		isUse = true;
		buffCount = 0;
	}
	//缓冲区剩余的字节数
	public int BuffRemain()
	{
		return BUFFER_SIZE - buffCount;
	}
	//获取客户端地址
	public string GetAdress()
	{
		if (!isUse)
			return "无法获取地址";
		return socket.RemoteEndPoint.ToString();
	}
	//关闭
	public void Close()
	{
		if (!isUse)
			return;
		
		Console.WriteLine("[断开链接]" + GetAdress());
		socket.Close();
		isUse = false;
	}
}
using System;
using System.Net;
using System.Net.Sockets;

class MainClass
{
	public static void Main(string[] args)
	{
		Console.WriteLine("Hello World!");
		Serv serv = new Serv();
		serv.Start("192.168.3.206", 1234);
		
		while (true)
		{
			string str = Console.ReadLine();
			switch (str)
			{
			case "quit":
				return;
			}
		}
	}
}

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值