HoloLens (1. generace) sdílení 240: více zařízení HoloLens

Důležité

výukové kurzy Mixed reality Academy byly navrženy s ohledem na HoloLens (1. generace), Unity 2017 a moderních sluchátek s poutavými realitami. V takovém případě je důležité nechat tyto kurzy na místě pro vývojáře, kteří stále hledají pokyny k vývoji pro tato zařízení. tyto kurzy nebudou aktualizovány na nejnovější sady nástrojů nebo interakce používané pro HoloLens 2 a nemusí být kompatibilní s novějšími verzemi Unity. Budou zachovány, aby pokračovaly v práci na podporovaných zařízeních. byla publikována nová série kurzů pro HoloLens 2.

Hologramy se předává na našem světě, protože se na něj připravujeme v prostoru. HoloLens zachovává hologramy pomocí různých systémů souřadnic k udržení přehledu umístění a orientace objektů. Když tyto systémy souřadnic sdílíme mezi zařízeními, můžeme vytvořit sdílené prostředí, které nám umožní částečně využít sdílený holografický svět.

V tomto kurzu provedeme následující:

  • Nastavte síť pro sdílené prostředí.
  • sdílejte hologramy v zařízeních HoloLens.
  • Podívejte se na další lidi našeho sdíleného holografického světa.
  • Vytvořte sdílené interaktivní prostředí, kde můžete cílit na jiné hráče a na ně spustit projektily.

Podpora zařízení

Kurz HoloLens Moderní sluchátka
MR Sharing 240: více zařízení HoloLens ✔️

Než začnete

Požadavky

soubory Project

  • Stáhněte soubory požadované projektem. Vyžaduje Unity 2017,2 nebo novější.
    • Pokud pořád potřebujete podporu Unity 5,6, použijte prosím tuto verzi.
    • Pokud pořád potřebujete podporu Unity 5,5, použijte prosím tuto verzi.
    • Pokud pořád potřebujete podporu Unity 5,4, použijte prosím tuto verzi.
  • Zrušte archivaci souborů na plochu nebo jiné snadné umístění. Název složky nechte jako SharedHolograms.

Poznámka

Pokud chcete před stažením projít zdrojový kód, je k dispozici na GitHub.

Kapitola 1 – Holo World

V této kapitole nastavíme náš první projekt Unity a provedete postup sestavení a nasazení.

Cíle

  • Nastavení Unity pro vývoj holografických aplikací
  • Podívejte se na svůj hologram!

Pokyny

  • Spusťte Unity.
  • Vyberte Otevřít.
  • Jako složku SharedHolograms , kterou jste předtím nearchivovali, zadejte umístění.
  • vyberte Project název a klikněte na vybrat složku.
  • V hierarchiiklikněte pravým tlačítkem myši na hlavní kameru a vyberte Odstranit.
  • Ve složce HoloToolkit-Sharing-240/Prefabs/Camera Najděte hlavní kameru Prefab.
  • Přetáhněte hlavní kameru do hierarchie.
  • V hierarchiiklikněte na vytvořit a vytvořit prázdné.
  • Klikněte pravým tlačítkem na nový GameObject a vyberte Přejmenovat.
  • Přejmenujte GameObject na hologramcollection.
  • Vyberte objekt hologramcollection v hierarchii.
  • V inspektoru nastavte pozici transformace na: X: 0, Y:-0,25, Z: 2.
  • do složky Hologramy na panelu Projectnajděte prostředek EnergyHub .
  • přetáhněte objekt EnergyHub z panelu Project do hierarchie jako podřízenou položku aplikace hologramcollection.
  • Vybrat soubor Uložit scénu jako...
  • Pojmenujte scénu SharedHolograms a klikněte na Uložit.
  • Kliknutím na tlačítko Přehrát v Unity zobrazíte náhled vašich hologramů.
  • Druhým stisknutím klávesy Play zastavíte režim náhledu.

