Hololens 使用Unet共享详细教程(二)

Hello World!我是山谷大叔~接下来我将出一系列Hololens开发教程(Hololens API解析空间共享、第三视角Spatial ViewMR交互设计视音频通讯服务器开发,多人游戏实战……),感兴趣的朋友可以关注我哦。下面开始放干货!


本节讲解网络通信这一块,Hololens MR应用是UWP的,网络这块客户端使用.NET的System.Net.Sockets会有一些问题,有些同步方法不兼容UWP平台SocketAsyncEventArg可以使用,或者使用Windows.Networking.Sockets。对UWP开发不熟悉的开发者建议先去看一下官方的入门教程,先有个大概了解。为了快速开发,了解整个过程。我先使用Unity 提供的Unet网络模块,经测试是可以跑在Hololens上的。但为了稳定性、效率、可扩展性等因素,我之后会出一个用System.Net.Sockets 或者 Networking.Sockets的开发的教程。好了下面就开始吧!

1.Unet 介绍

不熟悉Unet的同学请百度之

想了解源码的请点击Unet源码

2.网络发现NetworkDiscover

网络发现用于在局域网内,客户端收到服务端发送的广播,发现服务端,这样就可以知道服务端的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();
  }
Hololens 使用Unet共享详细教程(二)_第1张图片

3.同步Client Player

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); 
        }
    }
}

4.同步位置及命令

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

你可能感兴趣的:(Hololens开发)