using UnityEngine;
publicclassWorldCursor : MonoBehaviour
{
private MeshRenderer meshRenderer;
// Use this for initializationvoidStart()
{
// Grab the mesh renderer that's on the same object as this script.
meshRenderer = this.gameObject.GetComponentInChildren<MeshRenderer>();
}
// Update is called once per framevoidUpdate()
{
// Do a raycast into the world based on the user's// head position and orientation.var headPosition = Camera.main.transform.position;
var gazeDirection = Camera.main.transform.forward;
RaycastHit hitInfo;
if (Physics.Raycast(headPosition, gazeDirection, out hitInfo))
{
// If the raycast hit a hologram...// Display the cursor mesh.
meshRenderer.enabled = true;
// Move the cursor to the point where the raycast hit.this.transform.position = hitInfo.point;
// Rotate the cursor to hug the surface of the hologram.this.transform.rotation = Quaternion.FromToRotation(Vector3.up, hitInfo.normal);
}
else
{
// If the raycast did not hit a hologram, hide the cursor mesh.
meshRenderer.enabled = false;
}
}
}
[ファイル] > [ビルド設定] の順に選択してアプリをリビルドします。
先ほど、HoloLens へのデプロイに使用した Visual Studio ソリューションに戻ります。
Visual Studio で GazeGestureManager スクリプトを開き、次のコードを追加します。
cs
using UnityEngine;
using UnityEngine.XR.WSA.Input;
publicclassGazeGestureManager : MonoBehaviour
{
publicstatic GazeGestureManager Instance { get; privateset; }
// Represents the hologram that is currently being gazed at.public GameObject FocusedObject { get; privateset; }
GestureRecognizer recognizer;
// Use this for initializationvoidAwake()
{
Instance = this;
// Set up a GestureRecognizer to detect Select gestures.
recognizer = new GestureRecognizer();
recognizer.Tapped += (args) =>
{
// Send an OnSelect message to the focused object and its ancestors.if (FocusedObject != null)
{
FocusedObject.SendMessageUpwards("OnSelect", SendMessageOptions.DontRequireReceiver);
}
};
recognizer.StartCapturingGestures();
}
// Update is called once per framevoidUpdate()
{
// Figure out which hologram is focused this frame.
GameObject oldFocusObject = FocusedObject;
// Do a raycast into the world based on the user's// head position and orientation.var headPosition = Camera.main.transform.position;
var gazeDirection = Camera.main.transform.forward;
RaycastHit hitInfo;
if (Physics.Raycast(headPosition, gazeDirection, out hitInfo))
{
// If the raycast hit a hologram, use that as the focused object.
FocusedObject = hitInfo.collider.gameObject;
}
else
{
// If the raycast did not hit a hologram, clear the focused object.
FocusedObject = null;
}
// If the focused object changed this frame,// start detecting fresh gestures again.if (FocusedObject != oldFocusObject)
{
recognizer.CancelGestures();
recognizer.StartCapturingGestures();
}
}
}
編集のために Visual Studio でスクリプトを開き、既定のコードを次のコードに置き換えます。
cs
using UnityEngine;
publicclassSphereCommands : MonoBehaviour
{
// Called by GazeGestureManager when the user performs a Select gesturevoidOnSelect()
{
// If the sphere has no Rigidbody component, add one to enable physics.if (!this.GetComponent<Rigidbody>())
{
var rigidbody = this.gameObject.AddComponent<Rigidbody>();
rigidbody.collisionDetectionMode = CollisionDetectionMode.Continuous;
}
}
}
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Windows.Speech;
publicclassSpeechManager : MonoBehaviour
{
KeywordRecognizer keywordRecognizer = null;
Dictionary<string, System.Action> keywords = new Dictionary<string, System.Action>();
// Use this for initializationvoidStart()
{
keywords.Add("Reset world", () =>
{
// Call the OnReset method on every descendant object.this.BroadcastMessage("OnReset");
});
keywords.Add("Drop Sphere", () =>
{
var focusObject = GazeGestureManager.Instance.FocusedObject;
if (focusObject != null)
{
// Call the OnDrop method on just the focused object.
focusObject.SendMessage("OnDrop", SendMessageOptions.DontRequireReceiver);
}
});
// Tell the KeywordRecognizer about our keywords.
keywordRecognizer = new KeywordRecognizer(keywords.Keys.ToArray());
// Register a callback for the KeywordRecognizer and start recognizing!
keywordRecognizer.OnPhraseRecognized += KeywordRecognizer_OnPhraseRecognized;
keywordRecognizer.Start();
}
privatevoidKeywordRecognizer_OnPhraseRecognized(PhraseRecognizedEventArgs args)
{
System.Action keywordAction;
if (keywords.TryGetValue(args.text, out keywordAction))
{
keywordAction.Invoke();
}
}
}
Visual Studio で SphereCommands スクリプトを開きます。
このスクリプトを、次のように更新します。
cs
using UnityEngine;
publicclassSphereCommands : MonoBehaviour
{
Vector3 originalPosition;
// Use this for initializationvoidStart()
{
// Grab the original local position of the sphere when the app starts.
originalPosition = this.transform.localPosition;
}
// Called by GazeGestureManager when the user performs a Select gesturevoidOnSelect()
{
// If the sphere has no Rigidbody component, add one to enable physics.if (!this.GetComponent<Rigidbody>())
{
var rigidbody = this.gameObject.AddComponent<Rigidbody>();
rigidbody.collisionDetectionMode = CollisionDetectionMode.Continuous;
}
}
// Called by SpeechManager when the user says the "Reset world" commandvoidOnReset()
{
// If the sphere has a Rigidbody component, remove it to disable physics.var rigidbody = this.GetComponent<Rigidbody>();
if (rigidbody != null)
{
rigidbody.isKinematic = true;
Destroy(rigidbody);
}
// Put the sphere back into its original local position.this.transform.localPosition = originalPosition;
}
// Called by SpeechManager when the user says the "Drop sphere" commandvoidOnDrop()
{
// Just do the same logic as a Select gesture.
OnSelect();
}
}
Visual Studio で SphereSounds を開き、次のコードを更新し、[すべて保存] をクリックします。
cs
using UnityEngine;
publicclassSphereSounds : MonoBehaviour
{
AudioSource impactAudioSource = null;
AudioSource rollingAudioSource = null;
bool rolling = false;
voidStart()
{
// Add an AudioSource component and set up some defaults
impactAudioSource = gameObject.AddComponent<AudioSource>();
impactAudioSource.playOnAwake = false;
impactAudioSource.spatialize = true;
impactAudioSource.spatialBlend = 1.0f;
impactAudioSource.dopplerLevel = 0.0f;
impactAudioSource.rolloffMode = AudioRolloffMode.Logarithmic;
impactAudioSource.maxDistance = 20f;
rollingAudioSource = gameObject.AddComponent<AudioSource>();
rollingAudioSource.playOnAwake = false;
rollingAudioSource.spatialize = true;
rollingAudioSource.spatialBlend = 1.0f;
rollingAudioSource.dopplerLevel = 0.0f;
rollingAudioSource.rolloffMode = AudioRolloffMode.Logarithmic;
rollingAudioSource.maxDistance = 20f;
rollingAudioSource.loop = true;
// Load the Sphere sounds from the Resources folder
impactAudioSource.clip = Resources.Load<AudioClip>("Impact");
rollingAudioSource.clip = Resources.Load<AudioClip>("Rolling");
}
// Occurs when this object starts colliding with another objectvoidOnCollisionEnter(Collision collision)
{
// Play an impact sound if the sphere impacts strongly enough.if (collision.relativeVelocity.magnitude >= 0.1f)
{
impactAudioSource.Play();
}
}
// Occurs each frame that this object continues to collide with another objectvoidOnCollisionStay(Collision collision)
{
Rigidbody rigid = gameObject.GetComponent<Rigidbody>();
// Play a rolling sound if the sphere is rolling fast enough.if (!rolling && rigid.velocity.magnitude >= 0.01f)
{
rolling = true;
rollingAudioSource.Play();
}
// Stop the rolling sound if rolling slows down.elseif (rolling && rigid.velocity.magnitude < 0.01f)
{
rolling = false;
rollingAudioSource.Stop();
}
}
// Occurs when this object stops colliding with another objectvoidOnCollisionExit(Collision collision)
{
// Stop the rolling sound if the object falls off and stops colliding.if (rolling)
{
rolling = false;
impactAudioSource.Stop();
rollingAudioSource.Stop();
}
}
}
Visual Studio で TapToPlaceParent スクリプトを開き、次のようにコードを変更します。
cs
using UnityEngine;
publicclassTapToPlaceParent : MonoBehaviour
{
bool placing = false;
// Called by GazeGestureManager when the user performs a Select gesturevoidOnSelect()
{
// On each Select gesture, toggle whether the user is in placing mode.
placing = !placing;
// If the user is in placing mode, display the spatial mapping mesh.if (placing)
{
SpatialMapping.Instance.DrawVisualMeshes = true;
}
// If the user is not in placing mode, hide the spatial mapping mesh.else
{
SpatialMapping.Instance.DrawVisualMeshes = false;
}
}
// Update is called once per framevoidUpdate()
{
// If the user is in placing mode,// update the placement to match the user's gaze.if (placing)
{
// Do a raycast into the world that will only hit the Spatial Mapping mesh.var headPosition = Camera.main.transform.position;
var gazeDirection = Camera.main.transform.forward;
RaycastHit hitInfo;
if (Physics.Raycast(headPosition, gazeDirection, out hitInfo,
30.0f, SpatialMapping.PhysicsRaycastMask))
{
// Move this object's parent object to// where the raycast hit the Spatial Mapping mesh.this.transform.parent.position = hitInfo.point;
// Rotate this object's parent object to face the user.
Quaternion toQuat = Camera.main.transform.localRotation;
toQuat.x = 0;
toQuat.z = 0;
this.transform.parent.rotation = toQuat;
}
}
}
}
Visual Studio で HitTarget スクリプトを開き、次のようにコードを変更します。
cs
using UnityEngine;
publicclassHitTarget : MonoBehaviour
{
// These public fields become settable properties in the Unity editor.public GameObject underworld;
public GameObject objectToHide;
// Occurs when this object starts colliding with another objectvoidOnCollisionEnter(Collision collision)
{
// Hide the stage and show the underworld.
objectToHide.SetActive(false);
underworld.SetActive(true);
// Disable Spatial Mapping to let the spheres enter the underworld.
SpatialMapping.Instance.MappingEnabled = false;
}
}