AR Design
UBC EML collab with UBC SALA - visualizing IoT data in AR
Duplicator.cs
Go to the documentation of this file.
1 //
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 // Licensed under the MIT License. See LICENSE in the project root for license information.
4 //
5 
6 using System;
7 using System.Collections;
8 using UnityEngine;
9 
10 namespace HoloToolkit.Unity.UX
11 {
15  public class Duplicator : MonoBehaviour
16  {
17  public enum StateEnum
18  {
19  Empty,
20  Idle,
21  Activated,
22  Duplicating
23  }
24 
25  public enum ActivateModeEnum
26  {
27  Auto,
28  Manual,
29  }
30 
31  public Action OnTargetIdle;
32  public Action OnTargetActive;
33  public Action OnTargetEmpty;
34  public Action OnTargetDuplicateStart;
35  public Action OnTargetDuplicateEnd;
36 
37  public StateEnum State {
38  get {
39  return state;
40  }
41  protected set {
42  state = value;
43  }
44  }
45 
46  public GameObject Target {
47  get {
48  if (transform.childCount > 0)
49  {
50  return transform.GetChild(0).gameObject;
51  }
52  return null;
53  }
54  }
55 
56  [Header("Idle & Activation settings")]
57  [SerializeField]
58  protected ActivateModeEnum activateMode = ActivateModeEnum.Auto;
59  [SerializeField]
60  protected float autoActivateRadius = 0.05f;
61  [SerializeField]
62  protected float removeRadius = 0.25f;
63  [SerializeField]
64  protected float restorePosSpeed = 1f;
65  [SerializeField]
66  protected float activeTimeout = 0.5f;
67  [SerializeField]
68  protected AnimationCurve restoreCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f);
69 
70  [Header("Duplication settings")]
71  [SerializeField]
72  private bool limitDuplicates = false;
73  [SerializeField]
74  protected int maxDuplicates = 10;
75  [SerializeField]
76  protected float duplicateSpeed = 0.5f;
77  [SerializeField]
78  protected AnimationCurve duplicateGrowCurve = AnimationCurve.EaseInOut(0f, 0.001f, 1f, 1f);
79 
80  [Header("Current state")]
81  [SerializeField]
82  protected StateEnum state = StateEnum.Idle;
83 
84  protected Vector3 targetPos;
85  protected Vector3 targetScale;
86  protected Quaternion targetRot;
87  protected int numDuplicates;
88 
89  protected void OnEnable() {
90  StartCoroutine(UpdateTarget());
91  }
92 
93  protected virtual void StoreTarget() {
94  if (Target == null) {
95  Debug.LogError("No target found in duplicator.");
96  return;
97  }
98 
99  targetScale = Target.transform.localScale;
100  targetPos = Target.transform.localPosition;
101  targetRot = Target.transform.localRotation;
102  }
103 
104  protected virtual void RestoreTarget(float normalizedTime) {
105  normalizedTime = restoreCurve.Evaluate(normalizedTime);
106  Target.transform.localPosition = Vector3.Lerp(Target.transform.localPosition, targetPos, normalizedTime);
107  Target.transform.localRotation = Quaternion.Lerp(Target.transform.localRotation, targetRot, normalizedTime);
108  }
109 
110  protected virtual IEnumerator UpdateTarget() {
111 
112  while (Target == null) {
113  // Wait for a target to be added
114  yield return null;
115  }
116  // Store the target's position
117  StoreTarget();
118 
119  state = StateEnum.Idle;
120  float normalizedTime = 0f;
121 
122  while (isActiveAndEnabled) {
123 
124  float idleStartTime = Time.unscaledTime;
125  while (state == StateEnum.Idle) {
126  // While we're idle, move the target back into position
127  if (Target.transform.hasChanged) {
128  Target.transform.hasChanged = false;
129  idleStartTime = Time.unscaledTime;
130  // If our activate mode is auto, activate once the target is moved
131  if (activateMode == ActivateModeEnum.Auto && Vector3.Distance(Target.transform.position, transform.TransformPoint(targetPos)) > autoActivateRadius) {
132  state = StateEnum.Activated;
133 
134  if (OnTargetActive != null)
135  OnTargetActive();
136  }
137  } else {
138  normalizedTime = (Time.unscaledTime - idleStartTime) / restorePosSpeed;
139  RestoreTarget(normalizedTime);
140  }
141  yield return null;
142  }
143 
144  float lastTimeChanged = Time.unscaledTime;
145 
146  // While the target is activated, check to see if it's being moved
147  while (state == StateEnum.Activated) {
148  // If the target has moved, check to see if it has exited the radius
149  if (Target.transform.hasChanged) {
150  lastTimeChanged = Time.unscaledTime;
151  Target.transform.hasChanged = false;
152  // If the target has exited the radius, duplicate it
153  if (Vector3.Distance (Target.transform.position, transform.TransformPoint(targetPos)) > removeRadius) {
154  state = StateEnum.Duplicating;
155  }
156  // If the target hasn't moved for the timeout period, go back to idle
157  } else if (Time.unscaledTime > (lastTimeChanged + activeTimeout)) {
158  // Move it back to the target pos
159  float timedOutTime = Time.unscaledTime;
160  while (Time.unscaledTime < timedOutTime + restorePosSpeed) {
161  normalizedTime = (Time.unscaledTime - timedOutTime) / restorePosSpeed;
162  RestoreTarget(normalizedTime);
163  yield return null;
164  }
165  state = StateEnum.Idle;
166 
167  if(OnTargetIdle != null)
168  OnTargetIdle();
169  }
170  yield return null;
171  }
172 
173  if (state == StateEnum.Duplicating) {
174  // Unparent the object - at this point it's not our problem
175  GameObject originalTarget = Target;
176  originalTarget.transform.parent = null;
177  if (limitDuplicates && numDuplicates >= maxDuplicates) {
178  // No more items to duplicate
179  state = StateEnum.Empty;
180 
181  if (OnTargetEmpty != null)
182  OnTargetEmpty();
183 
184  } else {
185  // Duplicating!
186  if (OnTargetDuplicateStart != null)
187  OnTargetDuplicateStart();
188 
189  GameObject.Instantiate(originalTarget, transform);
190  Target.name = originalTarget.name;
191  Target.transform.localPosition = targetPos;
192  Target.transform.localRotation = targetRot;
193  float duplicateStartTime = Time.unscaledTime;
194  while (Time.unscaledTime < duplicateStartTime + duplicateSpeed) {
195  normalizedTime = (Time.unscaledTime - duplicateStartTime) / duplicateSpeed;
196  Target.transform.localScale = targetScale * duplicateGrowCurve.Evaluate(normalizedTime);
197  yield return null;
198  }
199  Target.transform.localScale = targetScale;
200  numDuplicates++;
201 
202  if (OnTargetDuplicateEnd != null)
203  OnTargetDuplicateEnd();
204 
205  state = StateEnum.Idle;
206 
207  if (OnTargetIdle != null)
208  OnTargetIdle();
209  }
210  }
211  yield return null;
212  }
213  }
214 
215  private void OnDrawGizmos() {
216 
217  switch (state) {
218  case StateEnum.Idle:
219  Gizmos.color = Color.cyan;
220  Gizmos.DrawWireSphere(transform.TransformPoint(targetPos), autoActivateRadius);
221  break;
222 
223  case StateEnum.Activated:
224  Gizmos.color = Color.magenta;
225  Gizmos.DrawWireSphere(transform.TransformPoint(targetPos), removeRadius);
226  break;
227 
228  case StateEnum.Duplicating:
229  break;
230  }
231 
232  if (Application.isPlaying) {
233  return;
234  }
235 
236  if (Target != null) {
237  StoreTarget();
238  }
239  }
240  }
241 }
virtual void RestoreTarget(float normalizedTime)
Definition: Duplicator.cs:104
Duplicates target object of the Bounding Box.
Definition: Duplicator.cs:15
virtual IEnumerator UpdateTarget()
Definition: Duplicator.cs:110