Exportujte projekt z Unity do Visual Studio

  • v Unity vyberte Nastavení sestavení souboru.
  • Kliknutím na tlačítko Přidat otevřené scény přidáte scénu.
  • v seznamu platforma vyberte Univerzální platforma Windows a klikněte na přepínač platforma.
  • Nastavte sadu SDK na Universal 10.
  • nastavte cílové zařízení na HoloLens a typ buildu UWP na D3D.
  • Ověřte projekty Unity jazyka C#.
  • Klikněte na sestavit.
  • V zobrazeném okně Průzkumník souborů vytvořte novou složku s názvem "App".
  • Jediné klikněte na složku aplikace .
  • Stiskněte Vybrat složku.
  • Po dokončení Unity se zobrazí okno Průzkumníka souborů.
  • Otevřete složku aplikace .
  • Otevřete SharedHolograms. sln a spusťte Visual Studio.
  • pomocí horního panelu nástrojů v Visual Studio změňte cíl z ladění na Release a z ARM na X86.
  • Klikněte na šipku rozevíracího seznamu vedle položku místní počítač a vyberte vzdálené zařízení.
    • Nastavte adresu na název nebo IP adresu vašeho HoloLens. pokud si nejste jisti ip adresou zařízení, podívejte se na Nastavení &> rozšířené možnosti sítě Internet Internet nebo požádejte Cortana &
    • Režim ověřování ponechejte nastavený na univerzální.
    • Klikněte na Vybrat .
  • Klikněte na ladění Spustit bez ladění nebo stiskněte klávesy CTRL + F5. Pokud se do vašeho zařízení poprvé nasadíte, budete ho muset spárovat s Visual Studio.
  • dejte na svůj HoloLens a najděte EnergyHub hologram.

Kapitola 2 – interakce

V této kapitole budeme pracovat s našimi hologramy. Nejprve přidáme kurzor k vizualizaci našich pohleduů. Pak přidáme gesta a použijeme naši ruku k umístění našich hologramů do prostoru.

Cíle

  • Pro řízení kurzoru použijte pohledu Input.
  • Pro práci s hologramy použijte vstup gesta.

Pokyny

Pohledu

  • Na panelu hierarchie vyberte objekt hologramcollection .
  • V panelu Inspektor klikněte na tlačítko Přidat komponentu .
  • Do nabídky zadejte do vyhledávacího pole pohledu Manager. Vyberte výsledek hledání.
  • Ve složce HoloToolkit-Sharing-240\Prefabs\Input vyhledejte prostředek kurzoru .
  • Přetáhněte Asset kurzorem na hierarchii.

Speciálních

  • Na panelu hierarchie vyberte objekt hologramcollection .
  • Klikněte na Přidat komponentu a do vyhledávacího pole zadejte správce gesta . Vyberte výsledek hledání.
  • Na panelu hierarchierozbalte položku hologramcollection.
  • Vyberte podřízený objekt EnergyHub .
  • V panelu Inspektor klikněte na tlačítko Přidat komponentu .
  • Do nabídky zadejte umístění pro hledání na hologramovémpoli. Vyberte výsledek hledání.
  • Uložte scénu tak, že vyberete soubor Uložit scénu.

Nasazení a poslech

  • pomocí pokynů z předchozí kapitoly sestavte a nasaďte do svého HoloLens.
  • jakmile se aplikace na vašem HoloLens spustí, posuňte svou hlavu kolem a všimněte si, jak EnergyHub sleduje vaše pohledu.
  • Všimněte si, jak se kurzor zobrazí při pohleduí na hologramu, a když se gazing na hologram, změní se bodově světle.
  • Hologram můžete umístit do vzduchového klepnutí. V tuto chvíli v našem projektu můžete umístit hologram jenom jednou (znovu nasadit a zkusit to znovu).

Kapitola 3 – sdílené souřadnice

Je zábavné zobrazit a pracovat s hologramy, ale pojďme dál. Nastavili jsme naše první sdílené prostředí – hologram všichni uvidí dohromady.

Cíle

  • Nastavte síť pro sdílené prostředí.
  • Vytvořte společný referenční bod.
  • Sdílejte systémy souřadnic mezi zařízeními.
  • Všichni vidí stejný hologram!

Poznámka

Aby se aplikace mohla připojit k serveru pro sdílení, musí být deklarovány možnosti InternetClientServer a PrivateNetworkClientServer . to se provádí už v Hologramy 240, ale mějte na paměti, že se zapamatujte na vaše vlastní projekty.

  1. v editoru Unity přejděte na nastavení přehrávače tak, že přejdete na upravit > Project Nastavení player. >
  2. klikněte na kartu Windows Store.
  3. v části publikování možností Nastavení se > podívejte na schopnost > a PrivateNetworkClientServer funkce.

Pokyny

  • na panelu Project přejděte do složky HoloToolkit-Sharing-240\Prefabs\Sharing .
  • Přetáhněte Prefab sdílení na panel hierarchie.

Dál musíme spustit službu sdílení. Tento krok vyžaduje jenom jeden počítač se sdíleným prostředím.

  • V Unity – v horní nabídce vyberte nabídku HoloToolkit-Sharing-240.
  • V rozevíracím seznamu vyberte položku Služba pro sdílení spouštěného obsahu.
  • Zaškrtněte možnost privátní síť a po zobrazení výzvy brány firewall klikněte na možnost Povolení přístupu .
  • Poznamenejte si adresu IPv4 zobrazenou v okně sdílení služby Service Console. Jedná se o stejnou IP adresu jako počítač, ve kterém je služba spuštěná.

