318 lines
12 KiB
C#
318 lines
12 KiB
C#
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
|
|
using Microsoft.MixedReality.Toolkit.Input;
|
|
using Microsoft.MixedReality.Toolkit.Utilities;
|
|
using Unity.Profiling;
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
|
|
namespace Microsoft.MixedReality.Toolkit.Teleport
|
|
{
|
|
/// <summary>
|
|
/// The Mixed Reality Toolkit's implementation of the <see cref="Microsoft.MixedReality.Toolkit.Teleport.IMixedRealityTeleportSystem"/>.
|
|
/// </summary>
|
|
public class MixedRealityTeleportSystem : BaseCoreSystem, IMixedRealityTeleportSystem
|
|
{
|
|
/// <summary>
|
|
/// Constructor.
|
|
/// </summary>
|
|
/// <param name="registrar">The <see cref="IMixedRealityServiceRegistrar"/> instance that loaded the service.</param>
|
|
[System.Obsolete("This constructor is obsolete (registrar parameter is no longer required) and will be removed in a future version of the Microsoft Mixed Reality Toolkit.")]
|
|
public MixedRealityTeleportSystem(
|
|
IMixedRealityServiceRegistrar registrar) : base(registrar, null) // Teleport system does not use a profile
|
|
{
|
|
Registrar = registrar;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructor.
|
|
/// </summary>
|
|
public MixedRealityTeleportSystem() : base(null) { } // Teleport system does not use a profile
|
|
|
|
private TeleportEventData teleportEventData;
|
|
|
|
private bool isTeleporting = false;
|
|
private bool isProcessingTeleportRequest = false;
|
|
|
|
private Vector3 targetPosition = Vector3.zero;
|
|
private Vector3 targetRotation = Vector3.zero;
|
|
|
|
/// <summary>
|
|
/// Used to clean up event system when shutting down, if this system created one.
|
|
/// </summary>
|
|
private GameObject eventSystemReference = null;
|
|
|
|
#region IMixedRealityService Implementation
|
|
|
|
/// <inheritdoc/>
|
|
public override string Name { get; protected set; } = "Mixed Reality Teleport System";
|
|
|
|
/// <inheritdoc />
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
InitializeInternal();
|
|
}
|
|
|
|
private void InitializeInternal()
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (!UnityEditor.EditorApplication.isPlaying)
|
|
{
|
|
var eventSystems = Object.FindObjectsOfType<EventSystem>();
|
|
|
|
if (eventSystems.Length == 0)
|
|
{
|
|
if (!IsInputSystemEnabled)
|
|
{
|
|
eventSystemReference = new GameObject("Event System");
|
|
eventSystemReference.AddComponent<EventSystem>();
|
|
}
|
|
else
|
|
{
|
|
Debug.Log("The input system didn't properly add an event system to your scene. Please make sure the input system's priority is set higher than the teleport system.");
|
|
}
|
|
}
|
|
else if (eventSystems.Length > 1)
|
|
{
|
|
Debug.Log("Too many event systems in the scene. The Teleport System requires only one.");
|
|
}
|
|
}
|
|
#endif // UNITY_EDITOR
|
|
|
|
teleportEventData = new TeleportEventData(EventSystem.current);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void Destroy()
|
|
{
|
|
base.Destroy();
|
|
|
|
if (eventSystemReference != null)
|
|
{
|
|
if (!Application.isPlaying)
|
|
{
|
|
Object.DestroyImmediate(eventSystemReference);
|
|
}
|
|
else
|
|
{
|
|
Object.Destroy(eventSystemReference);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion IMixedRealityService Implementation
|
|
|
|
#region IEventSystemManager Implementation
|
|
|
|
private static readonly ProfilerMarker HandleEventPerfMarker = new ProfilerMarker("[MRTK] MixedRealityTeleportSystem.HandleEvent");
|
|
|
|
/// <inheritdoc />
|
|
public override void HandleEvent<T>(BaseEventData eventData, ExecuteEvents.EventFunction<T> eventHandler)
|
|
{
|
|
using (HandleEventPerfMarker.Auto())
|
|
{
|
|
Debug.Assert(eventData != null);
|
|
var teleportData = ExecuteEvents.ValidateEventData<TeleportEventData>(eventData);
|
|
Debug.Assert(teleportData != null);
|
|
Debug.Assert(!teleportData.used);
|
|
|
|
// Process all the event listeners
|
|
base.HandleEvent(teleportData, eventHandler);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Register a <see href="https://docs.unity3d.com/ScriptReference/GameObject.html">GameObject</see> to listen to teleport events.
|
|
/// </summary>
|
|
public override void Register(GameObject listener) => base.Register(listener);
|
|
|
|
/// <summary>
|
|
/// Unregister a <see href="https://docs.unity3d.com/ScriptReference/GameObject.html">GameObject</see> from listening to teleport events.
|
|
/// </summary>
|
|
public override void Unregister(GameObject listener) => base.Unregister(listener);
|
|
|
|
#endregion IEventSystemManager Implementation
|
|
|
|
#region IMixedRealityTeleportSystem Implementation
|
|
|
|
/// <summary>
|
|
/// Is an input system registered?
|
|
/// </summary>
|
|
private bool IsInputSystemEnabled => CoreServices.InputSystem != null;
|
|
|
|
private float teleportDuration = 0.25f;
|
|
|
|
/// <inheritdoc />
|
|
public float TeleportDuration
|
|
{
|
|
get => teleportDuration;
|
|
set
|
|
{
|
|
if (isProcessingTeleportRequest)
|
|
{
|
|
Debug.LogWarning("Couldn't change teleport duration. Teleport in progress.");
|
|
return;
|
|
}
|
|
|
|
teleportDuration = value;
|
|
}
|
|
}
|
|
|
|
private static readonly ExecuteEvents.EventFunction<IMixedRealityTeleportHandler> OnTeleportRequestHandler =
|
|
delegate (IMixedRealityTeleportHandler handler, BaseEventData eventData)
|
|
{
|
|
var casted = ExecuteEvents.ValidateEventData<TeleportEventData>(eventData);
|
|
handler.OnTeleportRequest(casted);
|
|
};
|
|
|
|
private static readonly ProfilerMarker RaiseTeleportRequestPerfMarker = new ProfilerMarker("[MRTK] MixedRealityTeleportSystem.RaiseTeleportRequest");
|
|
|
|
/// <inheritdoc />
|
|
public void RaiseTeleportRequest(IMixedRealityPointer pointer, IMixedRealityTeleportHotspot hotSpot)
|
|
{
|
|
using (RaiseTeleportRequestPerfMarker.Auto())
|
|
{
|
|
// initialize event
|
|
teleportEventData.Initialize(pointer, hotSpot);
|
|
|
|
// Pass handler
|
|
HandleEvent(teleportEventData, OnTeleportRequestHandler);
|
|
}
|
|
}
|
|
|
|
private static readonly ExecuteEvents.EventFunction<IMixedRealityTeleportHandler> OnTeleportStartedHandler =
|
|
delegate (IMixedRealityTeleportHandler handler, BaseEventData eventData)
|
|
{
|
|
var casted = ExecuteEvents.ValidateEventData<TeleportEventData>(eventData);
|
|
handler.OnTeleportStarted(casted);
|
|
};
|
|
|
|
private static readonly ProfilerMarker RaiseTeleportStartedPerfMarker = new ProfilerMarker("[MRTK] MixedRealityTeleportSystem.RaiseTeleportStarted");
|
|
|
|
/// <inheritdoc />
|
|
public void RaiseTeleportStarted(IMixedRealityPointer pointer, IMixedRealityTeleportHotspot hotSpot)
|
|
{
|
|
if (isTeleporting)
|
|
{
|
|
Debug.LogError("Teleportation already in progress");
|
|
return;
|
|
}
|
|
|
|
using (RaiseTeleportStartedPerfMarker.Auto())
|
|
{
|
|
isTeleporting = true;
|
|
|
|
// initialize event
|
|
teleportEventData.Initialize(pointer, hotSpot);
|
|
|
|
// Pass handler
|
|
HandleEvent(teleportEventData, OnTeleportStartedHandler);
|
|
|
|
ProcessTeleportationRequest(teleportEventData);
|
|
}
|
|
}
|
|
|
|
private static readonly ExecuteEvents.EventFunction<IMixedRealityTeleportHandler> OnTeleportCompletedHandler =
|
|
delegate (IMixedRealityTeleportHandler handler, BaseEventData eventData)
|
|
{
|
|
var casted = ExecuteEvents.ValidateEventData<TeleportEventData>(eventData);
|
|
handler.OnTeleportCompleted(casted);
|
|
};
|
|
|
|
private static readonly ProfilerMarker RaiseTeleportCompletePerfMarker = new ProfilerMarker("[MRTK] MixedRealityTeleportSystem.RaiseTeleportComplete");
|
|
|
|
/// <summary>
|
|
/// Raise a teleportation completed event.
|
|
/// </summary>
|
|
/// <param name="pointer">The pointer that raised the event.</param>
|
|
/// <param name="hotSpot">The teleport target</param>
|
|
private void RaiseTeleportComplete(IMixedRealityPointer pointer, IMixedRealityTeleportHotspot hotSpot)
|
|
{
|
|
if (!isTeleporting)
|
|
{
|
|
Debug.LogError("No Active Teleportation in progress.");
|
|
return;
|
|
}
|
|
|
|
using (RaiseTeleportCompletePerfMarker.Auto())
|
|
{
|
|
// initialize event
|
|
teleportEventData.Initialize(pointer, hotSpot);
|
|
|
|
// Pass handler
|
|
HandleEvent(teleportEventData, OnTeleportCompletedHandler);
|
|
|
|
isTeleporting = false;
|
|
}
|
|
}
|
|
|
|
private static readonly ExecuteEvents.EventFunction<IMixedRealityTeleportHandler> OnTeleportCanceledHandler =
|
|
delegate (IMixedRealityTeleportHandler handler, BaseEventData eventData)
|
|
{
|
|
var casted = ExecuteEvents.ValidateEventData<TeleportEventData>(eventData);
|
|
handler.OnTeleportCanceled(casted);
|
|
};
|
|
|
|
private static readonly ProfilerMarker RaiseTeleportCanceledPerfMarker = new ProfilerMarker("[MRTK] MixedRealityTeleportSystem.RaiseTeleportHandled");
|
|
|
|
/// <inheritdoc />
|
|
public void RaiseTeleportCanceled(IMixedRealityPointer pointer, IMixedRealityTeleportHotspot hotSpot)
|
|
{
|
|
using (RaiseTeleportCanceledPerfMarker.Auto())
|
|
{
|
|
// initialize event
|
|
teleportEventData.Initialize(pointer, hotSpot);
|
|
|
|
// Pass handler
|
|
HandleEvent(teleportEventData, OnTeleportCanceledHandler);
|
|
}
|
|
}
|
|
|
|
#endregion IMixedRealityTeleportSystem Implementation
|
|
|
|
private static readonly ProfilerMarker ProcessTeleportationRequestPerfMarker = new ProfilerMarker("[MRTK] MixedRealityTeleportSystem.ProcessTeleportationRequest");
|
|
|
|
private void ProcessTeleportationRequest(TeleportEventData eventData)
|
|
{
|
|
using (ProcessTeleportationRequestPerfMarker.Auto())
|
|
{
|
|
isProcessingTeleportRequest = true;
|
|
|
|
targetRotation = Vector3.zero;
|
|
if (eventData.Pointer is IMixedRealityTeleportPointer teleportPointer && !teleportPointer.IsNull())
|
|
{
|
|
targetRotation.y = teleportPointer.PointerOrientation;
|
|
}
|
|
targetPosition = eventData.Pointer.Result.Details.Point;
|
|
|
|
if (eventData.Hotspot != null)
|
|
{
|
|
targetPosition = eventData.Hotspot.Position;
|
|
|
|
if (eventData.Hotspot.OverrideOrientation)
|
|
{
|
|
targetRotation.y = eventData.Hotspot.TargetRotation;
|
|
}
|
|
}
|
|
|
|
float height = targetPosition.y;
|
|
targetPosition -= CameraCache.Main.transform.position - MixedRealityPlayspace.Position;
|
|
targetPosition.y = height;
|
|
|
|
MixedRealityPlayspace.Position = targetPosition;
|
|
MixedRealityPlayspace.RotateAround(
|
|
CameraCache.Main.transform.position,
|
|
Vector3.up,
|
|
targetRotation.y - CameraCache.Main.transform.eulerAngles.y);
|
|
|
|
isProcessingTeleportRequest = false;
|
|
|
|
// Raise complete event using the pointer and hot spot provided.
|
|
RaiseTeleportComplete(eventData.Pointer, eventData.Hotspot);
|
|
}
|
|
}
|
|
}
|
|
}
|