前言: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中一段;主要功能是实现局域网的寻找服务器和搭建服务器; 我改了下:
如果之前广播结果是:有服务器了,那么就直接去连接;如果是没有服务器,那么本地创建服务器;
东西不多,也算是一种应用啦。希望对大家有帮助;