Condivisione MR 240: Più dispositivi HoloLensMR Sharing 240: Multiple HoloLens devices

Nota

Le esercitazioni di Mixed Reality Academy sono state progettate in base a HoloLens (prima generazione) e ai visori VR immersive di realtà mista.The Mixed Reality Academy tutorials were designed with HoloLens (1st gen) and Mixed Reality Immersive Headsets in mind. Pertanto, riteniamo importante lasciarle a disposizione degli sviluppatori a cui serve ancora materiale sussidiario per lo sviluppo di questi dispositivi.As such, we feel it is important to leave these tutorials in place for developers who are still looking for guidance in developing for those devices. Queste esercitazioni non verranno aggiornate con i set di strumenti o le interazioni più recenti usati per HoloLens 2.These tutorials will not be updated with the latest toolsets or interactions being used for HoloLens 2. Rimarranno invariate per consentire di continuare a lavorare sui dispositivi supportati.They will be maintained to continue working on the supported devices. Per HoloLens 2 è stata pubblicata una nuova serie di esercitazioni.A new series of tutorials has been posted for HoloLens 2.

Gli ologrammi ricevono una presenza nel nostro mondo rimanendo al posto mentre ci si sposta nello spazio.Holograms are given presence in our world by remaining in place as we move about in space. HoloLens mantiene gli ologrammi sul posto usando vari sistemi di coordinate per tenere traccia della posizione e dell'orientamento degli oggetti.HoloLens keeps holograms in place by using various coordinate systems to keep track of the location and orientation of objects. Quando si condividono questi sistemi di coordinate tra i dispositivi, è possibile creare un'esperienza condivisa che ci consenta di partecipare a un mondo olografico condiviso.When we share these coordinate systems between devices, we can create a shared experience that allows us to take part in a shared holographic world.

In questa esercitazione si apprenderà come:In this tutorial, we will:

  • Configurare una rete per un'esperienza condivisa.Setup a network for a shared experience.
  • Condividere gli ologrammi tra i dispositivi HoloLens.Share holograms across HoloLens devices.
  • Scopri altre persone nel nostro mondo olografico condiviso.Discover other people in our shared holographic world.
  • Consente di creare un'esperienza interattiva condivisa in cui è possibile fare riferimento ad altri giocatori e avviare i loro proiettili.Create a shared interactive experience where you can target other players - and launch projectiles at them!

Supporto di dispositiviDevice support

CorsoCourse HoloLensHoloLens Visori VR immersiveImmersive headsets
Condivisione MR 240: Più dispositivi HoloLensMR Sharing 240: Multiple HoloLens devices ✔️✔️

Prima di iniziareBefore you start

PrerequisitiPrerequisites

File di progettoProject files

  • Scaricare i file richiesti dal progetto.Download the files required by the project. Richiede Unity 2017,2 o versione successiva.Requires Unity 2017.2 or later.
    • Se è ancora necessario il supporto per Unity 5,6, usare questa versione.If you still need Unity 5.6 support, please use this release.
    • Se è ancora necessario il supporto per Unity 5,5, usare questa versione.If you still need Unity 5.5 support, please use this release.
    • Se è ancora necessario il supporto per Unity 5,4, usare questa versione.If you still need Unity 5.4 support, please use this release.
  • Annullare l'archiviazione dei file sul desktop o in un'altra posizione facile da raggiungere.Un-archive the files to your desktop or other easy to reach location. Mantenendo il nome della cartella SharedHolograms.Keep the folder name as SharedHolograms.

Nota

Se si vuole esaminare il codice sorgente prima del download, è disponibile in GitHub.If you want to look through the source code before downloading, it's available on GitHub.

Capitolo 1-Holo WorldChapter 1 - Holo World

In questo capitolo verrà configurato il primo progetto Unity ed eseguiamo il processo di compilazione e distribuzione.In this chapter, we'll setup our first Unity project and step through the build and deploy process.

ObiettiviObjectives

  • Configurare Unity per sviluppare app olografiche.Setup Unity to develop holographic apps.
  • Vedi l'ologramma!See your hologram!

