IMHOTEP Framework
 All Classes Namespaces Functions Variables Enumerations Enumerator Properties Pages
TeleportArc.cs
1 //======= Copyright (c) Valve Corporation, All rights reserved. ===============
2 //
3 // Purpose: Displays the arc lines for teleporting and does the traces
4 //
5 //=============================================================================
6 
7 using UnityEngine;
8 
9 namespace Valve.VR.InteractionSystem
10 {
11  //-------------------------------------------------------------------------
12  public class TeleportArc : MonoBehaviour
13  {
14  public int segmentCount = 60;
15  public float thickness = 0.01f;
16 
17  [Tooltip( "The amount of time in seconds to predict the motion of the projectile." )]
18  public float arcDuration = 3.0f;
19 
20  [Tooltip( "The amount of time in seconds between each segment of the projectile." )]
21  public float segmentBreak = 0.025f;
22 
23  [Tooltip( "The speed at which the line segments of the arc move." )]
24  public float arcSpeed = 0.2f;
25 
26  public Material material;
27 
28  [HideInInspector]
29  public int traceLayerMask = 0;
30 
31  //Private data
32  private LineRenderer[] lineRenderers;
33  private float arcTimeOffset = 0.0f;
34  private float prevThickness = 0.0f;
35  private int prevSegmentCount = 0;
36  private bool showArc = true;
37  private Vector3 startPos;
38  private Vector3 projectileVelocity;
39  private bool useGravity = true;
40  private Transform arcObjectsTransfrom;
41  private bool arcInvalid = false;
42 
43 
44  //-------------------------------------------------
45  void Start()
46  {
47  arcTimeOffset = Time.time;
48  }
49 
50 
51  //-------------------------------------------------
52  void Update()
53  {
54  if ( thickness != prevThickness || segmentCount != prevSegmentCount )
55  {
56  CreateLineRendererObjects();
57  prevThickness = thickness;
58  prevSegmentCount = segmentCount;
59  }
60  }
61 
62 
63  //-------------------------------------------------
64  private void CreateLineRendererObjects()
65  {
66  //Destroy any existing line renderer objects
67  if ( arcObjectsTransfrom != null )
68  {
69  Destroy( arcObjectsTransfrom.gameObject );
70  }
71 
72  GameObject arcObjectsParent = new GameObject( "ArcObjects" );
73  arcObjectsTransfrom = arcObjectsParent.transform;
74  arcObjectsTransfrom.SetParent( this.transform );
75 
76  //Create new line renderer objects
77  lineRenderers = new LineRenderer[segmentCount];
78  for ( int i = 0; i < segmentCount; ++i )
79  {
80  GameObject newObject = new GameObject( "LineRenderer_" + i );
81  newObject.transform.SetParent( arcObjectsTransfrom );
82 
83  lineRenderers[i] = newObject.AddComponent<LineRenderer>();
84 
85  lineRenderers[i].receiveShadows = false;
86  lineRenderers[i].reflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.Off;
87  lineRenderers[i].lightProbeUsage = UnityEngine.Rendering.LightProbeUsage.Off;
88  lineRenderers[i].shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
89  lineRenderers[i].material = material;
90 #if (UNITY_5_4)
91  lineRenderers[i].SetWidth( thickness, thickness );
92 #else
93  lineRenderers[i].startWidth = thickness;
94  lineRenderers[i].endWidth = thickness;
95 #endif
96  lineRenderers[i].enabled = false;
97  }
98  }
99 
100 
101  //-------------------------------------------------
102  public void SetArcData( Vector3 position, Vector3 velocity, bool gravity, bool pointerAtBadAngle )
103  {
104  startPos = position;
105  projectileVelocity = velocity;
106  useGravity = gravity;
107 
108  if ( arcInvalid && !pointerAtBadAngle )
109  {
110  arcTimeOffset = Time.time;
111  }
112  arcInvalid = pointerAtBadAngle;
113  }
114 
115 
116  //-------------------------------------------------
117  public void Show()
118  {
119  showArc = true;
120  if ( lineRenderers == null )
121  {
122  CreateLineRendererObjects();
123  }
124  }
125 
126 
127  //-------------------------------------------------
128  public void Hide()
129  {
130  //Hide the line segments if they were previously being shown
131  if ( showArc )
132  {
133  HideLineSegments( 0, segmentCount );
134  }
135  showArc = false;
136  }
137 
138 
139  //-------------------------------------------------
140  // Draws each segment of the arc individually
141  //-------------------------------------------------
142  public bool DrawArc( out RaycastHit hitInfo )
143  {
144  float timeStep = arcDuration / segmentCount;
145 
146  float currentTimeOffset = ( Time.time - arcTimeOffset ) * arcSpeed;
147 
148  //Reset the arc time offset when it has gone beyond a segment length
149  if ( currentTimeOffset > ( timeStep + segmentBreak ) )
150  {
151  arcTimeOffset = Time.time;
152  currentTimeOffset = 0.0f;
153  }
154 
155  float segmentStartTime = currentTimeOffset;
156 
157  float arcHitTime = FindProjectileCollision( out hitInfo );
158 
159  if ( arcInvalid )
160  {
161  //Only draw first segment
162  lineRenderers[0].enabled = true;
163  lineRenderers[0].SetPosition( 0, GetArcPositionAtTime( 0.0f ) );
164  lineRenderers[0].SetPosition( 1, GetArcPositionAtTime( arcHitTime < timeStep ? arcHitTime : timeStep ) );
165 
166  HideLineSegments( 1, segmentCount );
167  }
168  else
169  {
170  //Draw the first segment outside the loop if needed
171  int loopStartSegment = 0;
172  if ( segmentStartTime > segmentBreak )
173  {
174  float firstSegmentEndTime = currentTimeOffset - segmentBreak;
175  if ( arcHitTime < firstSegmentEndTime )
176  {
177  firstSegmentEndTime = arcHitTime;
178  }
179  DrawArcSegment( 0, 0.0f, firstSegmentEndTime );
180 
181  loopStartSegment = 1;
182  }
183 
184  bool stopArc = false;
185  int currentSegment = 0;
186  if ( segmentStartTime < arcHitTime )
187  {
188  for ( currentSegment = loopStartSegment; currentSegment < segmentCount; ++currentSegment )
189  {
190  //Clamp the segment end time to the arc duration
191  float segmentEndTime = segmentStartTime + timeStep;
192  if ( segmentEndTime >= arcDuration )
193  {
194  segmentEndTime = arcDuration;
195  stopArc = true;
196  }
197 
198  if ( segmentEndTime >= arcHitTime )
199  {
200  segmentEndTime = arcHitTime;
201  stopArc = true;
202  }
203 
204  DrawArcSegment( currentSegment, segmentStartTime, segmentEndTime );
205 
206  segmentStartTime += timeStep + segmentBreak;
207 
208  //If the previous end time or the next start time is beyond the duration then stop the arc
209  if ( stopArc || segmentStartTime >= arcDuration || segmentStartTime >= arcHitTime )
210  {
211  break;
212  }
213  }
214  }
215  else
216  {
217  currentSegment--;
218  }
219 
220  //Hide the rest of the line segments
221  HideLineSegments( currentSegment + 1, segmentCount );
222  }
223 
224  return arcHitTime != float.MaxValue;
225  }
226 
227 
228  //-------------------------------------------------
229  private void DrawArcSegment( int index, float startTime, float endTime )
230  {
231  lineRenderers[index].enabled = true;
232  lineRenderers[index].SetPosition( 0, GetArcPositionAtTime( startTime ) );
233  lineRenderers[index].SetPosition( 1, GetArcPositionAtTime( endTime ) );
234  }
235 
236 
237  //-------------------------------------------------
238  public void SetColor( Color color )
239  {
240  for ( int i = 0; i < segmentCount; ++i )
241  {
242 #if (UNITY_5_4)
243  lineRenderers[i].SetColors( color, color );
244 #else
245  lineRenderers[i].startColor = color;
246  lineRenderers[i].endColor = color;
247 #endif
248  }
249  }
250 
251 
252  //-------------------------------------------------
253  private float FindProjectileCollision( out RaycastHit hitInfo )
254  {
255  float timeStep = arcDuration / segmentCount;
256  float segmentStartTime = 0.0f;
257 
258  hitInfo = new RaycastHit();
259 
260  Vector3 segmentStartPos = GetArcPositionAtTime( segmentStartTime );
261  for ( int i = 0; i < segmentCount; ++i )
262  {
263  float segmentEndTime = segmentStartTime + timeStep;
264  Vector3 segmentEndPos = GetArcPositionAtTime( segmentEndTime );
265 
266  if ( Physics.Linecast( segmentStartPos, segmentEndPos, out hitInfo, traceLayerMask ) )
267  {
268  if ( hitInfo.collider.GetComponent<IgnoreTeleportTrace>() == null )
269  {
270  Util.DrawCross( hitInfo.point, Color.red, 0.5f );
271  float segmentDistance = Vector3.Distance( segmentStartPos, segmentEndPos );
272  float hitTime = segmentStartTime + ( timeStep * ( hitInfo.distance / segmentDistance ) );
273  return hitTime;
274  }
275  }
276 
277  segmentStartTime = segmentEndTime;
278  segmentStartPos = segmentEndPos;
279  }
280 
281  return float.MaxValue;
282  }
283 
284 
285  //-------------------------------------------------
286  public Vector3 GetArcPositionAtTime( float time )
287  {
288  Vector3 gravity = useGravity ? Physics.gravity : Vector3.zero;
289 
290  Vector3 arcPos = startPos + ( ( projectileVelocity * time ) + ( 0.5f * time * time ) * gravity );
291  return arcPos;
292  }
293 
294 
295  //-------------------------------------------------
296  private void HideLineSegments( int startSegment, int endSegment )
297  {
298  if ( lineRenderers != null )
299  {
300  for ( int i = startSegment; i < endSegment; ++i )
301  {
302  lineRenderers[i].enabled = false;
303  }
304  }
305  }
306  }
307 }