Postupujte podle dalších pokynů na všech počítačích , které se budou připojovat ke sdílenému prostředí.

  • V hierarchiivyberte objekt pro sdílení .
  • V inspektoruv součásti fáze sdílení změňte adresu serveru z ' localhost ' na IPv4 adresu počítače, na kterém běží SharingService.exe.
  • V hierarchii vyberte objekt hologramcollection .
  • V inspektoru klikněte na tlačítko Přidat komponentu .
  • Do vyhledávacího pole zadejte Import exportovat správce kotev. Vyberte výsledek hledání.
  • na panelu Project přejděte do složky skripty .
  • Dvojím kliknutím na skript HologramPlacement ho otevřete v Visual Studio.
  • Nahraďte obsah následujícím kódem.
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.Windows.Speech;
using Academy.HoloToolkit.Unity;
using Academy.HoloToolkit.Sharing;

public class HologramPlacement : Singleton<HologramPlacement>
{
    /// <summary>
    /// Tracks if we have been sent a transform for the anchor model.
    /// The anchor model is rendered relative to the actual anchor.
    /// </summary>
    public bool GotTransform { get; private set; }

    private bool animationPlayed = false;

    void Start()
    {
        // We care about getting updates for the anchor transform.
        CustomMessages.Instance.MessageHandlers[CustomMessages.TestMessageID.StageTransform] = this.OnStageTransform;

        // And when a new user join we will send the anchor transform we have.
        SharingSessionTracker.Instance.SessionJoined += Instance_SessionJoined;
    }

    /// <summary>
    /// When a new user joins we want to send them the relative transform for the anchor if we have it.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Instance_SessionJoined(object sender, SharingSessionTracker.SessionJoinedEventArgs e)
    {
        if (GotTransform)
        {
            CustomMessages.Instance.SendStageTransform(transform.localPosition, transform.localRotation);
        }
    }

    void Update()
    {
        if (GotTransform)
        {
            if (ImportExportAnchorManager.Instance.AnchorEstablished &&
                animationPlayed == false)
            {
                // This triggers the animation sequence for the anchor model and 
                // puts the cool materials on the model.
                GetComponent<EnergyHubBase>().SendMessage("OnSelect");
                animationPlayed = true;
            }
        }
        else
        {
            transform.position = Vector3.Lerp(transform.position, ProposeTransformPosition(), 0.2f);
        }
    }

    Vector3 ProposeTransformPosition()
    {
        // Put the anchor 2m in front of the user.
        Vector3 retval = Camera.main.transform.position + Camera.main.transform.forward * 2;

        return retval;
    }

    public void OnSelect()
    {
        // Note that we have a transform.
        GotTransform = true;

        // And send it to our friends.
        CustomMessages.Instance.SendStageTransform(transform.localPosition, transform.localRotation);
    }

    /// <summary>
    /// When a remote system has a transform for us, we'll get it here.
    /// </summary>
    /// <param name="msg"></param>
    void OnStageTransform(NetworkInMessage msg)
    {
        // We read the user ID but we don't use it here.
        msg.ReadInt64();

        transform.localPosition = CustomMessages.Instance.ReadVector3(msg);
        transform.localRotation = CustomMessages.Instance.ReadQuaternion(msg);

        // The first time, we'll want to send the message to the anchor to do its animation and
        // swap its materials.
        if (GotTransform == false)
        {
            GetComponent<EnergyHubBase>().SendMessage("OnSelect");
        }

        GotTransform = true;
    }

    public void ResetStage()
    {
        // We'll use this later.
    }
}
  • Zpátky v Unity vyberte v panelu hierarchiepoložku hologramcollection .
  • V panelu Inspektor klikněte na tlačítko Přidat komponentu .
  • Do nabídky zadejte do vyhledávacího pole správce stavu aplikace. Vyberte výsledek hledání.

Nasazení a poslech

  • sestavte projekt pro zařízení HoloLens.
  • určete jednu HoloLens pro nasazení na první. Aby bylo možné umístit EnergyHub (může to trvat přibližně 30-60 sekund), budete muset počkat na nahrání kotvy do služby. Až se nahrávání dokončí, gesta klepnutí se budou ignorovat.
  • po umístění EnergyHub se jeho umístění nahraje do služby a můžete ho nasadit na všechna ostatní HoloLens zařízení.
  • když se HoloLens poprvé připojí k relaci, umístění EnergyHub nemusí být na tomto zařízení správné. Po stažení kotvy a umístění EnergyHub ze služby by se však EnergyHub měla přeskočit na nové sdílené umístění. pokud k tomu nedojde během ~ 30-60 sekund, přejdeme do umístění původní HoloLens při nastavení kotvy, aby se shromáždilo více prostředí. Pokud se umístění pořád nezamkne, znovu ho nasaďte do zařízení.
  • Když všechna zařízení jsou připravená a spuštěná, vyhledejte EnergyHub. Můžete si všechno vyjádřit od pozice hologramu a směr, na který se text odkazuje?

