要用unity做一套展示系统,做出来的软件能够接收其他软件发过来的位置信息来更新展示界面中物体的位置。
因为发送端预计是UDP组播发送,因此采用组播收发的方法。下面是开发步骤
1、简单的实验用发信器
因为要对联网进行测试,需要有发信器,在这里使用一款TCP-UDP的服务管理软件(类似软件很多,自己做都可以)作为UDP信息的发送端。
UDP组播地址设置224.1.1.1:7000,选择范围:
组播组可以是永久的也可以是临时的。组播组地址中,有一部分由官方分配的,称为永久组播组。永久组播组保持不变的是它的ip地址,组中的成员构成可以发生变化。永久组播组中成员的数量都可以是任意的,甚至可以为零。那些没有保留下来供永久组播组使用的ip组播地址,可以被临时组播组利用。
224.0.0.0~224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其它地址供路由协议使用;
224.0.1.0~224.0.1.255是公用组播地址,可以用于Internet;
224.0.2.0~238.255.255.255为用户可用的组播地址(临时组地址),全网范围内有效;
239.0.0.0~239.255.255.255为本地管理组播地址,仅在特定的本地范围内有效。
组播地址为 224.0.0.0 ~ 239.255.255.255,其中 224.0.0.0~224.255.255.255 不建议在用户程序中使用,因为它们一般都有特殊用途。
2、Unity开发
发信器做好了,三个数据包轮发,我要实现的是解析数据包,拿出位置信息,并赋值给unity中的某物体,实现物体的三角形路径运动。
记录一个小问题:使用Unity5.5的时候,有时会出现用鼠标点击编辑面板上的编辑框,会出现之前输出过的文字,这是因为我用的搜狗输入法,有时英文输入是在中文模式下输入的,此时面板上的英文是有下划线的,需要按shift键或enter键去除下划线,即真正输出英文,而有时英文还有下划线时,就用鼠标点击空白处完成了输入,虽然看似没有问题,但是搜狗输入法的输入流一直没有断,因此编辑其他位置时,会出现输入流里的字符,解决方法很简单,切换成英文输入即可,此时也会自动清空中文输入法的缓存。
建立UdpRec.cs
脚本来接收处理udp信息,将此脚本附加到一个空物体(专门用于附加全局性脚本,在每个场景中,我都会有一个这样的空物体,都命名为SP0)上以运行。建立一个Terrain,与一个Cube,用于测试,Cube的Y位置不变,X,Z轴对应着UDP位置信息的x,y,则预计的效果为:
{x:0,y:0}
{x:10,y:0}
{x:5,y:10}
现在开始做UDP编程:使用以前用过的一个UDP通信类UdpComm
,包括了Udp打开、关闭、接收、发送等方法,非常简单,在工程里新建一个类UdpComm,把代码copy过来.
using System;
using System.Net.Sockets;
using System.Net;
using System.Threading;
class UdpComm
{
public event Action<byte[]> OnBytesReceived;
//使用的接收端口号
private int destPort;
private int localPort;
private string destIP;
bool isOpen;
private UdpClient udpClient;
bool isMulticast;
Thread myThread;
public UdpComm()
{
}
~UdpComm()
{
if (udpClient != null)
udpClient.Close();
}
public void Start(string destIP, int destPort, int localPort, bool isMulticast)
{
if (isOpen == true)
return;
this.destPort = destPort;
this.localPort = localPort;
this.destIP = destIP;
this.isMulticast = isMulticast;
myThread = new Thread(ReceiveData);
myThread.IsBackground = true;
myThread.Start();
isOpen = true;
}
public void Close()
{
isOpen = false;
if (udpClient != null)
{
myThread.Abort();
udpClient.Close();
}
}
public bool IsOpen
{
get
{
return isOpen;
}
}
/// <summary>
/// 在后台运行的接收线程
/// </summary>
private void ReceiveData()
{
IPEndPoint remote = null;
if (isMulticast == true)
remote = new IPEndPoint(IPAddress.Parse(destIP), destPort);
try
{
udpClient = new UdpClient(destPort);
}
catch{return;}
if (isMulticast == true)
{
try
{
udpClient.JoinMulticastGroup(IPAddress.Parse(destIP));
udpClient.EnableBroadcast = true;
}
catch{return;}
}
while (true)
{
try
{
byte[] bytes = udpClient.Receive(ref remote);
if (bytes != null)
{
if (OnBytesReceived != null)
OnBytesReceived(bytes);
}
}
catch (Exception e){continue;}
}
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Transmit(byte[] data)
{
if (isOpen == false)
return;
try
{
IPEndPoint iep = new IPEndPoint(IPAddress.Parse(destIP), destPort);
udpClient.Send(data, data.Length, iep);
}
catch (Exception err){}
}
}
这里插一点,C#脚本里各个生命周期阶段要搞清楚:
详细解释: unity3d中脚本生命周期
因此,UdpComm类的生成与配置应该在Start()里面,销毁应该在OnDestroy()里面
UdpComm mUdp = new UdpComm();
string destIP;
int destPort;
int localPort;
bool isMulticast;
void Start () {
destIP = "234.1.1.2";
destPort = 7000;
localPort = 7001;
isMulticast = true;
mUdp.Start(destIP, destPort, localPort, isMulticast);
mUdp.OnBytesReceived += new Action<byte[]>(udp_OnBytesReceived);
}
private void OnDestroy()
{
if (mUdp!=null)
mUdp.Close();
}
写udp_OnBytesReceived函数:
void udp_OnBytesReceived(byte[] data)
{
string dataStr = Encoding.Default.GetString(data);
dataStr = dataStr.Trim();
dataStr = dataStr.Substring(1, data.Length - 2);
string[] datas = dataStr.Split(',');
cX = int.Parse(datas[0].Split(':')[1]);
cZ = int.Parse(datas[1].Split(':')[1]);
}
cX和cZ控制Cube的位置,在Update里刷新位置,运行程序,用udp工具发送数据,可以改变Cube的位置,实验成功,这样就得到了联网对unity场景的控制。
另外记录一下:
unity线程与协程—-
协同程序(coroutine)与多线程情况下的线程比较类似:有自己的堆栈,自己的局部变量,有自己的指令指针(IP,instruction pointer),但与其它协同程序共享全局变量等很多信息。
协程(协同程序): 同一时间只能执行某个协程。开辟多个协程开销不大。协程适合对某任务进行分时处理。
线程: 同一时间可以同时执行多个线程。开辟多条线程开销很大。线程适合多任务同时处理。
1.协程,即协作式程序,其思想是,一系列互相依赖的协程间依次使用CPU,每次只有一个协程工作,而其他协程处于休眠状态。协程实际上是在一个线程中,只不过每个协程对CUP进行分时,协程可以访问和使用unity的所有方法和component
2.线程,多线程是阻塞式的,每个IO都必须开启一个新的线程,但是对于多CPU的系统应该使用thread,尤其是有大量数据运算的时刻,但是IO密集型就不适合;而且thread中不能操作unity的很多方法和component
线程和协同程序的主要不同在于:在多处理器情况下,从概念上来讲多线程程序同时运行多个线程;而协同程序是通过协作来完成,在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只在必要时才会被挂起。