IstruzioniInstructions

  • Riavviare Unity.Start Unity.
  • Selezionare Open (Apri).Select Open.
  • Immettere il percorso come cartella SharedHolograms precedentemente non archiviata.Enter location as the SharedHolograms folder you previously unarchived.
  • Selezionare nome progetto e fare clic su Seleziona cartella.Select Project Name and click Select Folder.
  • Nella gerarchia, fare clic con il pulsante destro del mouse sulla fotocamera principale e scegliere Elimina.In the Hierarchy, right-click the Main Camera and select Delete.
  • Nella cartella HoloToolkit-sharing-240/Prefabbricates/camera trovare la prefabbricata principale della fotocamera .In the HoloToolkit-Sharing-240/Prefabs/Camera folder, find the Main Camera prefab.
  • Trascinare e rilasciare la fotocamera principale nella gerarchia.Drag and drop the Main Camera into the Hierarchy.
  • Nella gerarchia fare clic su Crea e Crea vuoto.In the Hierarchy, click on Create and Create Empty.
  • Fare clic con il pulsante destro del mouse sul nuovo GameObject e scegliere Rinomina.Right-click the new GameObject and select Rename.
  • Rinominare GameObject in ologrammcollection.Rename the GameObject to HologramCollection.
  • Selezionare l'oggetto ologrammcollection nella gerarchia.Select the HologramCollection object in the Hierarchy.
  • Nel controllo impostare la posizione di trasformazione su: X: 0, Y:-0,25, Z: 2.In the Inspector set the transform position to: X: 0, Y: -0.25, Z: 2.
  • Nella cartella olografici del pannello del progetto individuare l'asset EnergyHub .In the Holograms folder in the Project panel, find the EnergyHub asset.
  • Trascinare e rilasciare l'oggetto EnergyHub dal pannello progetto alla gerarchia come elemento figlio di ologrammcollection.Drag and drop the EnergyHub object from the Project panel to the Hierarchy as a child of HologramCollection.
  • Seleziona File > Salva scena con nome...Select File > Save Scene As...
  • Assegnare alla scena il nome SharedHolograms e fare clic su Save.Name the scene SharedHolograms and click Save.
  • Premere il pulsante Play in Unity per visualizzare l'anteprima degli ologrammi.Press the Play button in Unity to preview your holograms.
  • Premere Riproduci una seconda volta per arrestare la modalità di anteprima.Press Play a second time to stop preview mode.

Esportare il progetto da Unity a Visual StudioExport the project from Unity to Visual Studio

  • In Unity selezionare File > impostazioni di compilazione.In Unity select File > Build Settings.
  • Fare clic su Aggiungi scene aperte per aggiungere la scena.Click Add Open Scenes to add the scene.
  • Selezionare piattaforma UWP (Universal Windows Platform) nell'elenco piattaforma e fare clic su Cambia piattaforma.Select Universal Windows Platform in the Platform list and click Switch Platform.
  • Impostare SDK su Universal 10.Set SDK to Universal 10.
  • Impostare il dispositivo di destinazione su HoloLens e il tipo di compilazione UWP su D3D.Set Target device to HoloLens and UWP Build Type to D3D.
  • Controllare i progetti C# di Unity.Check Unity C# Projects.
  • Fare clic su Compila.Click Build.
  • Nella finestra Esplora file visualizzata creare una nuova cartella denominata "app".In the file explorer window that appears, create a New Folder named "App".
  • Fare clic sulla cartella dell' app .Single click the App folder.
  • Premere Seleziona cartella.Press Select Folder.
  • Quando si esegue Unity, viene visualizzata una finestra Esplora file.When Unity is done, a File Explorer window will appear.
  • Aprire la cartella dell' app .Open the App folder.
  • Aprire SharedHolograms. sln per avviare Visual Studio.Open SharedHolograms.sln to launch Visual Studio.
  • Usando la barra degli strumenti superiore in Visual Studio, modificare la destinazione da debug a Release e da ARM a x86.Using the top toolbar in Visual Studio, change the target from Debug to Release and from ARM to X86.
  • Fare clic sulla freccia a discesa accanto a computer locale e selezionare dispositivo remoto.Click on the drop-down arrow next to Local Machine, and select Remote Device.
    • Impostare l' Indirizzo sul nome o sull'indirizzo IP del HoloLens.Set the Address to the name or IP address of your HoloLens. Se non si conosce l'indirizzo IP del dispositivo, controllare le impostazioni > rete & Internet > opzioni avanzate o richiedere a Cortana "Hey Cortana, qual è il mio indirizzo IP?"If you do not know your device IP address, look in Settings > Network & Internet > Advanced Options or ask Cortana "Hey Cortana, What's my IP address?"
    • Lasciare la modalità di autenticazione impostata su universale.Leave the Authentication Mode set to Universal.
    • Fare clic su SelezionaClick Select
  • Fare clic su debug > avvia senza eseguire debug o premere CTRL + F5.Click Debug > Start Without debugging or press Ctrl + F5. Se questa è la prima volta che si esegue la distribuzione nel dispositivo, sarà necessario associarla a Visual Studio.If this is the first time deploying to your device, you will need to pair it with Visual Studio.
  • Inserire il HoloLens e trovare l'ologramma di EnergyHub.Put on your HoloLens and find the EnergyHub hologram.

