Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Viktigt
Självstudierna Mixed Reality Academy har utformats med HoloLens (första generationen), Unity 2017 och Mixed Reality integrerande headset i åtanke. Därför tycker vi att det är viktigt att låta de här självstudierna vara kvar för utvecklare som fortfarande behöver vägledning för att utveckla för dessa enheter. De här självstudierna uppdateras inte med de senaste verktygsuppsättningarna eller interaktionerna som används för HoloLens 2 och kanske inte är kompatibla med nyare versioner av Unity. De kommer att underhållas för att fortsätta arbeta på de enheter som stöds. En ny serie självstudier har publicerats för HoloLens 2.
Hologram ges närvaro i vår värld genom att förbli på plats när vi rör oss i rymden. HoloLens håller hologram på plats med hjälp av olika koordinatsystem för att hålla reda på plats och orientering för objekt. När vi delar dessa koordinatsystem mellan enheter kan vi skapa en delad upplevelse som gör att vi kan delta i en delad holografisk värld.
I den här självstudiekursen ska du:
- Konfigurera ett nätverk för en delad upplevelse.
- Dela hologram över HoloLens-enheter.
- Upptäck andra människor i vår gemensamma holografiska värld.
- Skapa en delad interaktiv upplevelse där du kan rikta in dig på andra spelare – och starta projektiler på dem!
Stöd för enheter
| Kurs | HoloLens | Integrerande headset |
|---|---|---|
| MR-delning 240: Flera HoloLens-enheter | ✔️ |
Innan du börjar
Förutsättningar
- En Windows 10 dator som konfigurerats med rätt verktyg installerade med Internetåtkomst.
- Minst två HoloLens-enheter som har konfigurerats för utveckling.
Projektfiler
- Ladda ned de filer som krävs av projektet. Kräver Unity 2017.2 eller senare.
- Om du fortfarande behöver stöd för Unity 5.6 använder du den här versionen.
- Om du fortfarande behöver stöd för Unity 5.5 använder du den här versionen.
- Om du fortfarande behöver stöd för Unity 5.4 använder du den här versionen.
- Avarkivera filerna på skrivbordet eller på en annan plats som är lätt att nå. Behåll mappnamnet som SharedHolograms.
Anteckning
Om du vill titta igenom källkoden innan du laddar ned den är den tillgänglig på GitHub.
Kapitel 1 – Holo World
I det här kapitlet ska vi konfigurera vårt första Unity-projekt och gå igenom bygg- och distributionsprocessen.
Mål
- Konfigurera Unity för att utveckla holografiska appar.
- Se ditt hologram!
Instruktioner
- Starta Unity.
- Välj Öppna.
- Ange plats som mappen SharedHolograms som du tidigare har tagit bort från arkitekturen.
- Välj Projektnamn och klicka på Välj mapp.
- I hierarkin högerklickar du på huvudkameran och väljer Ta bort.
- I mappen HoloToolkit-Sharing-240/Prefabs/Camera letar du upp prefab för huvudkameran .
- Dra och släpp huvudkameran i hierarkin.
- I hierarkin klickar du på Skapa och skapa tom.
- Högerklicka på det nya GameObject och välj Byt namn.
- Byt namn på GameObject till HologramCollection.
- Välj Objektet HologramCollection i hierarkin.
- I Inspector anger du transformeringspositionen till: X: 0, Y: -0,25, Z: 2.
- Leta reda på EnergyHub-tillgången i mappen Holograms på projektpanelen.
- Dra och släpp EnergyHub-objektet från projektpanelen till hierarkin som underordnad HologramCollection.
- Välj Arkiv > Spara scen som...
- Ge scenen namnet SharedHologram och klicka på Spara.
- Tryck på knappen Spela upp i Unity för att förhandsgranska dina hologram.
- Tryck på Spela upp en andra gång för att stoppa förhandsgranskningsläget.
Exportera projektet från Unity till Visual Studio
- I Unity väljer du Inställningar för filbygge>.
- Klicka på Lägg till öppna scener för att lägga till scenen.
- Välj Universell Windows-plattform i listan Plattform och klicka på Växla plattform.
- Ange SDK till Universal 10.
- Ange Målenhet till HoloLens och UWP Build Type till D3D.
- Kontrollera Unity C#-projekt.
- Klicka på Skapa.
- I utforskarfönstret som visas skapar du en ny mapp med namnet "App".
- Klicka på mappen App .
- Tryck på Välj mapp.
- När Unity är klart visas ett Utforskaren fönster.
- Öppna mappen App .
- Öppna SharedHolograms.sln för att starta Visual Studio.
- Använd det översta verktygsfältet i Visual Studio och ändra målet från Felsökning till Släpp och från ARM till X86.
- Klicka på listrutepilen bredvid Lokal dator och välj Fjärrenhet.
- Ange Namnet eller IP-adressen för din HoloLens som Adress . Om du inte känner till enhetens IP-adress kan du titta i Inställningar > Nätverk & Avancerade internetalternativ > eller fråga Cortana "Hej Cortana, Vad är min IP-adress?"
- Låt autentiseringsläget vara inställt på Universell.
- Klicka på Välj
- Klicka på Felsök > start utan felsökning eller tryck på Ctrl + F5. Om det här är första gången du distribuerar till din enhet måste du koppla den till Visual Studio.
- Sätt på dig HoloLens och leta upp EnergyHub-hologrammet.
Kapitel 2 – Interaktion
I det här kapitlet interagerar vi med våra hologram. Först lägger vi till en markör för att visualisera blicken. Sedan lägger vi till gester och använder vår hand för att placera våra hologram i rymden.
Mål
- Använd blickinmatning för att styra en markör.
- Använd gestindata för att interagera med hologram.
Instruktioner
Blick
- I panelen Hierarki väljer du objektet HologramCollection .
- I panelen Kontroll klickar du på knappen Lägg till komponent .
- I menyn skriver du i sökrutan Gaze Manager. Välj sökresultatet.
- Leta reda på markörtillgången i mappen HoloToolkit-Sharing-240\Prefabs\Input.
- Dra och släpp markörtillgången i hierarkin.
Gest
- I panelen Hierarki väljer du objektet HologramCollection .
- Klicka på Lägg till komponent och skriv Gesthanteraren i sökfältet. Välj sökresultatet.
- Expandera HologramCollection på panelen Hierarki.
- Välj det underordnade EnergyHub-objektet .
- I panelen Kontroll klickar du på knappen Lägg till komponent .
- I menyn skriver du i sökrutan Hologramplacering. Välj sökresultatet.
- Spara scenen genom att välja Arkiv > Spara scen.
Distribuera och dra nytta av
- Skapa och distribuera till HoloLens med hjälp av anvisningarna i föregående kapitel.
- När appen har lanserats på HoloLens kan du flytta runt huvudet och se hur EnergyHub följer din blick.
- Observera hur markören visas när du tittar på hologrammet och ändras till en punktlampa när du inte tittar på ett hologram.
- Utför en lufttryckning för att placera hologrammet. Just nu i vårt projekt kan du bara placera hologrammet en gång (omdistribuera för att försöka igen).
Kapitel 3 – Delade koordinater
Det är roligt att se och interagera med hologram, men vi går vidare. Vi konfigurerar vår första delade upplevelse – ett hologram som alla kan se tillsammans.
Mål
- Konfigurera ett nätverk för en delad upplevelse.
- Upprätta en gemensam referenspunkt.
- Dela koordinatsystem mellan enheter.
- Alla ser samma hologram!
Anteckning
Funktionerna InternetClientServer och PrivateNetworkClientServer måste deklareras för att en app ska kunna ansluta till delningsservern. Detta görs för dig redan i Holograms 240, men tänk på detta för dina egna projekt.
- I Unity-redigeraren går du till spelarinställningarna genom att gå till "Redigera > projektinställningar > spelare"
- Klicka på fliken "Windows Store"
- I avsnittet "Funktioner för publiceringsinställningar>" kontrollerar du funktionen InternetClientServer och funktionen PrivateNetworkClientServer
Instruktioner
- I projektpanelen navigerar du till mappen HoloToolkit-Sharing-240\Prefabs\Sharing .
- Dra och släpp prefab för delning i hierarkipanelen.
Därefter måste vi starta delningstjänsten. Endast en dator i den delade upplevelsen behöver utföra det här steget.
- I Unity – i den översta menyn – väljer du menyn HoloToolkit-Sharing-240.
- Välj objektet Starta delningstjänst i listrutan.
- Markera alternativet Privat nätverk och klicka på Tillåt åtkomst när brandväggsprompten visas.
- Anteckna IPv4-adressen som visas i konsolfönstret För delningstjänsten. Det här är samma IP-adress som den dator som tjänsten körs på.
Följ resten av anvisningarna på alla datorer som ska ansluta till den delade upplevelsen.
- I hierarkin väljer du objektet Delning .
- Ändra serveradressen från "localhost" till IPv4-adressen för den dator som kör SharingService.exe i komponenten Delningssteg i Inspector.
- I Hierarki väljer du objektet HologramCollection .
- I Inspector klickar du på knappen Lägg till komponent .
- I sökrutan skriver du Import Export Anchor Manager. Välj sökresultatet.
- I projektpanelennavigerar du till mappen Skript.
- Dubbelklicka på skriptet HologramPlacement för att öppna det i Visual Studio.
- Ersätt innehållet med koden nedan.
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.
}
}
- När du är tillbaka i Unity väljer du HologramCollection på panelen Hierarki.
- I panelen Kontroll klickar du på knappen Lägg till komponent .
- På menyn skriver du i sökrutan App State Manager. Välj sökresultatet.
Distribuera och dra nytta av
- Skapa projektet för dina HoloLens-enheter.
- Ange en HoloLens som ska distribueras till först. Du måste vänta tills fästpunkten har laddats upp till tjänsten innan du kan placera EnergyHub (det kan ta cirka 30–60 sekunder). Tryckgesterna ignoreras tills uppladdningen är klar.
- När EnergyHub har placerats laddas dess plats upp till tjänsten och du kan sedan distribuera den till alla andra HoloLens-enheter.
- När en ny HoloLens först ansluter till sessionen kanske platsen för EnergyHub inte är korrekt på den enheten. Men så snart fästpunkten och EnergyHub-platserna har laddats ned från tjänsten bör EnergyHub hoppa till den nya, delade platsen. Om detta inte sker inom ~30–60 sekunder går du till platsen där den ursprungliga HoloLens var när du konfigurerade fästpunkten för att samla in fler miljö ledtrådar. Om platsen fortfarande inte är låst distribuerar du om till enheten.
- När alla enheter är redo och kör appen letar du upp EnergyHub. Kan ni alla komma överens om hologrammets plats och vilken riktning texten är riktad mot?
Kapitel 4 – Identifiering
Alla kan nu se samma hologram! Nu ska vi se alla andra som är anslutna till vår delade holografiska värld. I det här kapitlet hämtar vi huvudplatsen och roteringen av alla andra HoloLens-enheter i samma delningssession.
Mål
- Upptäck varandra i vår gemensamma upplevelse.
- Välj och dela en spelaravatar.
- Fäst spelaren avatar bredvid allas huvuden.
Instruktioner
- I projektpanelen navigerar du till mappen Hologram.
- Dra och släpp PlayerAvatarStore i hierarkin.
- I projektpanelennavigerar du till mappen Skript.
- Dubbelklicka på AvatarSelector-skriptet för att öppna det i Visual Studio.
- Ersätt innehållet med koden nedan.
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;
}
}
- I Hierarki väljer du objektet HologramCollection .
- I Inspector klickar du på Lägg till komponent.
- I sökrutan skriver du Local Player Manager. Välj sökresultatet.
- I Hierarki väljer du objektet HologramCollection .
- I Inspector klickar du på Lägg till komponent.
- I sökrutan skriver du Remote Player Manager. Välj sökresultatet.
- Öppna HologramPlacement-skriptet i Visual Studio.
- Ersätt innehållet med koden nedan.
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.
}
}
- Öppna Skriptet AppStateManager i Visual Studio.
- Ersätt innehållet med koden nedan.
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;
}
}
}
Distribuera och njut
- Skapa och distribuera projektet till dina HoloLens-enheter.
- När du hör ett pingande ljud hittar du menyn för avatarval och väljer en avatar med lufttrycksgesten.
- Om du inte tittar på några hologram ändrar punktljuset runt markören en annan färg när HoloLens kommunicerar med tjänsten: initierar (mörklila), laddar ned fästpunkten (grön), importerar/exporterar platsdata (gul) och laddar upp fästpunkten (blå). Om din punkt ljus runt markören är standardfärgen (ljuslila), då är du redo att interagera med andra spelare i din session!
- Titta på andra människor som är anslutna till ditt utrymme - det kommer att finnas en holografisk robot som flyter över axeln och efterliknar deras huvudrörelser!
Kapitel 5 - Placering
I det här kapitlet ska vi göra så att fästpunkten kan placeras på verkliga ytor. Vi använder delade koordinater för att placera fästpunkten i mittpunkten mellan alla som är anslutna till den delade upplevelsen.
Mål
- Placera hologram på det rumsliga mappningsnätet baserat på spelarnas huvudposition.
Instruktioner
- I projektpanelen navigerar du till mappen Hologram.
- Dra och släpp prefab för CustomSpatialMapping i hierarkin.
- I projektpanelennavigerar du till mappen Skript.
- Dubbelklicka på skriptet AppStateManager för att öppna det i Visual Studio.
- Ersätt innehållet med koden nedan.
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;
}
}
}
- I projektpanelennavigerar du till mappen Skript.
- Dubbelklicka på skriptet HologramPlacement för att öppna det i Visual Studio.
- Ersätt innehållet med koden nedan.
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();
}
}
Distribuera och dra nytta av
- Skapa och distribuera projektet till dina HoloLens-enheter.
- När appen är klar kan du stå i en cirkel och se hur EnergyHub visas i mitten av alla.
- Tryck för att placera EnergyHub.
- Prova röstkommandot "Återställ mål" för att välja EnergyHub-säkerhetskopieringen och arbeta tillsammans som en grupp för att flytta hologrammet till en ny plats.
Kapitel 6 - Real-World fysik
I det här kapitlet lägger vi till hologram som studsar från verkliga ytor. Se ditt utrymme fyllas med projekt som lanserats av både dig och dina vänner!
Mål
- Starta projektiler som studsar från verkliga ytor.
- Dela projektilerna så att andra spelare kan se dem.
Instruktioner
- I Hierarki väljer du objektet HologramCollection .
- I Inspector klickar du på Lägg till komponent.
- I sökrutan skriver du Projectile Launcher. Välj sökresultatet.
Distribuera och dra nytta av
- Skapa och distribuera till dina HoloLens-enheter.
- När appen körs på alla enheter utför du en lufttryckning för att starta projektilen på verkliga ytor.
- Se vad som händer när din projektil kolliderar med en annan spelares avatar!
Kapitel 7 - Grand Finale
I det här kapitlet ska vi hitta en portal som bara kan identifieras med samarbete.
Mål
- Samarbeta för att starta tillräckligt med projektiler vid fästpunkten för att hitta en hemlig portal!
Instruktioner
- I projektpanelen navigerar du till mappen Hologram.
- Dra och släpp underjordstillgången som underordnad HologramCollection.
- När HologramCollection är valt klickar du på knappen Lägg till komponent i Inspector.
- I menyn skriver du i sökrutan ExplodeTarget. Välj sökresultatet.
- När Du har valt HologramCollection drar du EnergyHub-objektet från Hierarki till fältet Mål i Inspector.
- När HologramCollection är markerat drar du objektet Underworld från Hierarki till fältet Underworld i Inspector.
Distribuera och dra nytta av
- Skapa och distribuera till dina HoloLens-enheter.
- När appen har startats kan du samarbeta för att starta projektiler på EnergyHub.
- När underjorden dyker upp lanserar du projektiler på robotar i underjorden (träffa en robot tre gånger för extra kul).