在前面的几篇文章中分别在桌面客户端和Web客户端中实现了MQTT协议的功能,为了丰富MQTT协议的使用场景,本篇文章就来尝试如何在Unity3D中实现MQTT协议的部分功能。
1.引入m2mqtt
m2mqtt是比较流行的MQTT开源类库之一,在GitHub上可以找到专为Unity3D封装的m2mqtt库,名为Unity3D_MQTT,地址为
将项目下载到本地,使用Unity3D打开,我用的版本是5.5,这个项目的版本更早,毕竟是几年前的,版本为5.0。项目中自带了一个示例脚本供参考,为了方便后面的使用,将MQTT相关文件打包输出。(去掉了不必要的场景文件和示例脚本)
之后使用Unity3D新建一个本地项目,导入刚刚输出的资源包。新增一个空物体命名为MainTarget做后面挂载脚本用,同时将当前场景保存为Home场景作为本项目的主场景。
2.页面布局
Unity3D的页面布局与WPF、HTML有所不同,WPF和HTML都是基于XML结构的标签型布局。简单起见,Uniy3D布局就使用其自带的UI系统。
首先在场景中添加一个画板Canvas,所有UI都将在画板上呈现,之后在Canvas上添加一个布局空间Panel。
注意:Unity3D默认是三维视角的,为了确保UI始终能在屏幕即主相机范围内显示,需要对Canvas属性进行修改。
设置步骤如下:
- 设置Canvas所在图层为“UI”
- 设置Canvas的Render Mode渲染模式为Screen Space-Camera
- 设置Render Camera为当前主相机MainCamera
然后按照和前两篇文章同样的需求依次添加UI对象,最终构建出界面。
3.代码实现
新建一个C#脚本MainTargetScript,并将脚本附加到对象MainTarget上,主要的功能都将在MainTargetScript.cs中实现。
3.1. UI绑定
与WPF、HTML不同,Unity3D有自身一套独特的交互体系,所以在UI交互上按照Unity3D的规则来做。为了简便,此处采用的是比较原始的方法。
在MainTargetScript类中定义UI对象:
#region UI对象
public InputField inputIP;//IP地址输入框
public InputField inputPort;//端口号输入框
public Button btnConnect;//连接按钮
public Button btnDisconnect;//断开按钮
public Toggle togTopic1;//主题选择框
public Toggle togTopic2;//主题选择框
public Toggle togTopic3;//主题选择框
public Toggle togTopic4;//主题选择框
public Toggle togTopic5;//主题选择框
public Button btnSubscribe;//订阅按钮
public Text txtResult;//结果输出文本框
public Dropdown dropTopics;//发布主题选择框
public InputField inputContent;//发布内容
public Button btnPublish;//发布按钮
public GameObject target;
#endregion
然后在Unity3D编辑器中将UI对象与脚本中的公共对象绑定。
3.2. UI交互事件绑定
绑定好UI控件后,在脚本MainTargetScript的Start事件中绑定按钮、下拉框等的触发事件。
void Start () {
btnConnect.onClick.AddListener(btnConnect_Click);
btnDisconnect.onClick.AddListener(btnDisconnect_Click);
btnSubscribe.onClick.AddListener(btnSubscribe_Click);
btnPublish.onClick.AddListener(btnPublish_Click);
dropTopics.onValueChanged.AddListener(dropTopics_ValueChange);
togTopic1.onValueChanged.AddListener(togTopic_ValueChange);
togTopic2.onValueChanged.AddListener(togTopic_ValueChange);
togTopic3.onValueChanged.AddListener(togTopic_ValueChange);
togTopic4.onValueChanged.AddListener(togTopic_ValueChange);
togTopic5.onValueChanged.AddListener(togTopic_ValueChange);
}
#region 订阅主题选中事件
private void togTopic_ValueChange(bool arg0)
{
}
#endregion
#region 发布主题选中事件
private void dropTopics_ValueChange(int arg0)
{
}
#endregion
#region 发布按钮点击事件
private void btnPublish_Click()
{
}
#endregion
#region 订阅按钮点击事件
private void btnSubscribe_Click()
{
}
#endregion
#region 断开按钮点击事件
private void btnDisconnect_Click()
{
}
#endregion
#region 连接按钮点击事件
private void btnConnect_Click()
{
}
#endregion
3.3. MQTT功能实现
m2mqtt与之前在WPF客户端使用的MQTTNet类库均为.Net平台,所以基本用法大同小异,只是一些数据结构和方法参数不太一样。下面就直接在UI交互事件中完善相关代码:
#region 变量
private string[] allTopics = { "/data/alarm", "/data/message", "/data/notify", "/action/start", "/action/stop" };
private List<string> selectedTopics;//选中订阅的主题
private string currentTopic;//选中发布的主题
private MqttClient client;//客户端
#endregion
// Use this for initialization
void Start () {
selectedTopics = new List<string> { "/data/alarm", "/data/message", "/data/notify", "/action/start", "/action/stop" };
btnConnect.onClick.AddListener(btnConnect_Click);
btnDisconnect.onClick.AddListener(btnDisconnect_Click);
btnSubscribe.onClick.AddListener(btnSubscribe_Click);
btnPublish.onClick.AddListener(btnPublish_Click);
dropTopics.onValueChanged.AddListener(dropTopics_ValueChange);
togTopic1.onValueChanged.AddListener(togTopic_ValueChange);
togTopic2.onValueChanged.AddListener(togTopic_ValueChange);
togTopic3.onValueChanged.AddListener(togTopic_ValueChange);
togTopic4.onValueChanged.AddListener(togTopic_ValueChange);
togTopic5.onValueChanged.AddListener(togTopic_ValueChange);
}
#region 订阅主题选中事件
private void togTopic_ValueChange(bool arg0)
{
//获取当前选中的对象
var current = UnityEngine.EventSystems.EventSystem.current.currentSelectedGameObject;
string topic = current.GetComponentInChildren<Text>().text;
if (arg0)//选中
{
selectedTopics.Add(topic);
}
else//未选中
{
selectedTopics.Remove(topic);
}
}
#endregion
#region 发布主题选中事件
private void dropTopics_ValueChange(int arg0)
{
currentTopic = allTopics[arg0];
}
#endregion
// Update is called once per frame
void Update () {
}
#region 发布按钮点击事件
private void btnPublish_Click()
{
string content = inputContent.text;
if (client!=null&&!string.IsNullOrEmpty(currentTopic)&&!string.IsNullOrEmpty(content))
{
client.Publish(currentTopic, System.Text.Encoding.UTF8.GetBytes(content), MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, true);
}
}
#endregion
#region 订阅按钮点击事件
private void btnSubscribe_Click()
{
if (client!=null&&selectedTopics!=null)
{
client.Subscribe(selectedTopics.ToArry(), new byte[] { MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE });
}
}
#endregion
#region 断开按钮点击事件
private void btnDisconnect_Click()
{
if (client!=null)
{
client.Disconnect();
}
}
#endregion
#region 连接按钮点击事件
private void btnConnect_Click()
{
string txtIP = inputIP.text;
string txtPort = inputPort.text;
string clientId = Guid.NewGuid().ToString();
string username = "admin";
string password = "password";
//客户端实例化
client = new MqttClient(IPAddress.Parse(txtIP), int.Parse(txtPort),false,null);
//绑定接收事件
client.MqttMsgPublishReceived += Client_MqttMsgPublishReceived;
//连接服务端
client.Connect(clientId, username, password);
}
//接收到发布的消息
private void Client_MqttMsgPublishReceived(object sender, MqttMsgPublishEventArgs e)
{
Debug.Log("Topic:"+e.Topic);
string tmp = System.Text.Encoding.UTF8.GetString(e.Message);
Debug.Log("Message" + tmp);
txtResult.text=tmp;
}
#endregion
代码编写完成后打包发布成exe程序。
4.测试实例
这次测试分别打开一个Unity3D客户端、一个WPF客户端、一个Web客户端。同样还是打开Apollo的管理界面查看结果。
分别连接,服务端管理界面上可以查到tcp模式有两个终端,ws(WebSocket)模式有一个。连接功能正常。
再测试下订阅、发布功能。订阅相应主题的能收到,没有订阅相应主题的就收不到。
大功告成,至此在Unity3D上实现MQTT协议就完成了。
勤能补拙,不断试错。