Capitolo 2-interazioneChapter 2 - Interaction

In questo capitolo si interagisce con gli ologrammi.In this chapter, we'll interact with our holograms. Prima di tutto, verrà aggiunto un cursore per visualizzare lo sguardo.First, we'll add a cursor to visualize our Gaze. Quindi, aggiungiamo i movimenti e usiamo la mano per inserire gli ologrammi nello spazio.Then, we'll add Gestures and use our hand to place our holograms in space.

ObiettiviObjectives

  • Utilizzare l'input di sguardi per controllare un cursore.Use gaze input to control a cursor.
  • Usare l'input dei movimenti per interagire con gli ologrammi.Use gesture input to interact with holograms.

IstruzioniInstructions

Sguardo fissoGaze

  • Nel Pannello gerarchia selezionare l'oggetto ologrammcollection .In the Hierarchy panel select the HologramCollection object.
  • Nel Pannello di controllo fare clic sul pulsante Aggiungi componente .In the Inspector panel click the Add Component button.
  • Nel menu digitare nella casella di ricerca lo sguardo Manager.In the menu, type in the search box Gaze Manager. Selezionare il risultato della ricerca.Select the search result.
  • Nella cartella HoloToolkit-sharing-240\Prefabs\Input trovare il cursore asset.In the HoloToolkit-Sharing-240\Prefabs\Input folder, find the Cursor asset.
  • Trascinare e rilasciare l'asset del cursore nella gerarchia.Drag and drop the Cursor asset onto the Hierarchy.

MovimentoGesture

  • Nel Pannello gerarchia selezionare l'oggetto ologrammcollection .In the Hierarchy panel select the HologramCollection object.
  • Fare clic su Aggiungi componente e digitare gestione movimenti nel campo di ricerca.Click Add Component and type Gesture Manager in the search field. Selezionare il risultato della ricerca.Select the search result.
  • Nel Pannello gerarchia espandere ologrammcollection.In the Hierarchy panel, expand HologramCollection.
  • Selezionare l'oggetto EnergyHub figlio.Select the child EnergyHub object.
  • Nel Pannello di controllo fare clic sul pulsante Aggiungi componente .In the Inspector panel click the Add Component button.
  • Nel menu digitare nella casella di ricerca posizionamento ologrammi.In the menu, type in the search box Hologram Placement. Selezionare il risultato della ricerca.Select the search result.
  • Salvare la scena selezionando File > Salva scena.Save the scene by selecting File > Save Scene.

Distribuisci e goditiDeploy and enjoy

  • Eseguire la compilazione e la distribuzione nella HoloLens usando le istruzioni del capitolo precedente.Build and deploy to your HoloLens, using the instructions from the previous chapter.
  • Una volta avviata l'app nella HoloLens, sposta la tua parte e osserva il modo in cui il EnergyHub segue il tuo sguardo.Once the app launches on your HoloLens, move your head around and notice how the EnergyHub follows your gaze.
  • Si noti come viene visualizzato il cursore quando si guarda l'ologramma e si passa a una luce puntiforme senza guardare un ologramma.Notice how the cursor appears when you gaze upon the hologram, and changes to a point light when not gazing at a hologram.
  • Eseguire un tocco aereo per posizionare l'ologramma.Perform an air-tap to place the hologram. Al momento del progetto, è possibile inserire l'ologramma una sola volta (Ridistribuisci per riprovare).At this time in our project, you can only place the hologram once (redeploy to try again).

Capitolo 3-Coordinate condiviseChapter 3 - Shared Coordinates