Kapitola 4 – zjišťování

Všichni uživatelé teď můžou vidět stejný hologram! Teď se podívejme na všechny ostatní připojené ke sdílenému holografickému světě. v této kapitole vytvoříme hlavní umístění a rotaci všech ostatních HoloLensch zařízení ve stejné relaci sdílení.

Cíle

  • Podívejte se na naše sdílené prostředí.
  • Vyberte a sdílejte miniaturu přehrávače.
  • Připojte miniaturu přehrávače vedle hlaví všech.

Pokyny

  • na panelu Project přejděte do složky Hologramy .
  • Přetáhněte PlayerAvatarStore do hierarchie.
  • na panelu Project přejděte do složky skripty .
  • Dvojím kliknutím na skript AvatarSelector ho otevřete v Visual Studio.
  • Nahraďte obsah následujícím kódem.
using UnityEngine;
using Academy.HoloToolkit.Unity;

/// <summary>
/// Script to handle the user selecting the avatar.
/// </summary>
public class AvatarSelector : MonoBehaviour
{
    /// <summary>
    /// This is the index set by the PlayerAvatarStore for the avatar.
    /// </summary>
    public int AvatarIndex { get; set; }

    /// <summary>
    /// Called when the user is gazing at this avatar and air-taps it.
    /// This sends the user's selection to the rest of the devices in the experience.
    /// </summary>
    void OnSelect()
    {
        PlayerAvatarStore.Instance.DismissAvatarPicker();

        LocalPlayerManager.Instance.SetUserAvatar(AvatarIndex);
    }

    void Start()
    {
        // Add Billboard component so the avatar always faces the user.
        Billboard billboard = gameObject.GetComponent<Billboard>();
        if (billboard == null)
        {
            billboard = gameObject.AddComponent<Billboard>();
        }

        // Lock rotation along the Y axis.
        billboard.PivotAxis = PivotAxis.Y;
    }
}
  • V hierarchii vyberte objekt hologramcollection .
  • V okně inspektor klikněte na Přidat komponentu.
  • Do vyhledávacího pole zadejte Local Player Manager. Vyberte výsledek hledání.
  • V hierarchii vyberte objekt hologramcollection .
  • V okně inspektor klikněte na Přidat komponentu.
  • Do vyhledávacího pole zadejte Správce vzdáleného přehrávače. Vyberte výsledek hledání.
  • Otevřete skript HologramPlacement v Visual Studio.
  • Nahraďte obsah následujícím kódem.
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.Windows.Speech;
using Academy.HoloToolkit.Unity;
using Academy.HoloToolkit.Sharing;

public class HologramPlacement : Singleton<HologramPlacement>
{
    /// <summary>
    /// Tracks if we have been sent a transform for the model.
    /// The model is rendered relative to the actual anchor.
    /// </summary>
    public bool GotTransform { get; private set; }

    /// <summary>
    /// When the experience starts, we disable all of the rendering of the model.
    /// </summary>
    List<MeshRenderer> disabledRenderers = new List<MeshRenderer>();

    void Start()
    {
        // When we first start, we need to disable the model to avoid it obstructing the user picking a hat.
        DisableModel();

        // We care about getting updates for the model transform.
        CustomMessages.Instance.MessageHandlers[CustomMessages.TestMessageID.StageTransform] = this.OnStageTransform;

        // And when a new user join we will send the model transform we have.
        SharingSessionTracker.Instance.SessionJoined += Instance_SessionJoined;
    }

    /// <summary>
    /// When a new user joins we want to send them the relative transform for the model if we have it.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Instance_SessionJoined(object sender, SharingSessionTracker.SessionJoinedEventArgs e)
    {
        if (GotTransform)
        {
            CustomMessages.Instance.SendStageTransform(transform.localPosition, transform.localRotation);
        }
    }

    /// <summary>
    /// Turns off all renderers for the model.
    /// </summary>
    void DisableModel()
    {
        foreach (MeshRenderer renderer in gameObject.GetComponentsInChildren<MeshRenderer>())
        {
            if (renderer.enabled)
            {
                renderer.enabled = false;
                disabledRenderers.Add(renderer);
            }
        }

        foreach (MeshCollider collider in gameObject.GetComponentsInChildren<MeshCollider>())
        {
            collider.enabled = false;
        }
    }

