Tanoda
InteractionXRController.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
10
11#if UNITY_2017_2_OR_NEWER
12using UnityEngine.XR;
13#else
14using UnityEngine.VR;
15#endif
16
17using System;
18using System.Collections.Generic;
19using UnityEngine;
20using Leap.Unity.Query;
21using Leap.Unity.Space;
22using UnityEngine.Serialization;
23
24namespace Leap.Unity.Interaction {
25
26 [DisallowMultipleComponent]
28
29 #region Inspector
30
31 [Header("Controller Configuration")]
32
33 [Tooltip("Read-only. InteractionXRControllers use Unity's built-in XRNode tracking "
34 + "API to receive tracking data for XR controllers by default. If you add a "
35 + "custom script to set this controller's trackingProvider to something "
36 + "other than the DefaultXRNodeTrackingProvider, the change will be reflected "
37 + "here. (Hint: Use the ExecuteInEditMode attribute.)")]
38 [Disable, SerializeField]
39 #pragma warning disable 0414
40 private string _trackingProviderType = "DefaultXRNodeTrackingProvider";
41#pragma warning restore 0414
44 }
45
46 [Tooltip("If this string is not empty and does not match a controller in "
47 + "Input.GetJoystickNames(), then this game object will disable itself.")]
48 [SerializeField, EditTimeOnly]
49 [FormerlySerializedAs("_deviceString")]
50 private string _deviceJoystickTokens = "oculus touch right"; // or, e.g., "openvr controller right"
51 public string deviceJoystickTokens { get { return _deviceJoystickTokens; } }
52
53 #pragma warning disable 0649
54 [Tooltip("Which hand will hold this controller? This property cannot be changed "
55 + "at runtime.")]
56 [SerializeField, EditTimeOnly]
57 private Chirality _chirality;
58 public Chirality chirality { get { return _chirality; } }
59 #pragma warning restore 0649
60
61 [Tooltip("Whether to continuously poll attached joystick data for a joystick that "
62 + "matches the device joystick tokens, using Input.GetJoystickNames(). This "
63 + "call allocates garbage, so be wary of setting a low polling interval.")]
64 [SerializeField, OnEditorChange("pollConnection")]
65 private bool _pollConnection = true;
75 public bool pollConnection {
76 get { return _pollConnection; }
77 set { _pollConnection = value; }
78 }
79 [Tooltip("The period in seconds at which to check if the controller is connected "
80 + "by calling Input.GetJoystickNames(). This call allocates garbage, so be "
81 + "wary of setting a low polling interval.")]
82 [MinValue(0f)]
83 [DisableIf("_pollConnection", isEqualTo: false)]
84 public float pollConnectionInterval = 2f;
85
86 [Header("Hover Configuration")]
87
88 [Tooltip("This is the point used to determine the distance to objects for the "
89 + "purposes of their 'hovered' state. Generally, it should be somewhere "
90 + "between the tip of the controller and the controller's center of mass.")]
91 [SerializeField]
92 private Transform _hoverPoint;
93
94 [Tooltip("These points refine the hover point when determining distances to "
95 + "interaction objects for evaluating which object should be the primary hover "
96 + "of this interaction controller. An object's proximity to one of these "
97 + "points is interpreted as the user's intention to interact specifically "
98 + "with that object, and is important when building less accident-prone user "
99 + "interfaces. For example, hands place their primary hover points on the "
100 + "thumb, index finger, and middle finger by default. Controllers generally "
101 + "should have a primary hover point at any tip of the controller you expect "
102 + "users might use to hit a button. Warning: Each point costs distance checks "
103 + "against nearby objects, so making this list large is costly!")]
104 [SerializeField]
105 public new List<Transform> primaryHoverPoints;
106
107 [Header("Grasping Configuration")]
108
109 [Tooltip("The point around which to check objects eligible for being grasped. Only "
110 + "objects with an InteractionBehaviour component with ignoreGrasping disabled "
111 + "are eligible for grasping. Upon attempting to grasp with a controller, the "
112 + "object closest to the grasp point is chosen for grasping.")]
113 public Transform graspPoint;
114
115 public float maxGraspDistance = 0.06F;
116
117 [Tooltip("This string should match an Axis specified in Edit->Project Settings->"
118 + "Input. This is the button to use to listen for grasping.")]
119 public string graspButtonAxis;
120
121 [Tooltip("The duration of time in seconds beyond initially pressing the grasp button "
122 + "that the user can move the grasp point within range of a graspable "
123 + "interaction object and still trigger a grasp. With a value of zero, objects "
124 + "can only be grasped if they are already within the grasp distance of the "
125 + "grasp point.")]
126 public float graspTimingSlop = 0.10F;
127
128 [Header("Enable/Disable GameObjects with Tracking State")]
129
130 [Tooltip("These objects will be made active only while the controller is tracked. "
131 + "For more fine-tuned behavior, we recommend implementing your own logic. "
132 + "controller.isJoystickDetected and controller.isTracked are useful for this.")]
133 [SerializeField]
134 private List<GameObject> _enableObjectsOnlyWhenTracked;
141 public List<GameObject> enableObjectsOnlyWhenTracked {
142 get {
143 if (_enableObjectsOnlyWhenTracked == null) {
144 _enableObjectsOnlyWhenTracked = new List<GameObject>();
145 }
146 return _enableObjectsOnlyWhenTracked;
147 }
148 }
149
150 #endregion // Inspector
151
152 #region Unity Events
153
154 protected override void Reset() {
155 base.Reset();
156
157 hoverEnabled = true;
158 contactEnabled = true;
159 graspingEnabled = true;
160
161 trackingProvider = _defaultTrackingProvider;
162 _hoverPoint = null;
163 primaryHoverPoints.Clear();
164 graspPoint = null;
165
166 maxGraspDistance = 0.06F;
167 graspTimingSlop = 0.1F;
168 }
169
170 protected virtual void OnValidate() {
171 _trackingProviderType = trackingProvider.GetType().ToString();
172 }
173
174 protected override void Start() {
175 base.Start();
176
177 trackingProvider.OnTrackingDataUpdate += refreshControllerTrackingData;
178 }
179
180 protected override void fixedUpdateController() {
181 fixedUpdatePollConnection();
182
183 if (isTracked) {
184 foreach (var gameObject in enableObjectsOnlyWhenTracked) {
185 gameObject.SetActive(true);
186 }
187 }
188 else {
189 foreach (var gameObject in enableObjectsOnlyWhenTracked) {
190 gameObject.SetActive(false);
191 }
192 }
193
194 refreshContactBoneTargets();
195 }
196
197 #endregion
198
199 #region Controller Connection Polling
200
201 private bool _isJoystickDetected = false;
212 public bool isJoystickDetected {
213 get { return string.IsNullOrEmpty(deviceJoystickTokens) || _isJoystickDetected; }
214 }
215
216 private float _pollTimer = 0f;
217
218 private void fixedUpdatePollConnection() {
219 if (_pollConnection && !_isJoystickDetected) {
220 _pollTimer += Time.fixedDeltaTime;
221 }
222
223 if (_pollConnection && _pollTimer >= pollConnectionInterval) {
224 _pollTimer = 0f;
225
227 }
228 }
229
231 _isJoystickDetected = true; // deviceJoystickTokens must be parsible to falsify.
232
233 if (deviceJoystickTokens != null && deviceJoystickTokens.Length > 0) {
234 string[] joysticksConnected = Input.GetJoystickNames().Query()
235 .Select(s => s.ToLower()).ToArray();
236 string[] controllerSupportTokens = deviceJoystickTokens.ToLower()
237 .Split(" ".ToCharArray());
238 bool matchesController = joysticksConnected.Query()
239 .Any(joystick => controllerSupportTokens.Query()
240 .All(token => joystick.Contains(token)));
241
242 _isJoystickDetected = matchesController;
243 }
244 }
245
246 #endregion
247
248 #region Controller Tracking
249
250 private bool _hasTrackedPositionLastFrame = false;
251 private Vector3 _trackedPositionLastFrame = Vector3.zero;
252 private Quaternion _trackedRotationLastFrame = Quaternion.identity;
253
254 private IXRControllerTrackingProvider _backingTrackingProvider = null;
256 get {
257 if (_backingDefaultTrackingProvider == null) {
258 _backingDefaultTrackingProvider = _defaultTrackingProvider;
259 }
260
261 return _backingDefaultTrackingProvider;
262 }
263 set {
264 if (_backingTrackingProvider != null) {
265 _backingTrackingProvider.OnTrackingDataUpdate -= refreshControllerTrackingData;
266 }
267
268 _backingTrackingProvider = value;
269
270 if (_backingTrackingProvider != null) {
271 _backingTrackingProvider.OnTrackingDataUpdate += refreshControllerTrackingData;
272 }
273 }
274 }
275
276 private IXRControllerTrackingProvider _backingDefaultTrackingProvider;
277 private IXRControllerTrackingProvider _defaultTrackingProvider {
278 get {
279 if (_backingDefaultTrackingProvider == null) {
280 refreshDefaultTrackingProvider();
281 }
282
283 return _backingDefaultTrackingProvider;
284 }
285 set {
286 _backingDefaultTrackingProvider = value;
287 }
288 }
289
290 private void refreshDefaultTrackingProvider() {
291 var defaultProvider = gameObject.GetComponent<DefaultXRNodeTrackingProvider>();
292 if (defaultProvider == null) {
293 defaultProvider = gameObject.AddComponent<DefaultXRNodeTrackingProvider>();
294 }
295 defaultProvider.xrNode = this.xrNode;
296
297 _defaultTrackingProvider = defaultProvider;
298 }
299
300 private void refreshControllerTrackingData(Vector3 position, Quaternion rotation) {
301 refreshIsBeingMoved(position, rotation);
302
303 if (_hasTrackedPositionLastFrame) {
304 _trackedPositionLastFrame = this.transform.position;
305 _trackedRotationLastFrame = this.transform.rotation;
306 }
307
308 this.transform.position = position;
309 this.transform.rotation = rotation;
310 refreshContactBoneTargets();
311
312 if (!_hasTrackedPositionLastFrame) {
313 _hasTrackedPositionLastFrame = true;
314 _trackedPositionLastFrame = this.transform.position;
315 _trackedRotationLastFrame = this.transform.rotation;
316 }
317 }
318
319 #endregion
320
321 #region Movement Detection
322
323 private const float RIG_LOCAL_MOVEMENT_SPEED_THRESHOLD = 00.07F;
324 private const float RIG_LOCAL_MOVEMENT_SPEED_THRESHOLD_SQR
325 = RIG_LOCAL_MOVEMENT_SPEED_THRESHOLD * RIG_LOCAL_MOVEMENT_SPEED_THRESHOLD;
326 private const float RIG_LOCAL_ROTATION_SPEED_THRESHOLD = 10.00F;
327 private const float BEING_MOVED_TIMEOUT = 0.5F;
328
329 private float _lastTimeMoved = 0F;
330 private bool _isBeingMoved = false;
331 private void refreshIsBeingMoved(Vector3 position, Quaternion rotation) {
332 var isMoving = false;
333 var baseTransform = this.manager.transform;
334
335 // Check translation speed, relative to the Interaction Manager.
336 var baseLocalPos = baseTransform.InverseTransformPoint(position);
337 var baseLocalPosLastFrame =baseTransform.InverseTransformPoint(
338 _trackedPositionLastFrame);
339 var baseLocalSqrSpeed = ((baseLocalPos - baseLocalPosLastFrame)
340 / Time.fixedDeltaTime).sqrMagnitude;
341 if (baseLocalSqrSpeed > RIG_LOCAL_MOVEMENT_SPEED_THRESHOLD_SQR) {
342 isMoving = true;
343 }
344
345 // Check rotation speed, relative to the Interaction Manager.
346 var baseLocalRot = baseTransform.InverseTransformRotation(rotation);
347 var baseLocalRotLastFrame = baseTransform.InverseTransformRotation(
348 _trackedRotationLastFrame);
349 var baseLocalAngularSpeed = Quaternion.Angle(baseLocalRot, baseLocalRotLastFrame)
350 / Time.fixedDeltaTime;
351 if (baseLocalAngularSpeed > RIG_LOCAL_ROTATION_SPEED_THRESHOLD) {
352 isMoving = true;
353 }
354
355 if (isMoving) {
356 _lastTimeMoved = Time.fixedTime;
357 }
358
359 // "isMoving" lasts for a bit after the controller stops moving, to avoid
360 // rapid oscillation of the value.
361 var timeSinceLastMoving = Time.fixedTime - _lastTimeMoved;
362 _isBeingMoved = trackingProvider != null && trackingProvider.isTracked
363 && timeSinceLastMoving < BEING_MOVED_TIMEOUT;
364 }
365
366 #endregion
367
368 #region General InteractionController Implementation
369
375 public override bool isTracked {
376 get {
378 }
379 }
380
388 public override bool isBeingMoved {
389 get {
390 return _isBeingMoved;
391 }
392 }
393
400 #if UNITY_2017_2_OR_NEWER
401 public XRNode xrNode {
402 get { return chirality == Chirality.Left ? XRNode.LeftHand : XRNode.RightHand; }
403 }
404 #else
405 public VRNode xrNode {
406 get { return chirality == Chirality.Left ? VRNode.LeftHand : VRNode.RightHand; }
407 }
408 #endif
409
413 public override bool isLeft {
414 get { return chirality == Chirality.Left; }
415 }
416
420 public override Vector3 position {
421 get {
422 return this.transform.position;
423 }
424 }
425
429 public override Quaternion rotation {
430 get {
431 return this.transform.rotation;
432 }
433 }
434
438 public override Vector3 velocity {
439 get {
440 if (_hasTrackedPositionLastFrame) {
441 return (this.transform.position - _trackedPositionLastFrame) / Time.fixedDeltaTime;
442 }
443 else {
444 return Vector3.zero;
445 }
446 }
447 }
448
454 get { return ControllerType.XRController; }
455 }
456
461 public override InteractionHand intHand {
462 get { return null; }
463 }
464
469 protected override void onObjectUnregistered(IInteractionBehaviour intObj) { }
470
471 #endregion
472
473 #region Hover Implementation
474
478 public override Vector3 hoverPoint {
479 get { return _hoverPoint == null ? Vector3.zero : _hoverPoint.position; }
480 }
481
490 protected override List<Transform> _primaryHoverPoints {
491 get { return primaryHoverPoints; }
492 }
493
494 private Vector3 _pivotingPositionOffset = Vector3.zero;
495 private Vector3 _unwarpingPositionOffset = Vector3.zero;
496 private Quaternion _unwarpingRotationOffset = Quaternion.identity;
497
498 protected override void unwarpColliders(Transform primaryHoverPoint, ISpaceComponent warpedSpaceElement) {
499 // Extension method calculates "unwarped" pose in world space.
500 Vector3 unwarpedPosition;
501 Quaternion unwarpedRotation;
502 warpedSpaceElement.anchor.transformer.WorldSpaceUnwarp(primaryHoverPoint.position,
503 primaryHoverPoint.rotation,
504 out unwarpedPosition,
505 out unwarpedRotation);
506
507 // Shift the controller to have its origin on the primary hover point so that
508 // rotations applied to the hand cause it to pivot around that point, then apply
509 // the position and rotation transformation.
510 _pivotingPositionOffset = -primaryHoverPoint.position;
511 _unwarpingPositionOffset = unwarpedPosition;
512 _unwarpingRotationOffset = unwarpedRotation * Quaternion.Inverse(primaryHoverPoint.rotation);
513
514 refreshContactBoneTargets(useUnwarpingData: true);
515 }
516
517 #endregion
518
519 #region Contact Implementation
520
521 private Vector3[] _contactBoneLocalPositions;
522 private Quaternion[] _contactBoneLocalRotations;
523
524 private Vector3[] _contactBoneTargetPositions;
525 private Quaternion[] _contactBoneTargetRotations;
526
527 private ContactBone[] _contactBones;
528 public override ContactBone[] contactBones {
529 get { return _contactBones; }
530 }
531
532 private GameObject _contactBoneParent;
533 protected override GameObject contactBoneParent {
534 get { return _contactBoneParent; }
535 }
536
537 protected override bool initContact() {
538 initContactBones();
539
540 if (_contactBoneParent == null) {
541 _contactBoneParent = new GameObject("VR Controller Contact Bones "
542 + (isLeft ? "(Left)" : "(Right"));
543 }
544
545 foreach (var contactBone in _contactBones) {
546 contactBone.transform.parent = _contactBoneParent.transform;
547 }
548
549 return true;
550 }
551
552 private void refreshContactBoneTargets(bool useUnwarpingData = false) {
554
555 // Move the controller transform temporarily into its "unwarped space" pose
556 // (only if we are using the controller in a curved space)
557 if (useUnwarpingData) {
558 moveControllerTransform(_pivotingPositionOffset, Quaternion.identity);
559 moveControllerTransform(_unwarpingPositionOffset, _unwarpingRotationOffset);
560 }
561
562 for (int i = 0; i < _contactBones.Length; i++) {
563 _contactBoneTargetPositions[i]
564 = this.transform.TransformPoint(_contactBoneLocalPositions[i]);
565 _contactBoneTargetRotations[i]
566 = this.transform.TransformRotation(_contactBoneLocalRotations[i]);
567 }
568
569 // Move the controller transform back to its original pose.
570 if (useUnwarpingData) {
571 moveControllerTransform(-_unwarpingPositionOffset, Quaternion.Inverse(_unwarpingRotationOffset));
572 moveControllerTransform(-_pivotingPositionOffset, Quaternion.identity);
573 }
574 }
575 }
576
577 private void moveControllerTransform(Vector3 deltaPosition, Quaternion deltaRotation) {
578 this.transform.rotation = deltaRotation * this.transform.rotation;
579 this.transform.position = deltaPosition + this.transform.position;
580 }
581
582 private List<ContactBone> _contactBoneBuffer = new List<ContactBone>();
583 private List<Collider> _colliderBuffer = new List<Collider>();
584 private void initContactBones() {
585 _colliderBuffer.Clear();
586 _contactBoneBuffer.Clear();
587
588 // Scan for existing colliders and construct contact bones out of them.
589 Utils.FindColliders<Collider>(this.gameObject, _colliderBuffer,
590 includeInactiveObjects: true);
591
592 foreach (var collider in _colliderBuffer) {
593 if (collider.isTrigger) continue; // Contact Bones are for "contacting" colliders.
594
595 ContactBone contactBone = collider.gameObject.AddComponent<ContactBone>();
596 Rigidbody body = collider.gameObject.GetComponent<Rigidbody>();
597 if (body == null) {
598 body = collider.gameObject.AddComponent<Rigidbody>();
599 }
600
601 body.freezeRotation = true;
602 body.useGravity = false;
603 body.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
604 body.mass = 1F;
605
606 contactBone.interactionController = this;
607 contactBone.rigidbody = body;
608 contactBone.collider = collider;
609
610 _contactBoneBuffer.Add(contactBone);
611 }
612
613 int numBones = _colliderBuffer.Count;
614 _contactBones = new ContactBone[numBones];
615 _contactBoneLocalPositions = new Vector3[numBones];
616 _contactBoneLocalRotations = new Quaternion[numBones];
617 _contactBoneTargetPositions = new Vector3[numBones];
618 _contactBoneTargetRotations = new Quaternion[numBones];
619 for (int i = 0; i < numBones; i++) {
620 _contactBones[i] = _contactBoneBuffer[i];
621
622 _contactBoneLocalPositions[i]
623 = _contactBoneTargetPositions[i]
624 = this.transform.InverseTransformPoint(_contactBones[i].transform.position);
625 _contactBoneLocalRotations[i]
626 = _contactBoneTargetRotations[i]
627 = this.transform.InverseTransformRotation(_contactBones[i].transform.rotation);
628 }
629 }
630
631 protected override void getColliderBoneTargetPositionRotation(int contactBoneIndex,
632 out Vector3 targetPosition,
633 out Quaternion targetRotation) {
634 targetPosition = _contactBoneTargetPositions[contactBoneIndex];
635 targetRotation = _contactBoneTargetRotations[contactBoneIndex];
636 }
637
638 #endregion
639
640 #region Grasping Implementation
641
653 public Func<float> graspAxisOverride = null;
654
655 private float _graspDepressedValue = 0.8F;
660 public float graspDepressedValue {
661 get { return _graspDepressedValue; }
662 set { _graspDepressedValue = value; }
663 }
664
665 private float _graspReleasedValue = 0.7F;
671 public float graspReleasedValue {
672 get { return _graspReleasedValue; }
673 set { _graspReleasedValue = value; }
674 }
675
676 private List<Vector3> _graspManipulatorPointsBuffer = new List<Vector3>();
683 public override List<Vector3> graspManipulatorPoints {
684 get {
685 _graspManipulatorPointsBuffer.Clear();
686 _graspManipulatorPointsBuffer.Add(hoverPoint);
687 _graspManipulatorPointsBuffer.Add(hoverPoint + this.transform.rotation * Vector3.forward * 0.05F);
688 _graspManipulatorPointsBuffer.Add(hoverPoint + this.transform.rotation * Vector3.right * 0.05F);
689 return _graspManipulatorPointsBuffer;
690 }
691 }
692
693 private IInteractionBehaviour _closestGraspableObject = null;
694
695 private bool _graspButtonLastFrame = false;
696 private bool _graspButtonDown = false;
697 private bool _graspButtonUp = false;
698 private float _graspButtonDownSlopTimer = 0F;
699 private bool _inputWarningDisplayed = false;
700
701 public override Vector3 GetGraspPoint() {
702 return graspPoint.transform.position;
703 }
704
705 protected override void fixedUpdateGraspingState() {
706 refreshClosestGraspableObject();
707
708 fixedUpdateGraspButtonState();
709 }
710
711 private void refreshClosestGraspableObject() {
712 _closestGraspableObject = null;
713
714 float closestGraspableDistance = float.PositiveInfinity;
715 foreach (var intObj in graspCandidates) {
716 float testDist = intObj.GetHoverDistance(this.graspPoint.position);
717 if (testDist < maxGraspDistance && testDist < closestGraspableDistance) {
718 _closestGraspableObject = intObj;
719 closestGraspableDistance = testDist;
720 }
721 }
722 }
723
724 private void fixedUpdateGraspButtonState(bool ignoreTemporal = false) {
725 _graspButtonDown = false;
726 _graspButtonUp = false;
727
728 bool graspButton = _graspButtonLastFrame;
729
730 if (!_graspButtonLastFrame) {
731 if (graspAxisOverride == null) {
732 try {
733 graspButton = Input.GetAxis(graspButtonAxis) > graspDepressedValue;
734 } catch {
735 if (!_inputWarningDisplayed) {
736 Debug.LogWarning("VR CONTROLLER INPUT AXES ARE NOT SET UP. Go to your Input Manager " +
737 "and add a definition for " + graspButtonAxis + " on the " + (isLeft ? "9" : "10") + "th " +
738 "Joystick Axis or disable this controller.", this);
739 _inputWarningDisplayed = true;
740 }
741 graspButton = Input.GetKey(isLeft ? KeyCode.JoystickButton14: KeyCode.JoystickButton15);
742 }
743 }
744 else {
745 graspButton = graspAxisOverride() > graspDepressedValue;
746 }
747
748 if (graspButton) {
749 // Grasp button was _just_ depressed this frame.
750 _graspButtonDown = true;
751 _graspButtonDownSlopTimer = graspTimingSlop;
752 }
753 }
754 else {
756 Debug.LogWarning("The graspReleasedValue should be less than or equal to the "
757 + "graspDepressedValue!", this);
759 }
760
761 if (graspAxisOverride == null) {
762 try {
763 graspButton = Input.GetAxis(graspButtonAxis) > graspDepressedValue;
764 } catch {
765 if (!_inputWarningDisplayed) {
766 Debug.LogWarning("VR CONTROLLER INPUT AXES ARE NOT SET UP. Go to your Input Manager " +
767 "and add a definition for " + graspButtonAxis + " on the " + (isLeft ? "9" : "10") + "th " +
768 "Joystick Axis or disable this controller.", this);
769 _inputWarningDisplayed = true;
770 }
771 graspButton = Input.GetKey(isLeft ? KeyCode.JoystickButton14 : KeyCode.JoystickButton15);
772 }
773 }
774 else {
775 graspButton = graspAxisOverride() > graspReleasedValue;
776 }
777
778 if (!graspButton) {
779 // Grasp button was _just_ released this frame.
780 _graspButtonUp = true;
781 _graspButtonDownSlopTimer = 0F;
782 }
783 }
784
785 if (_graspButtonDownSlopTimer > 0F) {
786 _graspButtonDownSlopTimer -= Time.fixedDeltaTime;
787 }
788
789 _graspButtonLastFrame = graspButton;
790 }
791
792 protected override bool checkShouldGrasp(out IInteractionBehaviour objectToGrasp) {
793 bool shouldGrasp = !isGraspingObject
794 && (_graspButtonDown || _graspButtonDownSlopTimer > 0F)
795 && _closestGraspableObject != null;
796
797 objectToGrasp = null;
798 if (shouldGrasp) { objectToGrasp = _closestGraspableObject; }
799
800 return shouldGrasp;
801 }
802
808 protected override bool checkShouldGraspAtemporal(IInteractionBehaviour intObj) {
809 bool shouldGrasp = !isGraspingObject
810 && _graspButtonLastFrame
811 && intObj.GetHoverDistance(graspPoint.position) < maxGraspDistance;
812 if (shouldGrasp) {
813 var tempControllers = Pool<List<InteractionController>>.Spawn();
814 try {
815 intObj.BeginGrasp(tempControllers);
816 }
817 finally {
818 tempControllers.Clear();
819 Pool<List<InteractionController>>.Recycle(tempControllers);
820 }
821 }
822
823 return shouldGrasp;
824 }
825
826 protected override bool checkShouldRelease(out IInteractionBehaviour objectToRelease) {
827 bool shouldRelease = _graspButtonUp && isGraspingObject;
828
829 objectToRelease = null;
830 if (shouldRelease) { objectToRelease = graspedObject; }
831
832 return shouldRelease;
833 }
834
835 #endregion
836
837 #region Gizmos
838
839 public override void OnDrawRuntimeGizmos(RuntimeGizmos.RuntimeGizmoDrawer drawer) {
840 base.OnDrawRuntimeGizmos(drawer);
841
842 // Grasp Point
843 float graspAmount = 0F;
844 if (graspAxisOverride != null) graspAmount = graspAxisOverride();
845 else {
846 try {
847 graspAmount = Input.GetAxis(graspButtonAxis);
848 }
849 catch (ArgumentException) { }
850 }
851
852 drawer.color = Color.Lerp(GizmoColors.GraspPoint, Color.white, graspAmount);
853 drawer.DrawWireSphere(GetGraspPoint(), maxGraspDistance);
854
855 // Nearest graspable object
856 if (_closestGraspableObject != null) {
857 drawer.color = Color.Lerp(GizmoColors.Graspable, Color.white, Mathf.Sin(Time.time * 2 * Mathf.PI * 2F));
858 drawer.DrawWireSphere(_closestGraspableObject.rigidbody.position, maxGraspDistance * 0.75F);
859 }
860 }
861
862 #endregion
863
864 }
865
866}
UnityEngine.Debug Debug
Definition: TanodaServer.cs:19
UnityEngine.Color Color
Definition: TestScript.cs:32
Contact Bones store data for the colliders and rigidbodies in each bone of the contact-related repres...
Definition: ContactBone.cs:24
Implements IVRControllerTrackingProvider using Unity.XR.InputTracking for XRNodes....
ReadonlyHashSet< IInteractionBehaviour > graspCandidates
Gets the set of objects currently considered graspable.
IInteractionBehaviour graspedObject
Gets the object the controller is currently grasping, or null if there is no such object.
bool isGraspingObject
Gets whether the controller is currently grasping an object.
List< GameObject > enableObjectsOnlyWhenTracked
These objects will be made active only while the controller is tracked. For more fine-tuned behavior,...
override Vector3 hoverPoint
Gets the center point used for hover distance checking.
override bool isBeingMoved
Gets whether or not the underlying controller is currently being moved in world space,...
override void OnDrawRuntimeGizmos(RuntimeGizmos.RuntimeGizmoDrawer drawer)
override bool checkShouldRelease(out IInteractionBehaviour objectToRelease)
Returns whether this controller should release an object this fixed frame, and if so,...
override bool checkShouldGraspAtemporal(IInteractionBehaviour intObj)
If the provided object is within range of this VR controller's grasp point and the grasp button is cu...
override InteractionHand intHand
This implementation of InteractionControllerBase does not represent a Leap hand, so it need not retur...
override List< Vector3 > graspManipulatorPoints
Gets a list returning this controller's hoverPoint. Because the InteractionVRController represents a ...
Func< float > graspAxisOverride
By default, InteractionVRController uses Input.GetAxis(graspButtonAxis) to determine the "depression"...
override bool initContact()
Called to initialize contact colliders. See remarks for implementation requirements.
override void getColliderBoneTargetPositionRotation(int contactBoneIndex, out Vector3 targetPosition, out Quaternion targetRotation)
If your controller features no moving colliders relative to itself, simply return the desired positio...
override List< Transform > _primaryHoverPoints
Gets the list of points to be used to perform higher-fidelity "primary hover" checks....
VRNode xrNode
Gets the XRNode associated with this XR controller. Note: If the tracking mode for this controller is...
bool pollConnection
Whether to continuously poll attached joystick data for a joystick that matches the device joystick t...
float graspDepressedValue
The value between 0 and 1 past which the grasping axis value will cause an attempt to grasp a graspab...
override void fixedUpdateController()
Called just before the InteractionController proceeds with its usual FixedUpdate.
override bool isLeft
Gets whether the controller is a left-hand controller.
override Vector3 position
Gets the last-tracked position of the controller.
override bool checkShouldGrasp(out IInteractionBehaviour objectToGrasp)
Returns whether this controller should grasp an object this fixed frame, and if so,...
override ControllerType controllerType
Gets the type of controller this is. For InteractionVRController, the type is always ControllerType....
override void unwarpColliders(Transform primaryHoverPoint, ISpaceComponent warpedSpaceElement)
Implementing this method is necessary to support curved spaces as rendered by a Leap Graphic Renderer...
override void onObjectUnregistered(IInteractionBehaviour intObj)
InteractionVRController doesn't need to do anything when an object is unregistered.
override bool isTracked
Gets whether or not the underlying controller is currently tracked and any joystick token filtering h...
override void fixedUpdateGraspingState()
Called every fixed frame if grasping is enabled in the Interaction Manager.
bool isJoystickDetected
Whether the device joystick tokens matched an entry in Input.GetJoystickNames(). If pollConnection is...
override Quaternion rotation
Gets the last-tracked rotation of the controller.
override Vector3 GetGraspPoint()
Returns approximately where the controller is grasping the currently grasped InteractionBehaviour....
override Vector3 velocity
Gets the current velocity of the controller.
float graspReleasedValue
If the grasping axis value passes the graspDepressedValue, it must then drop underneath this value in...
IInteractionBehaviour is the interface that defines all Interaction objects, specifying the minimum s...
float GetHoverDistance(Vector3 worldPosition)
void BeginGrasp(List< InteractionController > beganGrasping)
The interface for providing tracking data to an InteractionVRController.
Action< Vector3, Quaternion > OnTrackingDataUpdate
An event that is fired whenever new tracking data is available for this controller.
bool isTracked
Gets whether or not this provider is currently tracking the controller for which it provides data.
ControllerType
The Interaction Engine can be controlled by hands tracked by the Leap Motion Controller,...