È divertente vedere e interagire con gli ologrammi, ma andiamo oltre.It's fun to see and interact with holograms, but let's go further. Verrà configurata la prima esperienza condivisa, ovvero un ologramma che tutti gli utenti possono vedere insieme.We'll set up our first shared experience - a hologram everyone can see together.

ObiettiviObjectives

  • Configurare una rete per un'esperienza condivisa.Setup a network for a shared experience.
  • Stabilire un punto di riferimento comune.Establish a common reference point.
  • Condividere i sistemi di coordinate tra dispositivi.Share coordinate systems across devices.
  • Tutti gli utenti vedono lo stesso ologramma!Everyone sees the same hologram!

Nota

Per la connessione di un'app al server di condivisione, è necessario dichiarare le funzionalità InternetClientServer e PrivateNetworkClientServer .The InternetClientServer and PrivateNetworkClientServer capabilities must be declared for an app to connect to the sharing server. Questa operazione viene eseguita per gli ologrammi 240, ma è necessario tenerla presente per i propri progetti.This is done for you already in Holograms 240, but keep this in mind for your own projects.

  1. Nell'editor di Unity passare a Impostazioni lettore passando a "modifica > impostazioni progetto > lettore"In the Unity Editor, go to the player settings by navigating to "Edit > Project Settings > Player"
  2. Fare clic sulla scheda "Windows Store"Click on the "Windows Store" tab
  3. Nella sezione "impostazioni di pubblicazione > funzionalità" controllare la funzionalità InternetClientServer e la funzionalità PrivateNetworkClientServerIn the "Publishing Settings > Capabilities" section, check the InternetClientServer capability and the PrivateNetworkClientServer capability

IstruzioniInstructions

  • Nel Pannello del progetto passare alla cartella HoloToolkit-sharing-240\Prefabs\SharingIn the Project panel navigate to the HoloToolkit-Sharing-240\Prefabs\Sharing folder.
  • Trascinare e rilasciare il prefabbricato di condivisione nel Pannello gerarchia.Drag and drop the Sharing prefab into the Hierarchy panel.

Successivamente, è necessario avviare il servizio di condivisione.Next we need to launch the sharing service. È necessario eseguire questo passaggio solo per un PC nell'esperienza condivisa.Only one PC in the shared experience needs to do this step.

  • In Unity-nel menu in alto a destra selezionare il menu HoloToolkit-sharing-240.In Unity - in the top-hand menu - select the HoloToolkit-Sharing-240 menu.
  • Selezionare l'elemento avvio condivisione servizio nell'elenco a discesa.Select the Launch Sharing Service item in the drop-down.
  • Selezionare l'opzione rete privata e fare clic su Consenti accesso quando viene visualizzato il prompt del firewall.Check the Private Network option and click Allow Access when the firewall prompt appears.
  • Annotare l'indirizzo IPv4 visualizzato nella finestra della console del servizio di condivisione.Note down the IPv4 address displayed in the Sharing Service console window. Si tratta dello stesso IP del computer in cui viene eseguito il servizio.This is the same IP as the machine the service is being run on.

Seguire le istruzioni restanti in tutti i PC che faranno parte dell'esperienza condivisa.Follow the rest of the instructions on all PCs that will join the shared experience.

  • Nella gerarchia selezionare l'oggetto di condivisione .In the Hierarchy, select the Sharing object.
  • In controllo, nel componente fase di condivisione , modificare l' indirizzo del server da "localhost" all'indirizzo IPv4 del computer che esegue SharingService.exe.In the Inspector, on the Sharing Stage component, change the Server Address from 'localhost' to the IPv4 address of the machine running SharingService.exe.
  • Nella gerarchia selezionare l'oggetto ologrammcollection .In the Hierarchy select the HologramCollection object.
  • Nel controllo fare clic sul pulsante Aggiungi componente .In the Inspector click the Add Component button.
  • Nella casella di ricerca digitare Import Export Anchor Manager.In the search box, type Import Export Anchor Manager. Selezionare il risultato della ricerca.Select the search result.
  • Nel pannello progetto passare alla cartella script .In the Project panel navigate to the Scripts folder.
  • Fare doppio clic sullo script HologramPlacement per aprirlo in Visual Studio.Double-click the HologramPlacement script to open it in Visual Studio.
  • Sostituire il contenuto con il codice seguente.Replace the contents with the code below.
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.
    }
}
  • Tornare in Unity, selezionare ologrammcollection nel Pannello gerarchia.Back in Unity, select the HologramCollection in the Hierarchy panel.
  • Nel Pannello di controllo fare clic sul pulsante Aggiungi componente .In the Inspector panel click the Add Component button.
  • Nel menu digitare nella casella di ricerca Gestione stato app.In the menu, type in the search box App State Manager. Selezionare il risultato della ricerca.Select the search result.