    /// <summary>
    /// Turns on all renderers that were disabled.
    /// </summary>
    void EnableModel()
    {
        foreach (MeshRenderer renderer in disabledRenderers)
        {
            renderer.enabled = true;
        }

        foreach (MeshCollider collider in gameObject.GetComponentsInChildren<MeshCollider>())
        {
            collider.enabled = true;
        }

        disabledRenderers.Clear();
    }


    void Update()
    {
        // Wait till users pick an avatar to enable renderers.
        if (disabledRenderers.Count > 0)
        {
            if (!PlayerAvatarStore.Instance.PickerActive &&
            ImportExportAnchorManager.Instance.AnchorEstablished)
            {
                // After which we want to start rendering.
                EnableModel();

                // And if we've already been sent the relative transform, we will use it.
                if (GotTransform)
                {
                    // This triggers the animation sequence for the model and
                    // puts the cool materials on the model.
                    GetComponent<EnergyHubBase>().SendMessage("OnSelect");
                }
            }
        }
        else if (GotTransform == false)
        {
            transform.position = Vector3.Lerp(transform.position, ProposeTransformPosition(), 0.2f);
        }
    }

    Vector3 ProposeTransformPosition()
    {
        // Put the model 2m in front of the user.
        Vector3 retval = Camera.main.transform.position + Camera.main.transform.forward * 2;

        return retval;
    }

    public void OnSelect()
    {
        // Note that we have a transform.
        GotTransform = true;

        // And send it to our friends.
        CustomMessages.Instance.SendStageTransform(transform.localPosition, transform.localRotation);
    }

    /// <summary>
    /// When a remote system has a transform for us, we'll get it here.
    /// </summary>
    /// <param name="msg"></param>
    void OnStageTransform(NetworkInMessage msg)
    {
        // We read the user ID but we don't use it here.
        msg.ReadInt64();

        transform.localPosition = CustomMessages.Instance.ReadVector3(msg);
        transform.localRotation = CustomMessages.Instance.ReadQuaternion(msg);

        // The first time, we'll want to send the message to the model to do its animation and
        // swap its materials.
        if (disabledRenderers.Count == 0 && GotTransform == false)
        {
            GetComponent<EnergyHubBase>().SendMessage("OnSelect");
        }

        GotTransform = true;
    }

    public void ResetStage()
    {
        // We'll use this later.
    }
}
  • Otevřete skript AppStateManager v Visual Studio.
  • Nahraďte obsah následujícím kódem.
using UnityEngine;
using Academy.HoloToolkit.Unity;

/// <summary>
/// Keeps track of the current state of the experience.
/// </summary>
public class AppStateManager : Singleton<AppStateManager>
{
    /// <summary>
    /// Enum to track progress through the experience.
    /// </summary>
    public enum AppState
    {
        Starting = 0,
        WaitingForAnchor,
        WaitingForStageTransform,
        PickingAvatar,
        Ready
    }

    /// <summary>
    /// Tracks the current state in the experience.
    /// </summary>
    public AppState CurrentAppState { get; set; }

    void Start()
    {
        // We start in the 'picking avatar' mode.
        CurrentAppState = AppState.PickingAvatar;

        // We start by showing the avatar picker.
        PlayerAvatarStore.Instance.SpawnAvatarPicker();
    }

    void Update()
    {
        switch (CurrentAppState)
        {
            case AppState.PickingAvatar:
                // Avatar picking is done when the avatar picker has been dismissed.
                if (PlayerAvatarStore.Instance.PickerActive == false)
                {
                    CurrentAppState = AppState.WaitingForAnchor;
                }
                break;
            case AppState.WaitingForAnchor:
                if (ImportExportAnchorManager.Instance.AnchorEstablished)
                {
                    CurrentAppState = AppState.WaitingForStageTransform;
                    GestureManager.Instance.OverrideFocusedObject = HologramPlacement.Instance.gameObject;
                }
                break;
            case AppState.WaitingForStageTransform:
                // Now if we have the stage transform we are ready to go.
                if (HologramPlacement.Instance.GotTransform)
                {
                    CurrentAppState = AppState.Ready;
                    GestureManager.Instance.OverrideFocusedObject = null;
                }
                break;
        }
    }
}

Nasazení a poslech

  • sestavte a nasaďte projekt do zařízení HoloLens.
  • Když uslyšíte zvuk, který se pokoušíte testovat, najděte nabídku výběr avataru a vyberte miniaturu pomocí gesta na vzduchu.
  • pokud si nejste hledali žádné hologramy, bod světla kolem kurzoru se změní na jinou barvu, když HoloLens komunikuje se službou: inicializace (tmavě fialová), stažení kotvy (zelená), import/export dat umístění (žlutá), nahrání kotvy (modré). Pokud je váš bod kolem kurzoru výchozí barvou (světle fialová), budete připraveni pracovat s ostatními hráči v relaci.
  • Podívejte se na ostatní lidi připojené k vašemu prostoru – na holografickém robotovi se dostanete nad rameny a mimicking své hlavní pohyby.

