Tanoda
InteractionSlider.cs
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright (C) Ultraleap, Inc. 2011-2020. *
3 * *
4 * Use subject to the terms of the Apache License 2.0 available at *
5 * http://www.apache.org/licenses/LICENSE-2.0, or another agreement *
6 * between Ultraleap and you, your company or other organization. *
7 ******************************************************************************/
8
9using UnityEngine;
11using UnityEngine.Serialization;
13using System;
14
15namespace Leap.Unity.Interaction {
16
22
23 #region Inspector & Properties
24
25 public enum SliderType {
26 Vertical,
27 Horizontal,
28 TwoDimensional
29 }
30
31 [Header("Slider Settings")]
32 public SliderType sliderType = SliderType.Horizontal;
33
34 public bool dispatchSlideValueOnStart = true;
35
36 [Tooltip("Manually specify slider limits even if the slider's parent has a RectTransform.")]
37 [DisableIf("_parentHasRectTransform", isEqualTo: false)]
38 public bool overrideRectLimits = false;
39 [SerializeField, HideInInspector]
40 #pragma warning disable 0414
41 private bool _parentHasRectTransform = false;
42 #pragma warning restore 0414
43
44 [Header("Horizontal Axis")]
46
47 [Tooltip("The minimum and maximum values that the slider reports on the horizontal axis.")]
48 [FormerlySerializedAs("horizontalValueRange")]
49 [SerializeField]
50 private Vector2 _horizontalValueRange = new Vector2(0f, 1f);
51 public float minHorizontalValue {
52 get {
53 return _horizontalValueRange.x;
54 }
55 set {
56 if (value != _horizontalValueRange.x) {
57 _horizontalValueRange.x = value;
59 }
60 }
61 }
62
63 public float maxHorizontalValue {
64 get {
65 return _horizontalValueRange.y;
66 }
67 set {
68 if (value != _horizontalValueRange.y) {
69 _horizontalValueRange.y = value;
71 }
72 }
73 }
74
75 [Tooltip("The minimum and maximum horizontal extents that the slider can slide to in world space.")]
76 [MinMax(-0.5f, 0.5f)]
77 public Vector2 horizontalSlideLimits = new Vector2(-0.05f, 0.05f);
78
79 [Tooltip("The number of discrete quantized notches **beyond the first** that this "
80 + "slider can occupy on the horizontal axis. A value of zero indicates a "
81 + "continuous (non-quantized) slider for this axis.")]
82 [MinValue(0)]
83 public int horizontalSteps = 0;
84
85 [System.Serializable]
86 public class FloatEvent : UnityEvent<float> { }
88 [SerializeField]
89 [FormerlySerializedAs("horizontalSlideEvent")]
90 private FloatEvent _horizontalSlideEvent = new FloatEvent();
91
92 [Header("Vertical Axis")]
94
95 [Tooltip("The minimum and maximum values that the slider reports on the horizontal axis.")]
96 [FormerlySerializedAs("verticalValueRange")]
97 [SerializeField]
98 private Vector2 _verticalValueRange = new Vector2(0f, 1f);
99 public float minVerticalValue {
100 get {
101 return _verticalValueRange.x;
102 }
103 set {
104 if (value != _verticalValueRange.x) {
105 _verticalValueRange.x = value;
107 }
108 }
109 }
110
111 public float maxVerticalValue {
112 get {
113 return _verticalValueRange.y;
114 }
115 set {
116 if (value != _verticalValueRange.y) {
117 _verticalValueRange.y = value;
119 }
120 }
121 }
122
123 [MinMax(-0.5f, 0.5f)]
124 [Tooltip("The minimum and maximum vertical extents that the slider can slide to in world space.")]
125 public Vector2 verticalSlideLimits = new Vector2(0f, 0f);
126
127 [Tooltip("The number of discrete quantized notches **beyond the first** that this "
128 + "slider can occupy on the vertical axis. A value of zero indicates a "
129 + "continuous (non-quantized) slider for this axis.")]
130 [MinValue(0)]
131 public int verticalSteps = 0;
132
134 [SerializeField]
135 [FormerlySerializedAs("verticalSlideEvent")]
136 private FloatEvent _verticalSlideEvent = new FloatEvent();
137
138 public Action<float> HorizontalSlideEvent = (f) => { };
139 public Action<float> VerticalSlideEvent = (f) => { };
140
142 get {
144 }
145 set {
146 if (!_started) {
147 Debug.LogWarning("An object is attempting to access this slider's value before it has been initialized! Initializing now; this could yield unexpected behaviour...", this);
148 Start();
149 }
150
153 physicsPosition = transform.parent.TransformPoint(localPhysicsPosition);
154 rigidbody.position = physicsPosition;
155 }
156 }
157
159 get {
161 }
162 set {
163 if (!_started) {
164 Debug.LogWarning("An object is attempting to access this slider's value before it has been initialized! Initializing now; this could yield unpected behaviour...", this);
165 Start();
166 }
167
170 physicsPosition = transform.parent.TransformPoint(localPhysicsPosition);
171 rigidbody.position = physicsPosition;
172 }
173 }
174
177 get {
178 return Mathf.Lerp(_horizontalValueRange.x, _horizontalValueRange.y, _horizontalSliderPercent);
179 }
180 set {
181 HorizontalSliderPercent = Mathf.InverseLerp(_horizontalValueRange.x, _horizontalValueRange.y, value);
182 }
183 }
184
186 public float VerticalSliderValue {
187 get {
188 return Mathf.Lerp(_verticalValueRange.x, _verticalValueRange.y, _verticalSliderPercent);
189 }
190 set {
191 VerticalSliderPercent = Mathf.InverseLerp(_verticalValueRange.x, _verticalValueRange.y, value);
192 }
193 }
194
195 private void calculateSliderValues() {
196 // Calculate renormalized slider values.
200 }
201
205 }
206 }
207
209 get {
211 }
212 set {
213 var newValue = Mathf.Clamp01(value);
214 if (newValue != _horizontalSliderPercent) {
215 _horizontalSliderPercent = newValue;
217 }
218 }
219 }
220
222 get {
224 }
225 set {
226 var newValue = Mathf.Clamp01(value);
227 if (newValue != _verticalSliderPercent) {
228 _verticalSliderPercent = newValue;
230 }
231 }
232 }
233
241 get {
242 float range = _horizontalValueRange.y - _horizontalValueRange.x;
243 if (range == 0F) return 0;
244 else {
245 return (int)(_horizontalSliderPercent * horizontalSteps * 1.001F);
246 }
247 }
248 }
249
253 public bool wasSlid {
254 get { return _wasSlid && _sawWasSlid; }
255 }
256
257 #endregion
258
259 #region Internal State
260
262 protected float _verticalSliderPercent;
263 protected RectTransform parent;
264
265 private bool _started = false;
266
267 private bool _sawWasSlid = false;
268 private bool _wasSlid = false;
269
270 #endregion
271
272 #region Unity Events
273
274 protected override void OnValidate() {
275 base.OnValidate();
276
277 if (this.transform.parent != null && this.transform.parent.GetComponent<RectTransform>() != null) {
278 _parentHasRectTransform = true;
279 }
280 else {
281 _parentHasRectTransform = false;
282 }
283 }
284
285 protected override void OnEnable() {
286 base.OnEnable();
287
288 OnContactStay += calculateSliderValues;
289 }
290
291 protected override void OnDisable() {
292 OnContactStay -= calculateSliderValues;
293
294 base.OnDisable();
295 }
296
297 protected override void Start() {
298 if (_started) return;
299
300 _started = true;
301
302 calculateSliderLimits();
303
304 switch (sliderType) {
305 case SliderType.Horizontal:
306 verticalSlideLimits = new Vector2(0, 0);
307 break;
308 case SliderType.Vertical:
309 horizontalSlideLimits = new Vector2(0, 0);
310 break;
311 }
312
313 base.Start();
314
315 HorizontalSlideEvent += _horizontalSlideEvent.Invoke;
316 VerticalSlideEvent += _verticalSlideEvent.Invoke;
317
318 HorizontalSlideEvent += (f) => { _wasSlid = true; };
319 VerticalSlideEvent += (f) => { _wasSlid = true; };
320
323
327 }
328 }
329
330 protected override void Update() {
331 base.Update();
332
333 if (!Application.isPlaying) { return; }
334
335 if (isPressed || isGrasped) {
336 calculateSliderValues();
337 }
338
339 // Whenever "_wasSlid" is set to true (however many times between Update() cycles),
340 // this logic produces a single Update() cycle through which slider.wasSlid
341 // will return true. (This allows observing MonoBehaviours to simply check
342 // "wasSlid" during their Update() cycle to perform updating logic.)
343 if (_wasSlid && !_sawWasSlid) {
344 _sawWasSlid = true;
345 }
346 else if (_sawWasSlid) {
347 _wasSlid = false;
348 _sawWasSlid = false;
349 }
350 }
351
352 #endregion
353
354 #region Public API
355
357 calculateSliderLimits();
358 }
359
360 #endregion
361
362 #region Internal Methods
363
364 private void calculateSliderLimits() {
365 if (transform.parent != null) {
366 parent = transform.parent.GetComponent<RectTransform>();
367
368 if (overrideRectLimits) return;
369
370 if (parent != null) {
371 if (parent.rect.width < 0f || parent.rect.height < 0f) {
372 Debug.LogError("Parent Rectangle dimensions negative; can't set slider boundaries!", parent.gameObject);
373 enabled = false;
374 } else {
375 var self = transform.GetComponent<RectTransform>();
376 if (self != null) {
377 horizontalSlideLimits = new Vector2(parent.rect.xMin - transform.localPosition.x + self.rect.width / 2F, parent.rect.xMax - transform.localPosition.x - self.rect.width / 2F);
379 horizontalSlideLimits = new Vector2(0F, 0F);
380 }
381 if (Mathf.Abs(horizontalSlideLimits.x) < 0.0001F) {
383 }
384 if (Mathf.Abs(horizontalSlideLimits.y) < 0.0001F) {
386 }
387
388 verticalSlideLimits = new Vector2(parent.rect.yMin - transform.localPosition.y + self.rect.height / 2F, parent.rect.yMax - transform.localPosition.y - self.rect.height / 2F);
390 verticalSlideLimits = new Vector2(0F, 0F);
391 }
392 if (Mathf.Abs(verticalSlideLimits.x) < 0.0001F) {
393 verticalSlideLimits.x = 0F;
394 }
395 if (Mathf.Abs(verticalSlideLimits.y) < 0.0001F) {
396 verticalSlideLimits.y = 0F;
397 }
398 } else {
399 horizontalSlideLimits = new Vector2(parent.rect.xMin - transform.localPosition.x, parent.rect.xMax - transform.localPosition.x);
400 verticalSlideLimits = new Vector2(parent.rect.yMin - transform.localPosition.y, parent.rect.yMax - transform.localPosition.y);
401 }
402 }
403 }
404 }
405 }
406
407 protected override Vector3 constrainDepressedLocalPosition(Vector3 desiredOffset) {
408 Vector3 unSnappedPosition =
409 new Vector3(Mathf.Clamp((localPhysicsPosition.x + desiredOffset.x), initialLocalPosition.x + horizontalSlideLimits.x, initialLocalPosition.x + horizontalSlideLimits.y),
411 (localPhysicsPosition.z + desiredOffset.z));
412
413 float hSliderPercent = Mathf.InverseLerp(initialLocalPosition.x + horizontalSlideLimits.x, initialLocalPosition.x + horizontalSlideLimits.y, unSnappedPosition.x);
414 if (horizontalSteps > 0) {
415 hSliderPercent = Mathf.Round(hSliderPercent * (horizontalSteps)) / (horizontalSteps);
416 }
417
418 float vSliderPercent = Mathf.InverseLerp(initialLocalPosition.y + verticalSlideLimits.x, initialLocalPosition.y + verticalSlideLimits.y, unSnappedPosition.y);
419 if (verticalSteps > 0) {
420 vSliderPercent = Mathf.Round(vSliderPercent * (verticalSteps)) / (verticalSteps);
421 }
422
423 return new Vector3(Mathf.Lerp(initialLocalPosition.x + horizontalSlideLimits.x, initialLocalPosition.x + horizontalSlideLimits.y, hSliderPercent),
425 (localPhysicsPosition.z + desiredOffset.z));
426 }
427
428 #endregion
429
430 #region Gizmos
431
432 protected override void OnDrawGizmosSelected() {
433 base.OnDrawGizmosSelected();
434
435 if (transform.parent != null) {
436 Vector3 originPosition = Application.isPlaying ? initialLocalPosition : transform.localPosition;
437 if (Application.isPlaying && startingPositionMode == StartingPositionMode.Relaxed) {
438 originPosition = originPosition + Vector3.back * Mathf.Lerp(minMaxHeight.x, minMaxHeight.y, restingHeight);
439 }
440
441 // Actual slider slide limits
442 Gizmos.color = Color.blue;
443 Gizmos.DrawWireCube(originPosition +
444 new Vector3((sliderType == SliderType.Vertical ? 0F : (horizontalSlideLimits.x + horizontalSlideLimits.y) * 0.5f),
445 (sliderType == SliderType.Horizontal ? 0F : (verticalSlideLimits.x + verticalSlideLimits.y) * 0.5f),
446 0f),
447 new Vector3((sliderType == SliderType.Vertical ? 0F : horizontalSlideLimits.y - horizontalSlideLimits.x),
449 0f));
450
451 var self = GetComponent<RectTransform>();
452 if (self != null) {
453 // Apparent slide limits (against own rect limits)
454 parent = transform.parent.GetComponent<RectTransform>();
455 if (parent != null) {
456 var parentRectHorizontal = new Vector2(parent.rect.xMin - originPosition.x, parent.rect.xMax - originPosition.x);
457 var parentRectVertical = new Vector2(parent.rect.yMin - originPosition.y, parent.rect.yMax - originPosition.y);
458 Gizmos.color = Color.Lerp(Color.blue, Color.cyan, 0.5F);
459 Gizmos.DrawWireCube(originPosition +
460 new Vector3((parentRectHorizontal.x + parentRectHorizontal.y) * 0.5f,
461 (parentRectVertical.x + parentRectVertical.y) * 0.5f,
463 Mathf.Lerp(minMaxHeight.x, minMaxHeight.y, 0.5F) - Mathf.Lerp(minMaxHeight.x, minMaxHeight.y, 1 - restingHeight)
464 : -1F * Mathf.Lerp(minMaxHeight.x, minMaxHeight.y, 0.5F))),
465 new Vector3(parentRectHorizontal.y - parentRectHorizontal.x, parentRectVertical.y - parentRectVertical.x, (minMaxHeight.y - minMaxHeight.x)));
466
467 // Own rect width/height
468 Gizmos.color = Color.cyan;
469 Gizmos.DrawWireCube(originPosition,
470 self.rect.width * Vector3.right
471 + self.rect.height * Vector3.up);
472 }
473 }
474 }
475 }
476
477 #endregion
478 }
479}
UnityEngine.Debug Debug
Definition: TanodaServer.cs:19
UnityEngine.Color Color
Definition: TestScript.cs:32
Action OnContactStay
Called every frame during which one or more interaction controllers is colliding with this object.
Rigidbody rigidbody
The Rigidbody associated with this interaction object.
bool isGrasped
Gets whether this object is grasped by any interaction controller.
A physics-enabled button. Activated by physically pressing the button, with events for press and unpr...
Vector3 physicsPosition
The physical position of this element in world space; may diverge from the graphical position.
bool isPressed
Gets whether the button is currently held down.
Vector2 minMaxHeight
The minimum and maximum heights the button can exist at.
Vector3 localPhysicsPosition
The physical position of this element in local space; may diverge from the graphical position.
Vector3 initialLocalPosition
The initial position of this element in local space, stored on Start().
float restingHeight
The height that this button rests at; this value is a lerp in between the min and max height.
A physics-enabled slider. Sliding is triggered by physically pushing the slider to its compressed pos...
bool wasSlid
Gets whether the slider was slide in the latest Update().
float VerticalSliderPercent
summary> This slider's horizontal slider value, mapped between the values in the HorizontalValueRange...
int verticalSteps
summary> Triggered while this slider is depressed.
override Vector3 constrainDepressedLocalPosition(Vector3 desiredOffset)
Clamps the input local-space position to the bounds allowed by this UI element, without clamping alon...
float HorizontalSliderValue
summary> This slider's current vertical slider value, mapped between the values in the VerticalValueR...
int horizontalStepValue
Returns the number of horizontal steps past the minimum value of the slider, for sliders with a non-z...