Distribuisci e goditiDeploy and enjoy

  • Compilare il progetto per i dispositivi HoloLens.Build the project for your HoloLens devices.
  • Designare un HoloLens per la distribuzione iniziale.Designate one HoloLens to deploy to first. È necessario attendere che il punto di ancoraggio venga caricato nel servizio prima di poter inserire il EnergyHub (questa operazione può richiedere circa 30-60 secondi).You will need to wait for the Anchor to be uploaded to the service before you can place the EnergyHub (this can take ~30-60 seconds). Fino al termine del caricamento, i movimenti Tap verranno ignorati.Until the upload is done, your tap gestures will be ignored.
  • Dopo aver inserito il EnergyHub, il relativo percorso verrà caricato nel servizio ed è quindi possibile distribuirlo a tutti gli altri dispositivi HoloLens.After the EnergyHub has been placed, its location will be uploaded to the service and you can then deploy to all other HoloLens devices.
  • Quando una nuova HoloLens viene aggiunta per la prima volta alla sessione, il percorso di EnergyHub potrebbe non essere corretto nel dispositivo.When a new HoloLens first joins the session, the location of the EnergyHub may not be correct on that device. Tuttavia, non appena i percorsi di ancoraggio e EnergyHub sono stati scaricati dal servizio, EnergyHub dovrebbe passare al nuovo percorso condiviso.However, as soon as the anchor and EnergyHub locations have been downloaded from the service, the EnergyHub should jump to the new, shared location. Se questo non avviene entro ~ 30-60 secondi, passare alla posizione in cui si trovava il HoloLens originale durante l'impostazione dell'ancoraggio per raccogliere più indizi sull'ambiente.If this does not happen within ~30-60 seconds, walk to where the original HoloLens was when setting the anchor to gather more environment clues. Se il percorso non è ancora bloccato, ridistribuirlo nel dispositivo.If the location still does not lock on, redeploy to the device.
  • Quando i dispositivi sono tutti pronti ed eseguono l'app, cercare il EnergyHub.When the devices are all ready and running the app, look for the EnergyHub. È possibile accordare tutti sulla posizione dell'ologramma e sulla direzione del testo.Can you all agree on the hologram's location and which direction the text is facing?

Capitolo 4-individuazioneChapter 4 - Discovery

Tutti gli utenti possono ora visualizzare lo stesso ologramma!Everyone can now see the same hologram! A questo punto, vediamo tutti gli altri utenti connessi al nostro mondo olografico condiviso.Now let's see everyone else connected to our shared holographic world. In questo capitolo si otterrà la posizione della testa e la rotazione di tutti gli altri dispositivi HoloLens nella stessa sessione di condivisione.In this chapter, we'll grab the head location and rotation of all other HoloLens devices in the same sharing session.

ObiettiviObjectives

  • Scopri le altre nell'esperienza condivisa.Discover each other in our shared experience.
  • Scegliere e condividere un avatar del lettore.Choose and share a player avatar.
  • Alleghi l'avatar del lettore accanto alle intestazioni di tutti.Attach the player avatar next to everyone's heads.

