Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Önemli
Karma Gerçeklik Academy öğreticileri HoloLens (1. nesil), Unity 2017 ve Karma Gerçeklik Immersive Headsets düşünülerek tasarlanmıştır. Bu nedenle, bu cihazlar için geliştirme konusunda hala rehberlik arayan geliştiriciler için bu öğreticileri yerinde bırakmanın önemli olduğunu hissediyoruz. Bu öğreticiler, HoloLens 2 için kullanılan en son araç kümeleri veya etkileşimlerle güncelleştirilmez ve Unity'nin daha yeni sürümleriyle uyumlu olmayabilir. Desteklenen cihazlarda çalışmaya devam etmek için bakımları yapılır. HoloLens 2 için yeni bir öğretici serisi yayınlanmıştır.
Hologramlara dünyamızda biz uzayda hareket ettikçe yerinde kalarak varlık verilir. HoloLens, nesnelerin konumunu ve yönlendirmesini izlemek için çeşitli koordinat sistemlerini kullanarak hologramları yerinde tutar. Bu koordinat sistemlerini cihazlar arasında paylaştığımızda, paylaşılan bir holografik dünyada yer almamızı sağlayan paylaşılan bir deneyim oluşturabiliriz.
Bu öğreticide, şunları yapacağız:
- Paylaşılan bir deneyim için bir ağ kurun.
- HoloLens cihazları arasında hologramları paylaşın.
- Paylaşılan holografik dünyamızdaki diğer kişileri keşfedin.
- Diğer oyuncuları hedefleyebileceğiniz ve onlara mermi fırlatabileceğiniz paylaşılan bir etkileşimli deneyim oluşturun!
Cihaz desteği
Kurs | HoloLens | Çevreleyici kulaklıklar |
---|---|---|
MR Sharing 240: Birden çok HoloLens cihazı | ✔️ |
Başlamadan önce
Önkoşullar
- İnternet erişimiyle birlikte doğru araçlarla yapılandırılmış bir Windows 10 bilgisayar.
- Geliştirme için yapılandırılmış en az iki HoloLens cihazı.
Proje dosyaları
- Projenin gerektirdiği dosyaları indirin. Unity 2017.2 veya sonraki bir sürümü gerektirir.
- Dosyaları masaüstünüzde veya ulaşılmaya kolay başka bir konumda arşivden kaldırın. Klasör adını SharedHolograms olarak tutun.
Not
İndirmeden önce kaynak kodu incelemek isterseniz GitHub'da kullanılabilir.
Bölüm 1 - Holo World
Bu bölümde ilk Unity projemizi ayarlayıp derleme ve dağıtma işleminde adım adım ilerleyeceğiz.
Hedefler
- Holografik uygulamalar geliştirmek için Unity'yi kurun.
- Hologramınızı görün!
Yönergeler
- Unity'i başlatın.
- Aç’ı seçin.
- Konumu daha önce arşivlediğiniz SharedHolograms klasörü olarak girin.
- Proje Adı'nı seçin ve Klasör Seç'e tıklayın.
- Hiyerarşi'deAna Kamera'ya sağ tıklayın ve Sil'i seçin.
- HoloToolkit-Sharing-240/Prefabs/Camera klasöründe Ana Kamera ön dosyasını bulun.
- Ana KamerayıSürükleyip Hiyerarşi'ye bırakın.
- Hiyerarşi'de Oluştur ve Boş Oluştur'a tıklayın.
- Yeni GameObject'e sağ tıklayın ve Yeniden Adlandır'ı seçin.
- GameObject'i HologramCollection olarak yeniden adlandırın.
- HiyerarşideHologramCollection nesnesini seçin.
- Denetçidedönüştürme konumunu şu şekilde ayarlayın: X: 0, Y: -0.25, Z: 2.
- Proje panelindekiHologramlar klasöründe EnergyHub varlığını bulun.
- Project panelindekiEnergyHub nesnesini, HologramCollection'ın alt öğesi olarak Hiyerarşi'ye sürükleyip bırakın.
- Sahneyi Farklı Kaydet... öğesini > seçin
- Sahneyi SharedHolograms olarak adlandırın ve Kaydet'e tıklayın.
- Hologramlarınızın önizlemesini görüntülemek için Unity'de Yürüt düğmesine basın.
- Önizleme modunu durdurmak için Oynat'a ikinci kez basın.
Projeyi Unity'den Visual Studio'ya aktarma
- Unity'de Dosya > Derleme Ayarları'nı seçin.
- Sahneyi eklemek için Açık Sahne Ekle'ye tıklayın.
- Platformlistesinden Evrensel Windows Platformu seçin ve Platform Değiştir'e tıklayın.
- SDK'yıEvrensel 10 olarak ayarlayın.
- Hedef cihazıHoloLens ve UWP Derleme Türü'nüD3D olarak ayarlayın.
- Unity C# Projelerini denetleyin.
- Oluştur'a tıklayın.
- Görüntülenen dosya gezgini penceresinde "Uygulama" adlı yeni bir Klasör oluşturun.
- Uygulama klasörüne tek tek tıklayın.
- Klasör Seç'e basın.
- Unity tamamlandığında bir Dosya Gezgini penceresi görüntülenir.
- Uygulama klasörünü açın.
- Visual Studio'yu başlatmak için SharedHolograms.sln dosyasını açın.
- Visual Studio'daki üst araç çubuğunu kullanarak hedefi Hata Ayıklama'dan Yayın'a ve ARM'den X86'ya değiştirin.
- Yerel Makine'nin yanındaki açılan oka tıklayın ve Uzak Cihaz'ı seçin.
- Adres'i HoloLens'inizin adı veya IP adresi olarak ayarlayın. Cihazınızın IP adresini bilmiyorsanız Ayarlar > Ağ & İnternet > Gelişmiş Seçenekleri'ne bakın veya Cortana'ya "Hey Cortana, IP adresim nedir?" sorusunu sorun.
- Kimlik Doğrulama Modu'nuEvrensel olarak bırakın.
- Seç'e tıklayın
- Hata Ayıklama Hata Ayıklama > Olmadan Başlat'a tıklayın veya Ctrl + F5 tuşlarına basın. Cihazınıza ilk kez dağıtılıyorsa, bunu Visual Studio ile eşleştirmeniz gerekir.
- HoloLens'inizi takın ve EnergyHub hologramını bulun.
2. Bölüm - Etkileşim
Bu bölümde hologramlarımızla etkileşim kuracağız. İlk olarak, Bakış'ımızı görselleştirmek için bir imleç ekleyeceğiz. Ardından , Hareketler ekleyeceğiz ve hologramlarımızı uzaya yerleştirmek için elimizi kullanacağız.
Hedefler
- İmleci denetlemek için bakış girişini kullanın.
- Hologramlarla etkileşime geçmek için hareket girişini kullanın.
Yönergeler
Bakış
- Hiyerarşi panelindeHologramCollection nesnesini seçin.
- Denetçi panelindeBileşen Ekle düğmesine tıklayın.
- Menüde, Arama kutusuna Bakış Yöneticisi yazın. Arama sonucunu seçin.
- HoloToolkit-Sharing-240\Prefabs\Input klasöründe İmleç varlığını bulun.
- İmleç varlığını sürükleyip Hiyerarşi'ye bırakın.
Hareketi
- Hiyerarşi panelindeHologramCollection nesnesini seçin.
- Bileşen Ekle'ye tıklayın ve arama alanına Hareket Yöneticisi yazın. Arama sonucunu seçin.
- Hiyerarşi panelindeHologramCollection'ı genişletin.
- Alt EnergyHub nesnesini seçin.
- Denetçi panelindeBileşen Ekle düğmesine tıklayın.
- Menüde Hologram Yerleşimi arama kutusuna yazın. Arama sonucunu seçin.
- Dosya > Görünümü Kaydet'i seçerek sahneyi kaydedin.
Dağıtma ve keyfini çıkarma
- Önceki bölümde yer alan yönergeleri kullanarak HoloLens'inizi oluşturun ve dağıtın.
- Uygulama HoloLens'inizde başlatıldıktan sonra başınızı hareket ettirin ve EnergyHub'ın bakışınızı nasıl izlediğine dikkat edin.
- Holograma baktığınızda imlecin nasıl göründüğüne ve holograma bakmadığınızda nokta ışığına değiştiğine dikkat edin.
- Hologramı yerleştirmek için havadan dokunma gerçekleştirin. Şu anda projemizde hologramı yalnızca bir kez yerleştirebilirsiniz (yeniden denemek için yeniden dağıtın).
3. Bölüm - Paylaşılan Koordinatlar
Hologramları görmek ve bunlarla etkileşime geçmek eğlencelidir, ancak daha ileri gidelim. herkesin birlikte görebileceği bir hologram olan ilk paylaşılan deneyimimizi ayarlayacağız.
Hedefler
- Paylaşılan bir deneyim için bir ağ kurun.
- Ortak bir başvuru noktası oluşturun.
- Koordinat sistemlerini cihazlar arasında paylaşın.
- Herkes aynı hologramı görür!
Not
Bir uygulamanın paylaşım sunucusuna bağlanması için InternetClientServer ve PrivateNetworkClientServer özellikleri bildirilmelidir. Bu zaten Hologramlar 240'ta sizin için yapılır, ancak kendi projeleriniz için bunu aklınızda bulundurun.
- Unity Düzenleyicisi'nde"Proje Ayarlarını > Düzenle > Yürütücü" bölümüne giderek oynatıcı ayarlarına gidin
- "Windows Mağazası" sekmesine tıklayın
- "Yayımlama Ayarları > Özellikleri" bölümünde InternetClientServer özelliğini ve PrivateNetworkClientServer özelliğini denetleyin
Yönergeler
- Proje panelindeHoloToolkit-Sharing-240\Prefabs\Sharing klasörüne gidin.
- Paylaşım ön koşulunu Sürükleyip Hiyerarşi paneline bırakın.
Ardından paylaşım hizmetini başlatmamız gerekiyor. Bu adımı paylaşılan deneyimde yalnızca bir bilgisayar gerçekleştirmesi gerekir.
- Unity'de , üstteki menüden HoloToolkit-Sharing-240 menüsünü seçin.
- Açılan listeden Paylaşım Hizmetini Başlat öğesini seçin.
- Özel Ağ seçeneğini işaretleyin ve güvenlik duvarı istemi görüntülendiğinde Erişime İzin Ver'e tıklayın.
- Paylaşım Hizmeti konsol penceresinde görüntülenen IPv4 adresini not edin. Bu, hizmetin çalıştırıldığı makineyle aynı IP'dir.
Paylaşılan deneyime katılacak tüm bilgisayarlardaki yönergelerin geri kalanını izleyin.
- Hiyerarşi'deSharing nesnesini seçin.
- Denetçide, Paylaşım Aşaması bileşeninde Sunucu Adresi'ni 'localhost' yerine SharingService.exe çalıştıran makinenin IPv4 adresi olarak değiştirin.
- Hiyerarşi'deHologramCollection nesnesini seçin.
- Denetçi'deBileşen Ekle düğmesine tıklayın.
- Arama kutusuna Dışarı Aktarma Bağlantısı Yöneticisi'ni içeri aktar yazın. Arama sonucunu seçin.
- Proje panelindeBetikler klasörüne gidin.
- HologramPlacement betiğine çift tıklayarak Visual Studio'da açın.
- İçeriği aşağıdaki kodla değiştirin.
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.
}
}
- Unity'ye döndüğünüzde Hiyerarşi panelindeHologramCollection'ı seçin.
- Denetçi panelindeBileşen Ekle düğmesine tıklayın.
- Menüde, Uygulama Durumu Yöneticisi arama kutusuna yazın. Arama sonucunu seçin.
Dağıtma ve keyfini çıkarma
- HoloLens cihazlarınız için projeyi oluşturun.
- İlk olarak dağıtılacak bir HoloLens belirleyin. EnergyHub'ı yerleştirebilmeniz için anchor'ın hizmete yüklenmesini beklemeniz gerekir (bu işlem yaklaşık 30-60 saniye sürebilir). Karşıya yükleme işlemi tamamlanıncaya kadar dokunma hareketleriniz yoksayılır.
- EnergyHub yerleştirildikten sonra konumu hizmete yüklenir ve ardından diğer tüm HoloLens cihazlarına dağıtabilirsiniz.
- Yeni bir HoloLens oturuma ilk kez katıldığında EnergyHub'ın konumu bu cihazda doğru olmayabilir. Ancak, yer işareti ve EnergyHub konumları hizmetten indirildikten hemen sonra EnergyHub'ın yeni, paylaşılan konuma atlaması gerekir. Bu yaklaşık 30-60 saniye içinde gerçekleşmezse, daha fazla ortam ipucu toplamak için tutturucuyu ayarlarken orijinal HoloLens'in bulunduğu yere yürüyün. Konum hala kilitlenmiyorsa cihaza yeniden dağıtın.
- Tüm cihazlar hazır olduğunda ve uygulamayı çalıştırdığında EnergyHub'ı arayın. Hologramın konumu ve metnin hangi yöne dönük olduğu konusunda hemfikir misiniz?
Bölüm 4 - Bulma
Artık herkes aynı hologramı görebilir! Şimdi diğer herkesin paylaşılan holografik dünyamızla bağlantılı olduğunu görelim. Bu bölümde, aynı paylaşım oturumunda diğer tüm HoloLens cihazlarının baş konumunu ve rotasyonunu alacağız.
Hedefler
- Paylaşılan deneyimimizde birbirinizi keşfedin.
- Oyuncu avatarını seçin ve paylaşın.
- Oyuncu avatarını herkesin kafasının yanına ekleyin.
Yönergeler
- Proje panelindeHologramlar klasörüne gidin.
- PlayerAvatarStore öğesini sürükleyip Hiyerarşi'ye bırakın.
- Proje panelindeBetikler klasörüne gidin.
- AvatarSelector betiğine çift tıklayarak Visual Studio'da açın.
- İçeriği aşağıdaki kodla değiştirin.
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;
}
}
- Hiyerarşi'deHologramCollection nesnesini seçin.
- DenetçideBileşen Ekle'ye tıklayın.
- Arama kutusuna Local Player Manager yazın. Arama sonucunu seçin.
- Hiyerarşi'deHologramCollection nesnesini seçin.
- DenetçideBileşen Ekle'ye tıklayın.
- Arama kutusuna Remote Player Manager yazın. Arama sonucunu seçin.
- Visual Studio'da HologramPlacement betiğini açın.
- İçeriği aşağıdaki kodla değiştirin.
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.
}
}
- Visual Studio'da AppStateManager betiğini açın.
- İçeriği aşağıdaki kodla değiştirin.
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;
}
}
}
Dağıtma ve Keyfini Çıkarma
- Projeyi derleyin ve HoloLens cihazlarınıza dağıtın.
- Ping sesi duyduğunuzda avatar seçim menüsünü bulun ve havada dokunma hareketiyle bir avatar seçin.
- Herhangi bir holograma bakmıyorsanız HoloLens'iniz hizmetle iletişim kurarken imlecinizin etrafındaki nokta ışığı farklı bir renge dönüşür: başlatma (koyu mor), tutturucuyu indirme (yeşil), konum verilerini içeri/dışarı aktarma (sarı), yer işaretini (mavi) karşıya yükleme. İmlecinizin etrafındaki nokta ışığı varsayılan renkse (açık mor), oturumunuzda diğer oyuncularla etkileşime geçmek için hazırsınız demektir!
- Uzayınıza bağlı diğer kişilere bakın- omuzlarının üstünde yüzen ve baş hareketlerine taklit eden holografik bir robot olacak!
5. Bölüm - Yerleştirme
Bu bölümde, tutturucuyu gerçek dünya yüzeylerine yerleştirebilecek hale getireceğiz. Bu tutturucuyu, paylaşılan deneyime bağlı herkes arasında orta noktaya yerleştirmek için paylaşılan koordinatları kullanacağız.
Hedefler
- Hologramları oyuncuların kafa pozisyonuna göre uzamsal harita ağı üzerine yerleştirin.
Yönergeler
- Proje panelindeHologramlar klasörüne gidin.
- CustomSpatialMapping prefab'ını Hiyerarşi'ye sürükleyip bırakın.
- Proje panelindeBetikler klasörüne gidin.
- AppStateManager betiğine çift tıklayarak Visual Studio'da açın.
- İçeriği aşağıdaki kodla değiştirin.
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;
}
}
}
- Proje panelindeBetikler klasörüne gidin.
- HologramPlacement betiğine çift tıklayarak Visual Studio'da açın.
- İçeriği aşağıdaki kodla değiştirin.
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();
}
}
Dağıtma ve keyfini çıkarma
- Projeyi derleyin ve HoloLens cihazlarınıza dağıtın.
- Uygulama hazır olduğunda bir dairenin içinde durun ve EnergyHub'ın herkesin merkezinde nasıl göründüğüne dikkat edin.
- EnergyHub'ı yerleştirmek için dokunun.
- EnergyHub yedeklemesini seçmek ve hologramı yeni bir konuma taşımak için grup olarak birlikte çalışmak için 'Hedefi Sıfırla' sesli komutunu deneyin.
Bölüm 6 - Real-World Fizik
Bu bölümde gerçek dünya yüzeylerinden seken hologramlar ekleyeceğiz. Alanınızın hem sizin hem de arkadaşlarınızın başlattığı projelerle dolduğunu izleyin!
Hedefler
- Gerçek dünya yüzeylerinden dönen mermileri başlatın.
- Diğer oyuncuların görebilmesi için mermileri paylaşın.
Yönergeler
- Hiyerarşi'deHologramCollection nesnesini seçin.
- DenetçideBileşen Ekle'ye tıklayın.
- Arama kutusuna Projectile Launcher yazın. Arama sonucunu seçin.
Dağıtma ve keyfini çıkarma
- HoloLens cihazlarınızı derleyin ve dağıtın.
- Uygulama tüm cihazlarda çalışırken, gerçek dünya yüzeylerinde mermi başlatmak için havadan dokunma gerçekleştirin.
- Merminiz başka bir oyuncunun avatarıyla çarpıştığında ne olacağını görün!
Bölüm 7 - Büyük Final
Bu bölümde, yalnızca işbirliğiyle keşfedilebilen bir portalı ortaya çıkaracağız.
Hedefler
- Bir gizli portalı ortaya çıkarmak için yer işaretinde yeterli mermi başlatmak için birlikte çalışın!
Yönergeler
- Proje panelindeHologramlar klasörüne gidin.
- Underworld varlığını HologramCollection'ın alt öğesi olarak sürükleyip bırakın.
- HologramCollection seçili durumdayken Denetçi'dekiBileşen Ekle düğmesine tıklayın.
- Menüde , ExplodeTarget arama kutusuna yazın. Arama sonucunu seçin.
- HologramCollection seçiliyken Hiyerarşi'denEnergyHub nesnesini DenetçidekiHedef alanına sürükleyin.
- HologramCollection seçiliyken, Hiyerarşi'denYeraltıDünyası nesnesini Denetçi'dekiYeraltı Dünyası alanına sürükleyin.
Dağıtma ve keyfini çıkarma
- HoloLens cihazlarınızı derleyin ve dağıtın.
- Uygulama başlatıldığında, EnergyHub'da mermileri başlatmak için birlikte çalışın.
- Yeraltı dünyası göründüğünde, yeraltı robotlarında mermileri başlatın (ekstra eğlence için bir robota üç kez vur).