前言:Unity3D笔记是我平时做一些好玩的测试和研究,记录的笔记。会比较详细也可能随口一提就过了。 所以大家见谅了,内容一般都会是原创的(非原创我会注明转载)。由于很多内容其他的朋友也肯定研究发表过,大家请指出错误。
公司一个同事在用Unet的时候,是参考unity 的官方教程《Tanks》的。然后发现一个小问题:在局域网中,当两台电脑同时按下(接近的时间按下)play的时候,会一起检测局域网是否有服务器去加入。然后 都检测不到;于是就各自创建了服务器。没有在一个游戏中;
于是呢,考虑了下;我觉得造成该问题的原因是:游戏打开的时间接近,没有办法判断谁创建服务器,谁是客户端;导致互相都创建的问题;直觉告诉我,可以用UDP的广播实现问题:1.开始游戏,全网监听固定端口(5555)发来的消息(是否有服务器了)2.如果有则加入;3.如果1秒后没有收到,则自己广播信号出去(我要创建服务器);4.创建服务器;
用到了之前的一些代码;我改了下
直接上代码:UDP的全网接受和发送
using UnityEngine; using System.Collections; using System.Collections.Generic; using System; using System.Net; using System.Net.Sockets; using UnityEngine.UI; namespace ImmerUDP { public class GameUdpManager :MonoBehaviour { [Header("本地端口"),SerializeField] private int sendPort = 6666; [Header("目标端口"), SerializeField] private int receivePort = 8888; //服务器端Scoket对象; private Socket serverSocket; //udp客户端socket private UdpClient client; private EndPoint epSender; private IPEndPoint endpoint; //接受数据的字符数组 private byte[] ReceiveData = new byte[1024]; public static GameUdpManager Instance; private void Awake() { Instance = this; } public void StartUdpWork(int sendPort,int recevie) { this.sendPort = sendPort; this.receivePort = recevie; StartUdpReceive(); OpenSendSocket(); } /// <summary> /// 开启接受的Socket 并开始异步接受 /// </summary> public void StartUdpReceive() { //服务器Socket对象实例化 serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); //Socket对象跟服务器端的IP和端口绑定 serverSocket.Bind(new IPEndPoint(IPAddress.Any, receivePort)); //监听的端口和地址 epSender = (EndPoint)new IPEndPoint(IPAddress.Parse("255.255.255.255"), sendPort); //开始异步接受数据 serverSocket.BeginReceiveFrom(ReceiveData, 0, ReceiveData.Length, SocketFlags.None, ref epSender, new AsyncCallback(ReceiveFromClients), epSender); } /// <summary> /// 开启发送的Socket /// </summary> public void OpenSendSocket() { client = new UdpClient(new IPEndPoint(IPAddress.Any, sendPort)); //目标端口和地址 endpoint = new IPEndPoint(IPAddress.Parse("255.255.255.255"), receivePort); } /// <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); //Debug.Log(str); //把得到数据传给数据处理中心 NetworkDataHandler.Instance.NetworkDataUpdate(str); serverSocket.BeginReceiveFrom(ReceiveData, 0, ReceiveData.Length, SocketFlags.None, ref epSender, new AsyncCallback(ReceiveFromClients), epSender); } /// <summary> /// 发送数据 /// </summary> /// <param name="dataStr">str 数据</param> public void Send(string dataStr) { byte[] SendData = System.Text.Encoding.UTF8.GetBytes(dataStr); client.Send(SendData, SendData.Length, endpoint); } } }
说明:这边的UDP有这两个socket:分别是发送和接受;发送是向6666端口全网广播的;接受是:全网接受5555端口的;
消息处理中心:
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; public class NetworkDataHandler : MonoBehaviour { public static NetworkDataHandler Instance; public delegate void VoidEvent(); //注册对应的事件 public event VoidEvent serverOpenEvent; bool isDataUpdate = false; public string data=""; private void Awake() { Instance = this; } // Use this for initialization void Start () { } private void Update() { if (!isDataUpdate) return; isDataUpdate = false; } public void NetworkDataUpdate(string data) { this.data = data; // Debug.Log(data); if (data.Contains("ServerIsOpen")) { if (serverOpenEvent != null) serverOpenEvent(); } } }
执行脚本:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class demo5 : MonoBehaviour { public static bool isHasServer=false; bool isSendMessage = false; private void Awake() { } // Use this for initialization void Start () { Invoke("startServer", 1f); NetworkDataHandler.Instance.serverOpenEvent += openClient; ImmerUDP.GameUdpManager.Instance.StartUdpWork(5555,6666); } private void Update() { if(isSendMessage) ImmerUDP.GameUdpManager.Instance.Send("ServerIsOpen"); } void startServer() { if (isHasServer) return; isSendMessage = true; } void openClient() { if (isHasServer) return; if (isSendMessage == true) { Debug.Log("本机是服务器,不用去开始客户端"); return; } isHasServer = true; } }
说明:执行脚本的作用就是通过广播判断下:局域网中是否有服务器了;没呢:广播信息告诉大家,这边会创建服务器;
然后是《Tanks》中局域网开始游戏代码更改:
public IEnumerator DiscoverNetwork() { yield return new WaitForSeconds(1); NetworkDiscoveryCustom discovery = GetComponent<NetworkDiscoveryCustom>(); discovery.Initialize(); print(demo5.isHasServer); if (demo5.isHasServer) { discovery.StartAsClient(); } else { yield return new WaitForSeconds(0.5f); //NetworkDiscoveryCustom discovery2 = GetComponent<NetworkDiscoveryCustom>(); discovery.StartAsServer(); StartHost(); } //////start listening to other hosts //NetworkDiscoveryCustom discovery = GetComponent<NetworkDiscoveryCustom>(); //discovery.Initialize(); //discovery.StartAsClient(); ////wait few seconds for broadcasts to arrive //yield return new WaitForSeconds(8); ////we haven't found a match, open our own //if (discovery.running) //{ // discovery.StopBroadcast(); // yield return new WaitForSeconds(0.5f); // discovery.StartAsServer(); // StartHost(); //} }
说明:这是《Tanks》中NetworkManagerCustom.cs中一段;主要功能是实现局域网的寻找服务器和搭建服务器; 我改了下:
如果之前广播结果是:有服务器了,那么就直接去连接;如果是没有服务器,那么本地创建服务器;
东西不多,也算是一种应用啦。希望对大家有帮助;