IstruzioniInstructions

  • Nel Pannello del progetto passare alla cartella ologrammi .In the Project panel navigate to the Holograms folder.
  • Trascinare e rilasciare PlayerAvatarStore nella gerarchia.Drag and drop the PlayerAvatarStore into the Hierarchy.
  • Nel pannello progetto passare alla cartella script .In the Project panel navigate to the Scripts folder.
  • Fare doppio clic sullo script AvatarSelector per aprirlo in Visual Studio.Double-click the AvatarSelector script to open it in Visual Studio.
  • Sostituire il contenuto con il codice seguente.Replace the contents with the code below.
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;
    }
}
  • Nella gerarchia selezionare l'oggetto ologrammcollection .In the Hierarchy select the HologramCollection object.
  • Nel controllo fare clic su Aggiungi componente.In the Inspector click Add Component.
  • Nella casella di ricerca digitare local Player Manager.In the search box, type Local Player Manager. Selezionare il risultato della ricerca.Select the search result.
  • Nella gerarchia selezionare l'oggetto ologrammcollection .In the Hierarchy select the HologramCollection object.
  • Nel controllo fare clic su Aggiungi componente.In the Inspector click Add Component.
  • Nella casella di ricerca digitare Remote Player Manager.In the search box, type Remote Player Manager. Selezionare il risultato della ricerca.Select the search result.
  • Aprire lo script HologramPlacement in Visual Studio.Open the HologramPlacement script in Visual Studio.
  • Sostituire il contenuto con il codice seguente.Replace the contents with the code below.
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.
    }
}
  • Aprire lo script AppStateManager in Visual Studio.Open the AppStateManager script in Visual Studio.
  • Sostituire il contenuto con il codice seguente.Replace the contents with the code below.
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;
        }
    }
}

Distribuisci e goditiDeploy and Enjoy

  • Compilare e distribuire il progetto nei dispositivi HoloLens.Build and deploy the project to your HoloLens devices.
  • Quando si sente un suono di ping, trovare il menu di selezione avatar e selezionare un avatar con il gesto di tocco.When you hear a pinging sound, find the avatar selection menu and select an avatar with the air-tap gesture.
  • Se non si esaminano gli ologrammi, la luce puntiforme intorno al cursore diventerà un colore diverso quando il HoloLens comunica con il servizio: inizializzazione (viola scuro), download dell'ancoraggio (verde), importazione/esportazione dei dati della località (giallo), caricamento dell'ancoraggio (blu).If you're not looking at any holograms, the point light around your cursor will turn a different color when your HoloLens is communicating with the service: initializing (dark purple), downloading the anchor (green), importing/exporting location data (yellow), uploading the anchor (blue). Se la luce puntiforme intorno al cursore è il colore predefinito (viola chiaro), si è pronti per interagire con gli altri giocatori della sessione.If your point light around your cursor is the default color (light purple), then you are ready to interact with other players in your session!
  • Esaminare gli altri utenti connessi allo spazio. ci sarà un robot olografico che si trova al di sopra della spalla e mima i movimenti della testa.Look at other people connected to your space - there will be a holographic robot floating above their shoulder and mimicking their head motions!

Capitolo 5-selezione hostChapter 5 - Placement

In questo capitolo si farà in modo che l'ancoraggio possa essere inserito in superfici reali.In this chapter, we'll make the anchor able to be placed on real-world surfaces. Verranno usate le coordinate condivise per posizionare l'ancoraggio nel punto intermedio tra tutti gli utenti connessi all'esperienza condivisa.We'll use shared coordinates to place that anchor in the middle point between everyone connected to the shared experience.

ObiettiviObjectives

  • Posizionare gli ologrammi sulla mesh di mapping spaziale in base alla posizione della testa dei giocatori.Place holograms on the spatial mapping mesh based on players’ head position.

IstruzioniInstructions

  • Nel Pannello del progetto passare alla cartella ologrammi .In the Project panel navigate to the Holograms folder.
  • Trascinare e rilasciare il prefabbricato CustomSpatialMapping nella gerarchia.Drag and drop the CustomSpatialMapping prefab onto the Hierarchy.
  • Nel pannello progetto passare alla cartella script .In the Project panel navigate to the Scripts folder.
  • Fare doppio clic sullo script AppStateManager per aprirlo in Visual Studio.Double-click the AppStateManager script to open it in Visual Studio.
  • Sostituire il contenuto con il codice seguente.Replace the contents with the code below.
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;
        }
    }
}
  • Nel pannello progetto passare alla cartella script .In the Project panel navigate to the Scripts folder.
  • Fare doppio clic sullo script HologramPlacement per aprirlo in Visual Studio.Double-click the HologramPlacement script to open it in Visual Studio.
  • Sostituire il contenuto con il codice seguente.Replace the contents with the code below.
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();
    }
}

