Hello World!我是山谷大叔~接下来我将出一系列Hololens开发教程(Hololens API解析、空间共享、第三视角Spatial View,MR交互设计,视音频通讯,服务器开发,多人游戏实战……),感兴趣的朋友可以关注我哦。下面开始放干货!
本节讲解网络通信这一块,Hololens MR应用是UWP的,网络这块客户端使用.NET的System.Net.Sockets会有一些问题,有些同步方法不兼容UWP平台SocketAsyncEventArg可以使用,或者使用Windows.Networking.Sockets。对UWP开发不熟悉的开发者建议先去看一下官方的入门教程,先有个大概了解。为了快速开发,了解整个过程。我先使用Unity 提供的Unet网络模块,经测试是可以跑在Hololens上的。但为了稳定性、效率、可扩展性等因素,我之后会出一个用System.Net.Sockets 或者 Networking.Sockets的开发的教程。好了下面就开始吧!
网络发现用于在局域网内,客户端收到服务端发送的广播,发现服务端,这样就可以知道服务端的IP地址,用于自动连接,就不用在holoens上手势点击输入IP了,能够快速开始应用。
using UnityEngine;
using UnityEngine.Networking;
public class NetworkDiscoveryManager : NetworkDiscovery
{
//是否收到广播
public bool receivedBroadcast { get; private set; }
//控制广播发送的频率(毫秒)
private int BroadcastInterval = 1000;
//发送广播设备的IP地址
public string ServerIp { get; private set; }
private void Start()
{
//初始化NetworkDiscovery。
Initialize();
if (NetworkManager.singleton == null)
{
Debug.Log("需要 NetworkManager组件");
return ;
}
//作为客户端监听广播
StartAsClient();
//作为服务端发送广播(第一个启动的设备)
StartAsServer();
}
//开始监听广播
public override void OnReceivedBroadcast(string fromAddress, string data)
{
//从广播中接收ip数据,如果已经接收过了就不需要接收了
if (receivedBroadcast)
{
return;
}
receivedBroadcast = true;
//停止接听/发送广播
StopBroadcast();
//把广播设备的IP保存
ServerIp = fromAddress.Substring(fromAddress.LastIndexOf(':') + 1);
//设置NetworkManager的IP
NetworkManager.singleton.networkAddress = ServerIp;
//自己作为一个客户端连接上服务器IP
NetworkManager.singleton.StartClient();
}
private void StartAsServer()
{
//如果接收到了广播,说明已经有设备先广播了并作为了服务器,就接收广播,并连接
if (receivedBroadcast)
{
return;
}
//如果没有接收到了广播,说明还没有设备创建服务器
StopBroadcast();//停止接听/发送广播
//开启本机服务器(也可以作为客户端)
NetworkManager.singleton.StartHost();
//开始发送广播
StartAsServer();
}
using HoloToolkit.Examples.SharingWithUNET;
using UnityEngine;
using UnityEngine.Networking;
//数据同步频率(秒)
[NetworkSettings(sendInterval = 0.1f)]
public class ClientManager : NetworkBehaviour
{
public Transform playerHeadPos;
Transform cameraTransform;
[SyncVar] private Vector3 headLocalPosition;
[SyncVar] private Quaternion headLocalRotation;
[Command]
public void CmdTransform(Vector3 postion, Quaternion rotation)
{
if (!isLocalPlayer)
{
headLocalPosition = postion;
headLocalRotation = rotation;
}
}
void Start () {
transform.SetParent(SharedCollection.Instance.gameObject.transform);
cameraTransform= Camera.main.transform;
playerHeadPos = transform.Find("Head");
if (isLocalPlayer)
{
GlobleData.Instance.LocalMessageManager = transform.GetComponent();
}
}
void Update () {
//如果Client是远端控制的,就把远端Clientr的位置信息同步过来
if (!isLocalPlayer)
{
//同步位置和旋转
playerHeadPos.localPosition = Vector3.Lerp(playerHeadPos.localPosition, headLocalPosition, 0.1f);
playerHeadPos.localRotation = Quaternion.Lerp(playerHeadPos.localRotation,headLocalRotation,0.1f);
}
//如果Client是本地控制的,不需要同步
if (isLocalPlayer)
{
playerHeadPos.position = cameraTransform.position;
playerHeadPos.rotation = cameraTransform.rotation;
headLocalPosition = playerHeadPos.localPosition;
headLocalRotation = playerHeadPos.localRotation;
//向其他客户端同步Camera.main坐标信息
CmdTransform(headLocalPosition, headLocalRotation);
}
}
}
using HoloToolkit.Unity.InputModule;
using UnityEngine;
using UnityEngine.Networking;
public class MessageManager : NetworkBehaviour
{
[SyncVar(hook = "MessageChanged")]
public string MessageName;
[Command]
public void CmdMessageChanged(string message)
{
MessageName = message;
}
private void MessageChanged(string message)
{
//具体消息类型自己定义
if (message.Contains("Order"))
{
//同步命令到其他客户端(缩放、旋转、播放动画...)
string[] ordermsg = message.Split('|');
string order = ordermsg[1];
Invoke(order,0);
}
else if (message.Contains("HandDragPosition"))
{
//同步物体拖拽后的位置
SetPosition(message);
}
else {
print(message+"--未能解析消息!!!");
}
}
private void SetPosition(string v)
{
Vector3 position = new Vector3();
string[] pos = v.Split('|');
string name = pos[1];
position.x = float.Parse(pos[2]);
position.y = float.Parse(pos[3]); ;
position.z = float.Parse(pos[4]);
HandDraggable drag;
if (GlobleData.Instance.handDraggableDic.TryGetValue(name, out drag))
{
drag.HostTransformlocalPosition = position;
drag.transform.localPosition = position;
}
}
private void CreatSphere() {
GameManager.Instance.CreatSphere();
}
}
using System.Collections;
using System.Collections.Generic;
using HoloToolkit.Unity.InputModule;
using UnityEngine;
public class GlobleData : MonoBehaviour {
public static GlobleData Instance;
public MessageManager LocalMessageManager;
public Dictionary handDraggableDic = new Dictionary();
private void Awake()
{
Instance = this;
}
}
using HoloToolkit.Examples.SharingWithUNET;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour {
public static GameManager Instance;
public GameObject TestSphere;
//所有同步的物体需要放到SharedCollection下面,
//同步其相对于SharedCollection坐标
private Transform SharedCollectionTransform;
int TestSphereNum;
private void Awake()
{
Instance = this;
SharedCollectionTransform = SharedCollection.Instance.gameObject.transform;
}
public void CmdCreatSphere(){
GlobleData.Instance.LocalMessageManager.CmdMessageChanged("Order|CreatSphere");
}
public void CreatSphere() {
Vector3 pos = Camera.main.transform.position + Camera.main.transform.forward*2;
GameObject go= Instantiate(TestSphere, SharedCollectionTransform) as GameObject;
TestSphereNum++;
go.name = "TestSphere:" + TestSphereNum;
go.transform.position = pos;
go.transform.LookAt(Camera.main.transform);
}
}
Hololens 使用Unet共享详细教程(三)
如果有什么疑问或者发现笔者的错误请留言!
下一节讲解和空间锚点相关的内容~~~
获取源码请在群资料中下载( Hololens Unet Share 001)
Hololens MR 技术交流群 211031265