Kapitola 5 – umístění

V této kapitole se dá kotva dát umístit na reálné plochy. Použijeme sdílené souřadnice k umístění tohoto kotvy do středního bodu mezi všemi připojenými ke sdíleným prostředím.

Cíle

  • Umístěte hologramy na mřížku prostorového mapování na základě pozice účastníků.

Pokyny

  • na panelu Project přejděte do složky Hologramy .
  • Přetáhněte CustomSpatialMapping Prefab do hierarchie.
  • na panelu Project přejděte do složky skripty .
  • Dvojím kliknutím na skript AppStateManager ho otevřete v Visual Studio.
  • Nahraďte obsah následujícím kódem.
using UnityEngine;
using Academy.HoloToolkit.Unity;

/// <summary>
/// Keeps track of the current state of the experience.
/// </summary>
public class AppStateManager : Singleton<AppStateManager>
{
    /// <summary>
    /// Enum to track progress through the experience.
    /// </summary>
    public enum AppState
    {
        Starting = 0,
        PickingAvatar,
        WaitingForAnchor,
        WaitingForStageTransform,
        Ready
    }

    // The object to call to make a projectile.
    GameObject shootHandler = null;

    /// <summary>
    /// Tracks the current state in the experience.
    /// </summary>
    public AppState CurrentAppState { get; set; }

    void Start()
    {
        // The shootHandler shoots projectiles.
        if (GetComponent<ProjectileLauncher>() != null)
        {
            shootHandler = GetComponent<ProjectileLauncher>().gameObject;
        }

        // We start in the 'picking avatar' mode.
        CurrentAppState = AppState.PickingAvatar;

        // Spatial mapping should be disabled when we start up so as not
        // to distract from the avatar picking.
        SpatialMappingManager.Instance.StopObserver();
        SpatialMappingManager.Instance.gameObject.SetActive(false);

        // On device we start by showing the avatar picker.
        PlayerAvatarStore.Instance.SpawnAvatarPicker();
    }

    public void ResetStage()
    {
        // If we fall back to waiting for anchor, everything needed to
        // get us into setting the target transform state will be setup.
        if (CurrentAppState != AppState.PickingAvatar)
        {
            CurrentAppState = AppState.WaitingForAnchor;
        }

        // Reset the underworld.
        if (UnderworldBase.Instance)
        {
            UnderworldBase.Instance.ResetUnderworld();
        }
    }

    void Update()
    {
        switch (CurrentAppState)
        {
            case AppState.PickingAvatar:
                // Avatar picking is done when the avatar picker has been dismissed.
                if (PlayerAvatarStore.Instance.PickerActive == false)
                {
                    CurrentAppState = AppState.WaitingForAnchor;
                }
                break;
            case AppState.WaitingForAnchor:
                // Once the anchor is established we need to run spatial mapping for a
                // little while to build up some meshes.
                if (ImportExportAnchorManager.Instance.AnchorEstablished)
                {
                    CurrentAppState = AppState.WaitingForStageTransform;
                    GestureManager.Instance.OverrideFocusedObject = HologramPlacement.Instance.gameObject;

                    SpatialMappingManager.Instance.gameObject.SetActive(true);
                    SpatialMappingManager.Instance.DrawVisualMeshes = true;
                    SpatialMappingDeformation.Instance.ResetGlobalRendering();
                    SpatialMappingManager.Instance.StartObserver();
                }
                break;
            case AppState.WaitingForStageTransform:
                // Now if we have the stage transform we are ready to go.
                if (HologramPlacement.Instance.GotTransform)
                {
                    CurrentAppState = AppState.Ready;
                    GestureManager.Instance.OverrideFocusedObject = shootHandler;
                }
                break;
        }
    }
}
  • na panelu Project přejděte do složky skripty .
  • Dvojím kliknutím na skript HologramPlacement ho otevřete v Visual Studio.
  • Nahraďte obsah následujícím kódem.
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.Windows.Speech;
using Academy.HoloToolkit.Unity;
using Academy.HoloToolkit.Sharing;

public class HologramPlacement : Singleton<HologramPlacement>
{
    /// <summary>
    /// Tracks if we have been sent a transform for the model.
    /// The model is rendered relative to the actual anchor.
    /// </summary>
    public bool GotTransform { get; private set; }