Distribuisci e goditiDeploy and enjoy

  • Compilare e distribuire il progetto nei dispositivi HoloLens.Build and deploy the project to your HoloLens devices.
  • Quando l'app è pronta, si trova in un cerchio e si noterà che il EnergyHub viene visualizzato al centro di tutti.When the app is ready, stand in a circle and notice how the EnergyHub appears in the center of everyone.
  • Toccare per inserire il EnergyHub.Tap to place the EnergyHub.
  • Provare il comando vocale ' Reimposta destinazione ' per selezionare il EnergyHub di backup e collaborare come gruppo per spostare l'ologramma in una nuova posizione.Try the voice command 'Reset Target' to pick the EnergyHub back up and work together as a group to move the hologram to a new location.

Capitolo 6-Real-World fisicaChapter 6 - Real-World Physics

In questo capitolo verranno aggiunti gli ologrammi che rimbalzano sulle superfici reali.In this chapter we'll add holograms that bounce off real-world surfaces. Guarda lo spazio con i progetti avviati dall'utente e dai tuoi amici.Watch your space fill up with projects launched by both you and your friends!

ObiettiviObjectives

  • Avvia i proiettili che rimbalzano sulle superfici reali.Launch projectiles that bounce off real-world surfaces.
  • Condividere i proiettili in modo che possano essere visualizzati da altri lettori.Share the projectiles so other players can see them.

IstruzioniInstructions

  • Nella gerarchia selezionare l'oggetto ologrammcollection .In the Hierarchy select the HologramCollection object.
  • Nel controllo fare clic su Aggiungi componente.In the Inspector click Add Component.
  • Nella casella di ricerca digitare avvio del proiettile.In the search box, type Projectile Launcher. Selezionare il risultato della ricerca.Select the search result.

Distribuisci e goditiDeploy and enjoy

  • Compilare e distribuire nei dispositivi HoloLens.Build and deploy to your HoloLens devices.
  • Quando l'app è in esecuzione in tutti i dispositivi, è possibile eseguire un rubinetto d'aria per avviare il proiettile su superfici reali.When the app is running on all devices, perform an air-tap to launch projectile at real world surfaces.
  • Scopri cosa accade quando il proiettile si scontra con l'avatar di un altro giocatore.See what happens when your projectile collides with another player's avatar!

Capitolo 7-finale finaleChapter 7 - Grand Finale

In questo capitolo viene individuato un portale che può essere individuato solo con la collaborazione.In this chapter, we'll uncover a portal that can only be discovered with collaboration.

ObiettiviObjectives

  • Collaborare per avviare un numero sufficiente di proiettili nell'ancoraggio per individuare un portale segreto.Work together to launch enough projectiles at the anchor to uncover a secret portal!

IstruzioniInstructions

  • Nel Pannello del progetto passare alla cartella ologrammi .In the Project panel navigate to the Holograms folder.
  • Trascinare e rilasciare l'asset di Sottomondo come figlio di ologrammcollection.Drag and drop the Underworld asset as a child of HologramCollection.
  • Con ologrammcollection selezionato, fare clic sul pulsante Aggiungi componente nel controllo.With HologramCollection selected, click the Add Component button in the Inspector.
  • Nel menu digitare nella casella di ricerca ExplodeTarget.In the menu, type in the search box ExplodeTarget. Selezionare il risultato della ricerca.Select the search result.
  • Con ologrammcollection selezionato, dalla gerarchia trascinare l'oggetto EnergyHub nel campo di destinazione nel controllo.With HologramCollection selected, from the Hierarchy drag the EnergyHub object to the Target field in the Inspector.
  • Con l'oggetto ologrammcollection selezionato, dalla gerarchia trascinare l'oggetto di Sottomondo nel campo Sottomondo del controllo.With HologramCollection selected, from the Hierarchy drag the Underworld object to the Underworld field in the Inspector.

Distribuisci e goditiDeploy and enjoy

  • Compilare e distribuire nei dispositivi HoloLens.Build and deploy to your HoloLens devices.
  • Quando l'app è stata avviata, collaborare insieme per avviare i proiettili nella EnergyHub.When the app has launched, collaborate together to launch projectiles at the EnergyHub.
  • Quando viene visualizzato il Sottomondo, avviare i proiettili nei robot di Sottomondo (raggiungere un robot tre volte per divertirsi).When the underworld appears, launch projectiles at underworld robots (hit a robot three times for extra fun).