forked from cgvr/DeltaVR
deltavr multiplayer 2.0
This commit is contained in:
8
Assets/XRI_Examples/VRWalkthrough/Scripts/UI.meta
Normal file
8
Assets/XRI_Examples/VRWalkthrough/Scripts/UI.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cbdb43e6ec783e947b6ffce18f97dafa
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
85
Assets/XRI_Examples/VRWalkthrough/Scripts/UI/LazyFollow.cs
Normal file
85
Assets/XRI_Examples/VRWalkthrough/Scripts/UI/LazyFollow.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
namespace UnityEngine.XR.Content.UI.Layout
|
||||
{
|
||||
/// <summary>
|
||||
/// Makes the object this is attached to follow a target with a slight delay
|
||||
/// </summary>
|
||||
public class LazyFollow : MonoBehaviour
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[SerializeField]
|
||||
[Tooltip("The object being followed.")]
|
||||
Transform m_Target;
|
||||
#pragma warning restore 649
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Whether to always follow or only when in-view.")]
|
||||
bool m_FOV = false;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Whether rotation is locked to the z-axis for can move in any direction.")]
|
||||
bool m_ZRot = true;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Adjusts the follow point from the target by this amount.")]
|
||||
Vector3 m_TargetOffset = Vector3.forward;
|
||||
|
||||
Vector3 m_TargetLastPos;
|
||||
Camera m_Camera;
|
||||
public float smoothTime = 0.3F;
|
||||
private Vector3 velocity = Vector3.zero;
|
||||
|
||||
Vector3 targetPosition => m_Target.position + m_Target.TransformVector(m_TargetOffset);
|
||||
Quaternion targetRotation
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!m_ZRot)
|
||||
{
|
||||
var eulerAngles = m_Target.eulerAngles;
|
||||
eulerAngles = new Vector3(eulerAngles.x, eulerAngles.y, 0f);
|
||||
return Quaternion.Euler(eulerAngles);
|
||||
}
|
||||
|
||||
return m_Target.rotation;
|
||||
}
|
||||
}
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (m_Camera == null)
|
||||
m_Camera = Camera.main;
|
||||
|
||||
// Default to main camera
|
||||
if (m_Target == null)
|
||||
if (m_Camera != null)
|
||||
m_Target = m_Camera.transform;
|
||||
|
||||
transform.position = targetPosition;
|
||||
transform.rotation = targetRotation;
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
var targetPos = targetPosition;
|
||||
m_TargetLastPos = targetPos;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (m_FOV)
|
||||
{
|
||||
Vector3 screenPoint = m_Camera.WorldToViewportPoint(this.gameObject.transform.position);
|
||||
var inFov = screenPoint.z > 0f && screenPoint.x > 0f && screenPoint.x < 1f && screenPoint.y > 0f && screenPoint.y < 1f;
|
||||
if (inFov)
|
||||
return;
|
||||
}
|
||||
|
||||
var targetPos = targetPosition;
|
||||
if (m_TargetLastPos == targetPos)
|
||||
return;
|
||||
|
||||
transform.position = Vector3.SmoothDamp(transform.position, targetPos, ref velocity, smoothTime);
|
||||
m_TargetLastPos = targetPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 73fb1778a5310f041a99bc0eb94493cf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
61
Assets/XRI_Examples/VRWalkthrough/Scripts/UI/TurnToFace.cs
Normal file
61
Assets/XRI_Examples/VRWalkthrough/Scripts/UI/TurnToFace.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
namespace UnityEngine.XR.Content.UI.Layout
|
||||
{
|
||||
/// <summary>
|
||||
/// Makes this object face a target smoothly and along specific axes
|
||||
/// </summary>
|
||||
public class TurnToFace : MonoBehaviour
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[SerializeField, Tooltip("Target to face towards. If not set, this will default to the main camera")]
|
||||
Transform m_FaceTarget;
|
||||
|
||||
[SerializeField, Tooltip("Speed to turn")]
|
||||
float m_TurnToFaceSpeed = 5f;
|
||||
|
||||
[SerializeField, Tooltip("Local rotation offset")]
|
||||
Vector3 m_RotationOffset = Vector3.zero;
|
||||
|
||||
[SerializeField, Tooltip("If enabled, ignore the x axis when rotating")]
|
||||
bool m_IgnoreX;
|
||||
|
||||
[SerializeField, Tooltip("If enabled, ignore the y axis when rotating")]
|
||||
bool m_IgnoreY;
|
||||
|
||||
[SerializeField, Tooltip("If enabled, ignore the z axis when rotating")]
|
||||
bool m_IgnoreZ;
|
||||
#pragma warning restore 649
|
||||
|
||||
void Awake()
|
||||
{
|
||||
// Default to main camera
|
||||
if (m_FaceTarget == null)
|
||||
if (Camera.main != null)
|
||||
m_FaceTarget = Camera.main.transform;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (m_FaceTarget != null)
|
||||
{
|
||||
var facePosition = m_FaceTarget.position;
|
||||
var forward = facePosition - transform.position;
|
||||
var targetRotation = forward.sqrMagnitude > float.Epsilon ? Quaternion.LookRotation(forward, Vector3.up) : Quaternion.identity;
|
||||
targetRotation *= Quaternion.Euler(m_RotationOffset);
|
||||
if (m_IgnoreX || m_IgnoreY || m_IgnoreZ)
|
||||
{
|
||||
var targetEuler = targetRotation.eulerAngles;
|
||||
var currentEuler = transform.rotation.eulerAngles;
|
||||
targetRotation = Quaternion.Euler
|
||||
(
|
||||
m_IgnoreX ? currentEuler.x : targetEuler.x,
|
||||
m_IgnoreY ? currentEuler.y : targetEuler.y,
|
||||
m_IgnoreZ ? currentEuler.z : targetEuler.z
|
||||
);
|
||||
}
|
||||
|
||||
var ease = 1f - Mathf.Exp(-m_TurnToFaceSpeed * Time.unscaledDeltaTime);
|
||||
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, ease);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fbbaac3589451e3488cba166692bc4ba
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
90
Assets/XRI_Examples/VRWalkthrough/Scripts/UI/ZoneScale.cs
Normal file
90
Assets/XRI_Examples/VRWalkthrough/Scripts/UI/ZoneScale.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
namespace UnityEngine.XR.Content.UI.Layout
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that controls the scale of the GameObject based on the distance from the main camera.
|
||||
/// The distance is divided into zones, and the transform scale is smoothly transitioned when the object moves into a different zone.
|
||||
/// The size of the zones are relative to the viewer's scale.
|
||||
/// </summary>
|
||||
public class ZoneScale : MonoBehaviour
|
||||
{
|
||||
const float k_SmoothTime = 0.3f;
|
||||
|
||||
#pragma warning disable 649
|
||||
[SerializeField]
|
||||
bool m_Clamp;
|
||||
|
||||
[SerializeField]
|
||||
float m_ClampMax = 10f;
|
||||
|
||||
[SerializeField]
|
||||
float m_ClampMin = 1f;
|
||||
|
||||
[SerializeField]
|
||||
float m_ZoneSize = 0.2f;
|
||||
|
||||
[SerializeField]
|
||||
float m_DefaultScale = 1f;
|
||||
#pragma warning restore 649
|
||||
|
||||
float m_YVelocity;
|
||||
float m_LastScale = 1.0f;
|
||||
bool m_Snap;
|
||||
Transform m_MainCameraTransform;
|
||||
int m_CurrentZone;
|
||||
|
||||
//IProvidesViewerScale IFunctionalitySubscriber<IProvidesViewerScale>.provider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Skip the transition interpolation to the target scale immediately
|
||||
/// </summary>
|
||||
public void Snap()
|
||||
{
|
||||
m_Snap = true;
|
||||
SetScaleForCurrentDistance();
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
var mainCamera = Camera.main;
|
||||
if (mainCamera != null)
|
||||
m_MainCameraTransform = mainCamera.transform;
|
||||
|
||||
Snap();
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
SetScaleForCurrentDistance();
|
||||
}
|
||||
|
||||
void SetScaleForCurrentDistance()
|
||||
{
|
||||
if (m_MainCameraTransform == null)
|
||||
return;
|
||||
|
||||
var cameraPosition = m_MainCameraTransform.position;
|
||||
var deltaToCamera = cameraPosition - transform.position;
|
||||
var adjustedDistance = deltaToCamera.magnitude;
|
||||
var scaledZoneSize = m_ZoneSize * Camera.main.transform.lossyScale.x;
|
||||
var zone = Mathf.CeilToInt(adjustedDistance / scaledZoneSize);
|
||||
var bufferSize = scaledZoneSize * 0.5f;
|
||||
if (adjustedDistance > m_CurrentZone * scaledZoneSize + bufferSize ||
|
||||
adjustedDistance < m_CurrentZone * scaledZoneSize - bufferSize)
|
||||
{
|
||||
m_CurrentZone = zone;
|
||||
}
|
||||
|
||||
var targetScale = m_CurrentZone * scaledZoneSize;
|
||||
var newScale = m_Snap ? targetScale : Mathf.SmoothDamp(m_LastScale, targetScale, ref m_YVelocity, k_SmoothTime);
|
||||
|
||||
if (m_Snap)
|
||||
m_Snap = false;
|
||||
|
||||
if (m_Clamp)
|
||||
newScale = Mathf.Clamp(newScale, m_ClampMin, m_ClampMax);
|
||||
|
||||
transform.localScale = Vector3.one * (newScale * m_DefaultScale);
|
||||
m_LastScale = newScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f3116b93585e947a59ce152e3e69dba8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
147
Assets/XRI_Examples/VRWalkthrough/Scripts/Walkthrough.cs
Normal file
147
Assets/XRI_Examples/VRWalkthrough/Scripts/Walkthrough.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEngine.XR.Content.Walkthrough
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a walkthrough - a series of steps gated by triggers
|
||||
/// </summary>
|
||||
public class Walkthrough : MonoBehaviour
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[SerializeField]
|
||||
[Tooltip("The name of this walkthrough - for reference by UI")]
|
||||
string m_WalkthroughName;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("All of the steps this walkthrough requires, in order")]
|
||||
WalkthroughStep[] m_Steps;
|
||||
|
||||
[SerializeField]
|
||||
GameObject m_Waypoint;
|
||||
|
||||
[SerializeField]
|
||||
GameObject m_WaypointLink;
|
||||
|
||||
[SerializeField]
|
||||
bool m_LoopOnComplete = false;
|
||||
#pragma warning restore 649
|
||||
|
||||
/// <summary>
|
||||
/// The name of the walkthrough experience
|
||||
/// </summary>
|
||||
public string walkthroughName => m_WalkthroughName;
|
||||
|
||||
/// <summary>
|
||||
/// All of the steps this walkthrough requires, in order
|
||||
/// </summary>
|
||||
public WalkthroughStep[] steps => m_Steps;
|
||||
|
||||
/// <summary>
|
||||
/// The currently active step of the walkthrough
|
||||
/// </summary>
|
||||
public int currentStep { get; private set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Event that is raised whenever the state of the walkthrough has changed.
|
||||
/// </summary>
|
||||
public Action walkthroughChangedCallback;
|
||||
|
||||
/// <summary>
|
||||
/// Shifts to another step of the walkthrough
|
||||
/// </summary>
|
||||
/// <param name="stepIndex">The step to make active</param>
|
||||
/// <param name="autoProgressIfComplete">If true, allows for skipping to the subsequent step if the current one is already complete.</param>
|
||||
public void SkipToStep(int stepIndex, bool autoProgressIfComplete)
|
||||
{
|
||||
// Ignore invalid indices and no-ops
|
||||
if (stepIndex < 0 || stepIndex >= m_Steps.Length || stepIndex == currentStep)
|
||||
return;
|
||||
|
||||
// If any steps between our current step and the next are incomplete and block progression, we do not allow skipping to occur. This prevents
|
||||
// problems like skipping to a step where relocalization has not yet occurred.
|
||||
if (stepIndex > currentStep)
|
||||
{
|
||||
for (var testStepIndex = currentStep; testStepIndex < stepIndex; testStepIndex++)
|
||||
{
|
||||
var testStep = m_Steps[testStepIndex];
|
||||
if (!testStep.canSkip)
|
||||
{
|
||||
Debug.LogWarning($"Can't skip past incomplete step {testStep.name}");
|
||||
walkthroughChangedCallback?.Invoke();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If a valid step is already being displayed, set it back to inactive now
|
||||
if (currentStep >= 0 && currentStep < m_Steps.Length)
|
||||
m_Steps[currentStep].CancelStep();
|
||||
|
||||
currentStep = stepIndex;
|
||||
m_Steps[currentStep].StartStep(OnStepComplete, autoProgressIfComplete);
|
||||
|
||||
walkthroughChangedCallback?.Invoke();
|
||||
}
|
||||
|
||||
public void SkipToStep(int stepIndex)
|
||||
{
|
||||
SkipToStep(stepIndex, false);
|
||||
}
|
||||
|
||||
void Awake()
|
||||
{
|
||||
int stepIndex = 1;
|
||||
// We ensure each walkthrough step is ready to work (as we can't ensure components are waking in a determined order), then start the first step
|
||||
foreach (var step in m_Steps)
|
||||
{
|
||||
step.Initialize();
|
||||
var waypoint = Instantiate(m_Waypoint, step.gameObject.transform);
|
||||
waypoint.transform.localPosition = Vector3.zero;
|
||||
waypoint.transform.rotation = Quaternion.identity;
|
||||
var waypointText = waypoint.GetComponentInChildren<TMPro.TMP_Text>();
|
||||
waypointText.text = stepIndex.ToString();
|
||||
step.waypoint = waypoint;
|
||||
|
||||
if (stepIndex > 1)
|
||||
{
|
||||
var link = Instantiate(m_WaypointLink, step.gameObject.transform);
|
||||
link.transform.localPosition = Vector3.zero;
|
||||
link.transform.rotation = Quaternion.identity;
|
||||
var linkCurve = link.GetComponentInChildren<WaypointCurve>();
|
||||
linkCurve.start = m_Steps[stepIndex - 2].gameObject.transform.position;
|
||||
linkCurve.end = m_Steps[stepIndex - 1].gameObject.transform.position;
|
||||
step.link = link;
|
||||
}
|
||||
|
||||
stepIndex++;
|
||||
}
|
||||
if (m_Steps != null && m_Steps.Length > 0)
|
||||
m_Steps[currentStep].StartStep(OnStepComplete);
|
||||
|
||||
walkthroughChangedCallback?.Invoke();
|
||||
}
|
||||
|
||||
void OnStepComplete(bool autoProgress)
|
||||
{
|
||||
// We still call the changed callback even if we are not auto-progressing, as some UI may want to update labels or controls
|
||||
if (!autoProgress)
|
||||
{
|
||||
walkthroughChangedCallback?.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
// If we are auto-progressing, increment the step index and start the process again
|
||||
currentStep++;
|
||||
|
||||
if (m_LoopOnComplete && currentStep >= m_Steps.Length)
|
||||
currentStep = 0;
|
||||
|
||||
if (m_Steps == null || currentStep >= m_Steps.Length)
|
||||
return;
|
||||
|
||||
m_Steps[currentStep].StartStep(OnStepComplete);
|
||||
|
||||
walkthroughChangedCallback?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 97a681864d9ec3e4a84166c398ef5d45
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
288
Assets/XRI_Examples/VRWalkthrough/Scripts/WalkthroughStep.cs
Normal file
288
Assets/XRI_Examples/VRWalkthrough/Scripts/WalkthroughStep.cs
Normal file
@@ -0,0 +1,288 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.XR.Interaction.Toolkit;
|
||||
|
||||
namespace UnityEngine.XR.Content.Walkthrough
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information needed to process one step of a walkthrough.
|
||||
/// </summary>
|
||||
public class WalkthroughStep : MonoBehaviour
|
||||
{
|
||||
/// Local method use only -- created here to reduce garbage collection. Collections must be cleared before use
|
||||
static readonly List<WalkthroughTrigger> s_TriggersToRemove = new List<WalkthroughTrigger>();
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Camera target to reposition user.")]
|
||||
GameObject m_CameraTarget;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("The Teleportation Provider used to reposition the user. Usually a component on the XR Origin.")]
|
||||
TeleportationProvider m_TeleportationProvider;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Optional audio source for voiceover")]
|
||||
AudioSource m_AudioSource;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Objects to enable when this step is active.")]
|
||||
List<GameObject> m_Visuals = new List<GameObject>();
|
||||
|
||||
#pragma warning disable 649
|
||||
[SerializeField]
|
||||
[Tooltip("Actions to call when the step starts.")]
|
||||
UnityEvent m_OnStepBegin;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Actions to call when the step completes.")]
|
||||
UnityEvent m_OnStepComplete;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("The purpose of this step.")]
|
||||
string m_Description;
|
||||
#pragma warning restore 649
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("If true, this step cannot be skipped until completed at least once.")]
|
||||
bool m_BlockUntilComplete;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("If true, this step will automatically progress when complete - unless explicitly skipped to.")]
|
||||
bool m_AutoProgressOnComplete = true;
|
||||
|
||||
bool m_Started;
|
||||
bool m_AutoProgressEnabled = true;
|
||||
bool m_StepInvoked;
|
||||
|
||||
Action<bool> m_OnComplete;
|
||||
GameObject m_Waypoint;
|
||||
GameObject m_Link;
|
||||
|
||||
List<WalkthroughTrigger> m_Triggers = new List<WalkthroughTrigger>();
|
||||
List<WalkthroughTrigger> m_RemainingTriggers = new List<WalkthroughTrigger>();
|
||||
|
||||
/// <summary>
|
||||
/// The purpose of this step. Appends a (Complete) if complete and normally has triggers.
|
||||
/// </summary>
|
||||
public string description => $"{m_Description}{(completed && m_Triggers.Count > 0 ? " (Complete)" : "") }";
|
||||
|
||||
|
||||
public GameObject waypoint
|
||||
{
|
||||
get => m_Waypoint;
|
||||
set => m_Waypoint = value;
|
||||
}
|
||||
|
||||
public GameObject link
|
||||
{
|
||||
get => m_Link;
|
||||
set => m_Link = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the step visuals are hidden until active and that all triggers are accounted for
|
||||
/// </summary>
|
||||
public void Initialize()
|
||||
{
|
||||
if (!m_Started)
|
||||
SetVisualsState(false);
|
||||
|
||||
GetComponents(m_Triggers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this step does not currently have any triggers remaining to fire
|
||||
/// </summary>
|
||||
public bool canProgress => (!m_BlockUntilComplete || (m_RemainingTriggers.Count == 0));
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this step does not block, or has been completed at least once.
|
||||
/// </summary>
|
||||
public bool canSkip => (!m_BlockUntilComplete || completed);
|
||||
|
||||
/// <summary>
|
||||
/// True if this step's triggers have been activated at least once
|
||||
/// </summary>
|
||||
public bool completed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Makes this step and its triggers the active focus of a walkthrough
|
||||
/// </summary>
|
||||
/// <param name="onComplete">Callback to fire when this step's triggers are complete</param>
|
||||
/// <param name="allowAutoProgress">If this step is allow to auto-progress during this activation</param>
|
||||
public void StartStep(Action<bool> onComplete, bool allowAutoProgress = true)
|
||||
{
|
||||
if (m_Started)
|
||||
return;
|
||||
|
||||
// Autoprogression is enabled only if the step AND walkthrough allow it
|
||||
m_AutoProgressEnabled = allowAutoProgress && m_AutoProgressOnComplete;
|
||||
|
||||
SetVisualsState(true);
|
||||
SetAudioSource(true);
|
||||
if (m_Waypoint != null)
|
||||
{
|
||||
m_Waypoint.SetActive(false);
|
||||
}
|
||||
|
||||
if (m_CameraTarget != null && m_TeleportationProvider != null)
|
||||
{
|
||||
SetCameraPosition();
|
||||
}
|
||||
|
||||
m_OnComplete = onComplete;
|
||||
|
||||
m_Started = true;
|
||||
|
||||
if (m_Triggers.Count == 0 && m_AutoProgressEnabled)
|
||||
{
|
||||
CompleteStep();
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var currentTrigger in m_Triggers)
|
||||
{
|
||||
if (currentTrigger.ResetTrigger())
|
||||
m_RemainingTriggers.Add(currentTrigger);
|
||||
}
|
||||
|
||||
if (m_RemainingTriggers.Count == 0)
|
||||
{
|
||||
CompleteStep();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_RemainingTriggers.Count > 0)
|
||||
m_AutoProgressEnabled = m_AutoProgressOnComplete;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends this step being the focus of the current walkthrough
|
||||
/// </summary>
|
||||
public void CancelStep()
|
||||
{
|
||||
SetVisualsState(false);
|
||||
SetAudioSource(false);
|
||||
if (m_Waypoint != null)
|
||||
{
|
||||
m_Waypoint.SetActive(true);
|
||||
}
|
||||
|
||||
if (!m_Started)
|
||||
return;
|
||||
|
||||
m_OnComplete = null;
|
||||
m_Started = false;
|
||||
|
||||
m_RemainingTriggers.Clear();
|
||||
}
|
||||
|
||||
void CompleteStep()
|
||||
{
|
||||
if (!m_Started)
|
||||
return;
|
||||
|
||||
completed = true;
|
||||
|
||||
m_OnComplete?.Invoke(m_AutoProgressEnabled);
|
||||
m_OnComplete = null;
|
||||
m_Started = false;
|
||||
|
||||
// We disable visuals if the the next step is being activated
|
||||
if (m_AutoProgressEnabled)
|
||||
{
|
||||
SetVisualsState(false);
|
||||
SetAudioSource(false);
|
||||
if (m_Waypoint != null)
|
||||
{
|
||||
m_Waypoint.SetActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
m_RemainingTriggers.Clear();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
// If this step is running, check remaining triggers. Any triggers that are now met get removed.
|
||||
// If there are no triggers left, then the step is complete.
|
||||
if (!m_Started)
|
||||
return;
|
||||
|
||||
if (m_RemainingTriggers.Count == 0)
|
||||
return;
|
||||
|
||||
s_TriggersToRemove.Clear();
|
||||
foreach (var currentTrigger in m_RemainingTriggers)
|
||||
{
|
||||
if (currentTrigger.Check())
|
||||
s_TriggersToRemove.Add(currentTrigger);
|
||||
}
|
||||
|
||||
foreach (var toRemove in s_TriggersToRemove)
|
||||
{
|
||||
m_RemainingTriggers.Remove(toRemove);
|
||||
}
|
||||
s_TriggersToRemove.Clear();
|
||||
|
||||
if (m_RemainingTriggers.Count == 0)
|
||||
{
|
||||
CompleteStep();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void SetVisualsState(bool enabled)
|
||||
{
|
||||
if (m_Visuals != null)
|
||||
{
|
||||
foreach (var currentVisual in m_Visuals)
|
||||
{
|
||||
if (currentVisual != null)
|
||||
currentVisual.SetActive(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_StepInvoked == enabled)
|
||||
return;
|
||||
|
||||
m_StepInvoked = enabled;
|
||||
|
||||
if (enabled && m_OnStepBegin != null)
|
||||
m_OnStepBegin.Invoke();
|
||||
|
||||
if (!enabled && m_OnStepComplete != null)
|
||||
m_OnStepComplete.Invoke();
|
||||
}
|
||||
|
||||
void SetAudioSource(bool enabled)
|
||||
{
|
||||
if (m_AudioSource != null)
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
m_AudioSource.Play();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_AudioSource.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetCameraPosition()
|
||||
{
|
||||
TeleportRequest request = new TeleportRequest()
|
||||
{
|
||||
requestTime = Time.time,
|
||||
matchOrientation = MatchOrientation.TargetUpAndForward,
|
||||
|
||||
destinationPosition = m_CameraTarget.transform.position,
|
||||
destinationRotation = m_CameraTarget.transform.rotation
|
||||
};
|
||||
|
||||
m_TeleportationProvider.QueueTeleportRequest(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c2b422177dd3c8a4eb44616d56d8e45b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cfb30d95353445d468ef3170b5dc0812
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UnityEngine.XR.Content.Walkthrough
|
||||
{
|
||||
/// <summary>
|
||||
/// Trigger that, when active, waits for a UI button to be pressed
|
||||
/// </summary>
|
||||
internal class ButtonPressTrigger : WalkthroughTrigger
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[SerializeField]
|
||||
[Tooltip("The UI button that when pressed, will allow this trigger to pass.")]
|
||||
Button m_ButtonToPress;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Allow pressing of the button switch the step of the tutorial")]
|
||||
bool m_SwitchContext = true;
|
||||
|
||||
#pragma warning restore 649
|
||||
bool m_Triggered = false;
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (m_ButtonToPress == null)
|
||||
return;
|
||||
|
||||
m_ButtonToPress.onClick.RemoveListener(ButtonPressHandler);
|
||||
m_ButtonToPress.onClick.AddListener(ButtonPressHandler);
|
||||
}
|
||||
|
||||
public override bool ResetTrigger()
|
||||
{
|
||||
m_Triggered = false;
|
||||
if (m_ButtonToPress == null)
|
||||
return false;
|
||||
|
||||
m_ButtonToPress.onClick.RemoveListener(ButtonPressHandler);
|
||||
m_ButtonToPress.onClick.AddListener(ButtonPressHandler);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Check()
|
||||
{
|
||||
return m_Triggered;
|
||||
}
|
||||
|
||||
void ButtonPressHandler()
|
||||
{
|
||||
// Attempt to switch to this step if this button is not part of the current step
|
||||
if (m_SwitchContext)
|
||||
{
|
||||
var parent = GetComponentInParent<WalkthroughStep>();
|
||||
var walkthrough = GetComponentInParent<Walkthrough>();
|
||||
if (parent != null && walkthrough != null)
|
||||
{
|
||||
var steps = walkthrough.steps;
|
||||
var stepIndex = Array.IndexOf(steps, parent);
|
||||
if (stepIndex != walkthrough.currentStep)
|
||||
walkthrough.SkipToStep(stepIndex);
|
||||
}
|
||||
}
|
||||
|
||||
m_Triggered = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 489565b6c95bfbc49b1681ab52f265ea
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace UnityEngine.XR.Content.Walkthrough
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for all triggers used by walkthrough steps.
|
||||
/// </summary>
|
||||
abstract public class WalkthroughTrigger : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempts to return a trigger to a state where it can be activated again
|
||||
/// </summary>
|
||||
/// <returns>False if this trigger cannot be reset or would automatically fire</returns>
|
||||
public abstract bool ResetTrigger();
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this trigger's pass/fail condition is active
|
||||
/// </summary>
|
||||
/// <returns>True if this trigger is no longer blocking the current walkthrough step</returns>
|
||||
public abstract bool Check();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f562431b9a534d4c8f265289a8b51ca
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
136
Assets/XRI_Examples/VRWalkthrough/Scripts/WaypointCurve.cs
Normal file
136
Assets/XRI_Examples/VRWalkthrough/Scripts/WaypointCurve.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
namespace UnityEngine.XR.Content.Walkthrough
|
||||
{
|
||||
/// <summary>
|
||||
/// Manipulates a line renderer to draw a curved path between two locations
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(LineRenderer))]
|
||||
public class WaypointCurve : MonoBehaviour
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[SerializeField]
|
||||
[Tooltip("Where the curved path should begin.")]
|
||||
Transform m_StartPoint;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Where the curved path should complete.")]
|
||||
Transform m_EndPoint;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Bends the first control point of the path.")]
|
||||
float m_CurveFactorStart = 1.0f;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Bends the last control point of the path.")]
|
||||
float m_CurveFactorEnd = 1.0f;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Enable to make the path animate colors.")]
|
||||
bool m_Animate = false;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("How quickly to animate the path.")]
|
||||
float m_AnimSpeed = 0.25f;
|
||||
#pragma warning restore 649
|
||||
|
||||
Vector3[] m_ControlPoints = new Vector3[4];
|
||||
LineRenderer m_LineRenderer;
|
||||
int m_CurveCount = 0;
|
||||
int m_LayerOrder = 0;
|
||||
int m_SegmentCount = 50;
|
||||
float m_Time = 0.0f;
|
||||
|
||||
public Vector3 start
|
||||
{
|
||||
get => m_StartPoint.position;
|
||||
set => m_StartPoint.position = value;
|
||||
}
|
||||
|
||||
public Vector3 end
|
||||
{
|
||||
get => m_EndPoint.position;
|
||||
set => m_EndPoint.position = value;
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (!m_LineRenderer)
|
||||
{
|
||||
m_LineRenderer = GetComponent<LineRenderer>();
|
||||
}
|
||||
m_LineRenderer.sortingLayerID = m_LayerOrder;
|
||||
m_CurveCount = (int)4 / 3;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
DrawCurve();
|
||||
|
||||
if (m_Animate) { AnimateCurve(); }
|
||||
}
|
||||
|
||||
void DrawCurve()
|
||||
{
|
||||
var dist = Mathf.Clamp(Vector3.Distance(m_StartPoint.position, m_EndPoint.position), 0f, 1f);
|
||||
m_ControlPoints[0] = m_StartPoint.position;
|
||||
m_ControlPoints[1] = m_StartPoint.position + m_StartPoint.right * (dist * m_CurveFactorStart);
|
||||
m_ControlPoints[2] = m_EndPoint.position - m_EndPoint.right * (dist * m_CurveFactorEnd);
|
||||
m_ControlPoints[3] = m_EndPoint.position;
|
||||
|
||||
for (int j = 0; j < m_CurveCount; j++)
|
||||
{
|
||||
for (int i = 1; i <= m_SegmentCount; i++)
|
||||
{
|
||||
float t = i / (float)m_SegmentCount;
|
||||
int nodeIndex = j * 3;
|
||||
Vector3 pixel = CalculateCubicBezierPoint(t, m_ControlPoints[nodeIndex], m_ControlPoints[nodeIndex + 1], m_ControlPoints[nodeIndex + 2], m_ControlPoints[nodeIndex + 3]);
|
||||
m_LineRenderer.positionCount = (((j * m_SegmentCount) + i));
|
||||
m_LineRenderer.SetPosition((j * m_SegmentCount) + (i - 1), pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 CalculateCubicBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
|
||||
{
|
||||
float u = 1 - t;
|
||||
float tt = t * t;
|
||||
float uu = u * u;
|
||||
float uuu = uu * u;
|
||||
float ttt = tt * t;
|
||||
|
||||
Vector3 p = uuu * p0;
|
||||
p += 3 * uu * t * p1;
|
||||
p += 3 * u * tt * p2;
|
||||
p += ttt * p3;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void AnimateCurve()
|
||||
{
|
||||
Gradient newGrad = new Gradient();
|
||||
|
||||
GradientColorKey[] colorKeys = new GradientColorKey[1];
|
||||
GradientAlphaKey[] alphaKeys = new GradientAlphaKey[2];
|
||||
|
||||
GradientColorKey colorKey = new GradientColorKey(new Color(0.1254902f, 0.5882353f, 0.9529412f), 0f);
|
||||
colorKeys[0] = colorKey;
|
||||
|
||||
GradientAlphaKey alphaKeyStart1 = new GradientAlphaKey(.25f, m_Time);
|
||||
GradientAlphaKey alphaKeyStart = new GradientAlphaKey(.25f, m_Time);
|
||||
GradientAlphaKey alphaKeyEnd = new GradientAlphaKey(1f, 1f);
|
||||
alphaKeys[0] = alphaKeyStart;
|
||||
alphaKeys[1] = alphaKeyEnd;
|
||||
|
||||
newGrad.SetKeys(colorKeys, alphaKeys);
|
||||
newGrad.mode = GradientMode.Blend;
|
||||
|
||||
m_LineRenderer.colorGradient = newGrad;
|
||||
m_Time += (Time.deltaTime * m_AnimSpeed);
|
||||
|
||||
if (m_Time >= 1f)
|
||||
{
|
||||
m_Time = 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ae36f98bfe7bbde4ba8f0bd443e41943
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user