    /// <summary>
    /// When the experience starts, we disable all of the rendering of the model.
    /// </summary>
    List<MeshRenderer> disabledRenderers = new List<MeshRenderer>();

    /// <summary>
    /// We use a voice command to enable moving the target.
    /// </summary>
    KeywordRecognizer keywordRecognizer;

    void Start()
    {
        // When we first start, we need to disable the model to avoid it obstructing the user picking a hat.
        DisableModel();

        // We care about getting updates for the model transform.
        CustomMessages.Instance.MessageHandlers[CustomMessages.TestMessageID.StageTransform] = this.OnStageTransform;

        // And when a new user join we will send the model transform we have.
        SharingSessionTracker.Instance.SessionJoined += Instance_SessionJoined;

        // And if the users want to reset the stage transform.
        CustomMessages.Instance.MessageHandlers[CustomMessages.TestMessageID.ResetStage] = this.OnResetStage;

        // Setup a keyword recognizer to enable resetting the target location.
        List<string> keywords = new List<string>();
        keywords.Add("Reset Target");
        keywordRecognizer = new KeywordRecognizer(keywords.ToArray());
        keywordRecognizer.OnPhraseRecognized += KeywordRecognizer_OnPhraseRecognized;
        keywordRecognizer.Start();
    }

    /// <summary>
    /// When the keyword recognizer hears a command this will be called.  
    /// In this case we only have one keyword, which will re-enable moving the
    /// target.
    /// </summary>
    /// <param name="args">information to help route the voice command.</param>
    private void KeywordRecognizer_OnPhraseRecognized(PhraseRecognizedEventArgs args)
    {
        ResetStage();
    }

    /// <summary>
    /// Resets the stage transform, so users can place the target again.
    /// </summary>
    public void ResetStage()
    {
        GotTransform = false;

        // AppStateManager needs to know about this so that
        // the right objects get input routed to them.
        AppStateManager.Instance.ResetStage();

        // Other devices in the experience need to know about this as well.
        CustomMessages.Instance.SendResetStage();

        // And we need to reset the object to its start animation state.
        GetComponent<EnergyHubBase>().ResetAnimation();
    }

    /// <summary>
    /// When a new user joins we want to send them the relative transform for the model if we have it.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Instance_SessionJoined(object sender, SharingSessionTracker.SessionJoinedEventArgs e)
    {
        if (GotTransform)
        {
            CustomMessages.Instance.SendStageTransform(transform.localPosition, transform.localRotation);
        }
    }

    /// <summary>
    /// Turns off all renderers for the model.
    /// </summary>
    void DisableModel()
    {
        foreach (MeshRenderer renderer in gameObject.GetComponentsInChildren<MeshRenderer>())
        {
            if (renderer.enabled)
            {
                renderer.enabled = false;
                disabledRenderers.Add(renderer);
            }
        }

        foreach (MeshCollider collider in gameObject.GetComponentsInChildren<MeshCollider>())
        {
            collider.enabled = false;
        }
    }

    /// <summary>
    /// Turns on all renderers that were disabled.
    /// </summary>
    void EnableModel()
    {
        foreach (MeshRenderer renderer in disabledRenderers)
        {
            renderer.enabled = true;
        }

        foreach (MeshCollider collider in gameObject.GetComponentsInChildren<MeshCollider>())
        {
            collider.enabled = true;
        }

        disabledRenderers.Clear();
    }


    void Update()
    {
        // Wait till users pick an avatar to enable renderers.
        if (disabledRenderers.Count > 0)
        {
            if (!PlayerAvatarStore.Instance.PickerActive &&
            ImportExportAnchorManager.Instance.AnchorEstablished)
            {
                // After which we want to start rendering.
                EnableModel();

                // And if we've already been sent the relative transform, we will use it.
                if (GotTransform)
                {
                    // This triggers the animation sequence for the model and
                    // puts the cool materials on the model.
                    GetComponent<EnergyHubBase>().SendMessage("OnSelect");
                }
            }
        }
        else if (GotTransform == false)
        {
            transform.position = Vector3.Lerp(transform.position, ProposeTransformPosition(), 0.2f);
        }
    }

