Unity3D笔记——UDP 广播和异步接受

本文介绍如何在Unity3D中使用UDP广播实现多点触控输入,包括服务器端和客户端的代码实现,以及如何解析JSON数据并将其转换为游戏内的输入。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:Unity3D笔记是我平时做一些好玩的测试和研究,记录的笔记。会比较详细也可能随口一提就过了。 所以大家见谅了,内容一般都会是原创的(非原创我会注明转载)。由于很多内容其他的朋友也肯定研究发表过,大家用于指出错误。

前段时间,公司有一个针对游乐园的项目。项目是基于安卓3399板子的,会检测一些红外的触碰来替代鼠标当作输入。本来以为那么就 Launcher 里有个服务,检测到有触碰那么转换下屏幕坐标之后模拟鼠标点击就可以了。可是真是用的时候发现了一些局限性:
1.鼠标点击是单一事件,不可能同一时间点击多个地方。
2.延迟其实有点大,虽然就零点几秒。但是我们的项目对延迟比较看重。

所以提出了第二个方案:红外检测到数据后Launcher的服务通过坐标转换成屏幕坐标,通过UDP全网广播,游戏和桌面监听(桌面也需要通过触碰打开游戏等等)。所以就需要我专门做一个unity能用的UDP监听功能,并转换成输入。

一.UDP广播

为了测试,我也做了一个简单的UDP的广播代码;

 //服务器端Scoket对象;
    UdpClient client;

    private IPEndPoint endpoint;

void Start()
    {
        //绑定UdpClient 端口,8866
        client = new UdpClient(new IPEndPoint(IPAddress.Any, 8866));

        //全网(255,255,255,255)
        endpoint = new IPEndPoint(IPAddress.Parse("255.255.255.255"), 0);

        //每秒都发送
        InvokeRepeating("Send",1,1);
    }


    void Send()
    {
        string dataStr = "[{\"x\":732.8001098632813,\"y\":1000.0156860351563},{\"x\":817.5443725585938,\"y\":379.5652160644531},{\"x\":956.8383178710938,\"y\":538.0902099609375}]";

        byte[] SendData = System.Text.Encoding.UTF8.GetBytes(dataStr);

        //发送,不需要异步。因为是广播,并且不关心有没有接受
        client.Send(SendData, SendData.Length, endpoint);
    }

备注:上面的代码就做了个简单功能:向全网每秒广播信息。端口号是:8866

二.游戏这边需要接受数据了。UDP异步接受

有几点需求:
1.单纯接受8866端口的数据就可以。
2.将byte数据转换成坐标数据。
3.接受到数据,才会触发游戏一些输入机制。所以需要异步接收(防止卡死)

unity这边的接受代码:

 //服务器端Scoket对象;
    private Socket ServerSocket;

    private IPEndPoint Clients;

    private IPEndPoint Server;

    private EndPoint epSender;

    //发送数据的字符数组
    private byte[] SendData = new byte[1024];

    //接受数据的字符数组
    private byte[] ReceiveData = new byte[1024];


    // Use this for initialization
    void Start()
    {
        //服务器Socket对象实例化
        ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

        //服务器的IP和端口
        Server = new IPEndPoint(IPAddress.Any, 8866);

        //Socket对象跟服务器端的IP和端口绑定
        ServerSocket.Bind(Server);

        //客户端的IP和端口,端口 0 表示任意端口
        Clients = new IPEndPoint(IPAddress.Any, 0);

        //实例化客户端 终点
        epSender = (EndPoint)Clients;

        ServerSocket.BeginReceiveFrom(ReceiveData, 0, ReceiveData.Length, SocketFlags.None, ref epSender, new AsyncCallback(ReceiveFromClients), epSender);

    }

    /// <summary>
    /// 异步接受,处理数据
    /// </summary>
    /// <param name="iar"></param>
    private void ReceiveFromClients(IAsyncResult iar)
    {
        int reve = ServerSocket.EndReceiveFrom(iar, ref epSender);

        //数据处理
        string str = System.Text.Encoding.UTF8.GetString(ReceiveData, 0, reve);

       //每次接受到数据就传给ServerInput做处理。
        ServerInput.GetInstance().AnalyzeJsonData(str);

        ServerSocket.BeginReceiveFrom(ReceiveData, 0, ReceiveData.Length, SocketFlags.None, ref epSender, new AsyncCallback(ReceiveFromClients), epSender);

    }

下面这句是每次启动异步接受的代码。

ServerSocket.BeginReceiveFrom(ReceiveData, 0, ReceiveData.Length, SocketFlags.None, ref epSender, new AsyncCallback(ReceiveFromClients), epSender);

下面的是:每次接受到数据后就将数据传给serverInput做处理。

 //数据处理
        string str = System.Text.Encoding.UTF8.GetString(ReceiveData, 0, reve);

       //每次接受到数据就传给ServerInput做处理。
        ServerInput.GetInstance().AnalyzeJsonData(str);

ServerInput代码:我们的协议是json格式的。所以这里做了个json的转换。和一个公开的事件:ServerInputEvent 每次接受到数据都会传给这个事件。游戏里哪里需要 直接注册这个事件就可以用了。
(注意:安卓的OpenOL的屏幕坐标系和unity的坐标系是上下反的。所以要处理下:Screen.height-(int)jObject[“y”])

 public event Action<List<Vector2>> ServerInputEvent;

    private static ServerInput instance = null;
    public static ServerInput GetInstance() { return instance ?? new ServerInput(); }

    ServerInput() { instance = this; }

    /// <summary>
    /// 处理服务器传来的json信息,传给事件
    /// </summary>
    /// <param name="jsonData"></param>
    public void AnalyzeJsonData(string jsonData) {

        try
        {
            List<Vector2> posList = new List<Vector2>();
            JArray jsonObj = JArray.Parse(jsonData);

            foreach (JObject jObject in jsonObj)
            {
                //Debug.Log(jObject["x"]);
                //Debug.Log(jObject["y"]);

                Vector2 pos = new Vector2((int)jObject["x"],Screen.height-(int)jObject["y"]);
                posList.Add(pos);

            }
            if (ServerInputEvent != null)
                ServerInputEvent(posList);
        }
        catch (Exception)
        {

            throw;
        }
    } 

**最后附上test代码: 简单的打印到屏幕中
备注下(异步接受是子线程的,所以不能直接给UI赋值 所以转到主线程的Update.涉及到物理系统也一样)**

[SerializeField]
    private Text text;

    string str = null;

    // Use this for initialization
    void Start () {
        //向ServerInputEvent事件注册
        ServerInput.GetInstance().ServerInputEvent += attack;
    }

    private void Update()
    {
        text.text = str;
    }

    //这边写游戏内容的控制
    void attack(List<Vector2> pos) {

        str = "";

        foreach (var item in pos)
        {
            print(item);
            str = str + "  " + "(" + item.x.ToString() + "," + item.y.ToString()+ ")";
        }
    }

这里写图片描述

最后附上项目:
链接:https://pan.baidu.com/s/1cK0UXC 密码:9gw0

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值