    Vector3 ProposeTransformPosition()
    {
        Vector3 retval;
        // We need to know how many users are in the experience with good transforms.
        Vector3 cumulatedPosition = Camera.main.transform.position;
        int playerCount = 1;
        foreach (RemotePlayerManager.RemoteHeadInfo remoteHead in RemotePlayerManager.Instance.remoteHeadInfos)
        {
            if (remoteHead.Anchored && remoteHead.Active)
            {
                playerCount++;
                cumulatedPosition += remoteHead.HeadObject.transform.position;
            }
        }

        // If we have more than one player ...
        if (playerCount > 1)
        {
            // Put the transform in between the players.
            retval = cumulatedPosition / playerCount;
            RaycastHit hitInfo;

            // And try to put the transform on a surface below the midpoint of the players.
            if (Physics.Raycast(retval, Vector3.down, out hitInfo, 5, SpatialMappingManager.Instance.LayerMask))
            {
                retval = hitInfo.point;
            }
        }
        // If we are the only player, have the model act as the 'cursor' ...
        else
        {
            // We prefer to put the model on a real world surface.
            RaycastHit hitInfo;

            if (Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out hitInfo, 30, SpatialMappingManager.Instance.LayerMask))
            {
                retval = hitInfo.point;
            }
            else
            {
                // But if we don't have a ray that intersects the real world, just put the model 2m in
                // front of the user.
                retval = Camera.main.transform.position + Camera.main.transform.forward * 2;
            }
        }
        return retval;
    }

    public void OnSelect()
    {
        // Note that we have a transform.
        GotTransform = true;

        // And send it to our friends.
        CustomMessages.Instance.SendStageTransform(transform.localPosition, transform.localRotation);
    }

    /// <summary>
    /// When a remote system has a transform for us, we'll get it here.
    /// </summary>
    /// <param name="msg"></param>
    void OnStageTransform(NetworkInMessage msg)
    {
        // We read the user ID but we don't use it here.
        msg.ReadInt64();

        transform.localPosition = CustomMessages.Instance.ReadVector3(msg);
        transform.localRotation = CustomMessages.Instance.ReadQuaternion(msg);

        // The first time, we'll want to send the message to the model to do its animation and
        // swap its materials.
        if (disabledRenderers.Count == 0 && GotTransform == false)
        {
            GetComponent<EnergyHubBase>().SendMessage("OnSelect");
        }

        GotTransform = true;
    }

    /// <summary>
    /// When a remote system has a transform for us, we'll get it here.
    /// </summary>
    void OnResetStage(NetworkInMessage msg)
    {
        GotTransform = false;

        GetComponent<EnergyHubBase>().ResetAnimation();
        AppStateManager.Instance.ResetStage();
    }
}

Nasazení a poslech

  • sestavte a nasaďte projekt do zařízení HoloLens.
  • Až bude aplikace připravená, ponechte kruh a Všimněte si, jak se EnergyHub zobrazí v centru všech uživatelů.
  • Klepnutím umístěte EnergyHub.
  • Vyzkoušejte hlasový příkaz "resetovat cíl", abyste mohli vybrat EnergyHub zálohování a spolupracovat jako se skupinou pro přesunutí hologramu do nového umístění.

Kapitola 6 – Real-World fyzika

V této kapitole přidáme hologramy, které odcházejí z reálného světa. Sledujte své místo a naplňte projekty spouštěné vámi i přáteli.

Cíle

  • Spouštěcí projektily, které odcházejí z reálného světa.
  • Sdílejte projektily, aby je ostatní hráči viděli.

Pokyny

  • V hierarchii vyberte objekt hologramcollection .
  • V okně inspektor klikněte na Přidat komponentu.
  • Do vyhledávacího pole zadejte spouštěč spouštěče. Vyberte výsledek hledání.

Nasazení a poslech

  • sestavujte a nasaďte do zařízení HoloLens.
  • Když je aplikace spuštěná na všech zařízeních, provedete to vzduchem a spustíte projektil na skutečných plochách.
  • Podívejte se, co se stane, když se projektil nekoliduje s miniaturou jiného hráče!

Kapitola 7 – celkový konečný

V této kapitole budeme odkrýt portál, který se dá zjistit jenom s spoluprací.

Cíle

  • Pracujte společně a na kotvě otevřete dostatečně projektily, abyste mohli odhalit tajný portál.

Pokyny

  • na panelu Project přejděte do složky Hologramy .
  • Přetáhněte podsvětový prostředek jako podřízenou položku aplikace hologramcollection.
  • Když je vybraná možnost hologramcollection , klikněte na tlačítko Přidat komponentu v okně inspektor.
  • Do nabídky zadejte do vyhledávacího pole ExplodeTarget. Vyberte výsledek hledání.
  • Když je vybraná možnost hologramcollection , z hierarchie přetáhněte objekt EnergyHub do cílového pole v inspektoru.
  • Je-li vybrána možnost hologramcollection , přetáhněte z hierarchieobjekt do podsvěta dopole PODSVĚT v inspektoru.

Nasazení a poslech

  • sestavujte a nasaďte do zařízení HoloLens.
  • Po spuštění aplikace Spolupracujte společně a spusťte střelné EnergyHub.
  • Po zobrazení světa se střelné střely spustí na robotech v podsvětě (pro další zábavu stiskněte třikrát).