Replace UltimateXR with HurricaneVR

This commit is contained in:
2024-08-08 17:01:07 +02:00
parent e8658374d6
commit fb21dbbb73
5932 changed files with 358362 additions and 2174150 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e538dc1865da2ed41b9f803e3e586405
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,17 @@
using HurricaneVR.Framework.Shared;
using UnityEngine;
namespace HurricaneVR.Framework.Components.Creators
{
public class HVRPhysicsLeverCreator : MonoBehaviour
{
public Transform Anchor;
public Transform Lever;
public HVRAxis Axis;
public float Mass = 5f;
public float MinAngle = 90f;
public float MaxAngle = 90f;
public float GrabbedDamper = 3f;
public float Damper = 10f;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c1b17ef000f068b468782f079bd24ba6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,97 @@
using System;
using HurricaneVR.Framework.Core.Utils;
using UnityEngine;
using UnityEngine.Events;
namespace HurricaneVR.Framework.Components
{
/// <summary>
/// Basic button check for travel along a defined axis, requires the user to have setup their own constraint system.
/// Superceded by the new HVRPhysicsButton component which helps create joints and limits for you
/// </summary>
[RequireComponent(typeof(Rigidbody))]
public class HVRButton : MonoBehaviour
{
public VRButtonEvent ButtonDown = new VRButtonEvent();
public VRButtonEvent ButtonUp = new VRButtonEvent();
public Vector3 Axis;
[Tooltip("How far the button must travel to become pressed.")]
public float Threshold;
[Tooltip("Threshold to hit on the return to allow the button to be pressed again.")]
public float UpThreshold;
[Tooltip("The resting position of the button")]
public Vector3 StartPosition;
public bool IsPressed = false;
public AudioClip AudioButtonDown;
public AudioClip AudioButtonUp;
public Rigidbody Rigidbody { get; private set; }
protected virtual void Awake()
{
//for older projects that wouldn't have set this in the editor
if(StartPosition == Vector3.zero)
{
StartPosition = transform.localPosition;
}
Rigidbody = GetComponent<Rigidbody>();
}
private void FixedUpdate()
{
var distance = (StartPosition - transform.localPosition).magnitude;
if (!IsPressed && distance >= Threshold)
{
IsPressed = true;
OnButtonDown();
}
else if (IsPressed && distance < UpThreshold)
{
IsPressed = false;
OnButtonUp();
}
ClampBounds();
}
private void ClampBounds()
{
var test = new Vector3(transform.localPosition.x * Axis.x, transform.localPosition.y * Axis.y, transform.localPosition.z * Axis.z);
if (test.x > StartPosition.x || test.y > StartPosition.y || test.z > StartPosition.z)
{
transform.localPosition = StartPosition;
Rigidbody.velocity = Vector3.zero;
}
}
private void LateUpdate()
{
ClampBounds();
}
protected virtual void OnButtonDown()
{
if (AudioButtonDown)
if(SFXPlayer.Instance) SFXPlayer.Instance.PlaySFX(AudioButtonDown, transform.position);
ButtonDown.Invoke(this);
}
protected virtual void OnButtonUp()
{
if (AudioButtonUp)
if(SFXPlayer.Instance) SFXPlayer.Instance.PlaySFX(AudioButtonUp, transform.position);
ButtonUp.Invoke(this);
}
}
[Serializable]
public class VRButtonEvent : UnityEvent<HVRButton> { }
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 73f686e3f59dc0541a3a83773538b43f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,13 @@
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
/// <summary>
/// Tags a grabbable object as climbable which is then used by the player controller to know if they can climb or not.
/// For the hexabody integration it will kick in the climbing strength override set on the HexaHands component.
/// </summary>
public class HVRClimbable : MonoBehaviour
{
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0ff48744587821344b5cc14739c2d16c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,58 @@
using System;
using UnityEngine;
using UnityEngine.Events;
namespace HurricaneVR.Framework.Components
{
/// <summary>
/// Helper component used to propogate a collision if a force or velocity threshold was met.
/// </summary>
public class HVRCollisionEvents : MonoBehaviour
{
[Header("Settings")]
public CollisionEventType CollisionType = CollisionEventType.Impulse;
[Tooltip("Force threshold to breach to fire the ThresholdMet event")]
public float ForceThreshold;
[Tooltip("Collision velocity threshold to breach to fire the ThresholdMetEvent")]
public float VelocityThreshold;
public UnityEvent ThresholdMet = new UnityEvent();
[Header("Debug")]
public float LastImpulse;
public float LastVelocity;
public float MaxImpulse;
public float MaxVelocity;
protected virtual void OnCollisionEnter(Collision other)
{
LastImpulse = other.impulse.magnitude;
LastVelocity = other.relativeVelocity.magnitude;
MaxImpulse = Mathf.Max(MaxImpulse, LastImpulse);
MaxVelocity = Mathf.Max(MaxVelocity, LastVelocity);
var forceMet = LastImpulse > ForceThreshold;
var velocityMet = LastVelocity > VelocityThreshold;
if (CollisionType == CollisionEventType.Impulse && forceMet ||
CollisionType == CollisionEventType.Velocity && velocityMet ||
CollisionType == CollisionEventType.ImpulseOrVelocity && (forceMet || velocityMet))
{
ThresholdMet.Invoke();
}
}
}
[Serializable]
public enum CollisionEventType
{
Impulse, Velocity, ImpulseOrVelocity
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 123d9563fca44af1864835effb36ffbc
timeCreated: 1600399646

View File

@@ -0,0 +1,184 @@
using System;
using HurricaneVR.Framework.ControllerInput;
using HurricaneVR.Framework.Shared;
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
/// <summary>
/// Handles offsettings the physics hand target based on the connected device. Also offsets the target based on grab point settings if desired.
/// To manage custom offsets please use the SetMiscPositionOffset(Vector3 position, Vector3 rotation) and ResetGrabPointOffsets() functions.
/// </summary>
public class HVRControllerOffset : MonoBehaviour
{
/// <summary>
/// The hand that this controller offset represents.
/// </summary>
public HVRHandSide HandSide;
/// <summary>
/// The transform that aim's the teleport line, it will be counter rotated when a grabbable requests the hand to rotate when held.
/// </summary>
[Tooltip("The transform that aim's the teleport line, it will be counter rotated when a grabbable requests the hand to rotate when held.")]
public Transform Teleport;
/// <summary>
/// Smooth lerp speed to move towards the target grab point rotation offset
/// </summary>
[Tooltip("Smooth lerp speed to move towards the target grab point rotation offset")]
public float GrabPointSmoothSpeed = 8f;
private HVRDevicePoseOffset _offsets;
public Vector3 ControllerPositionOffset => _offsets != null ? _offsets.Position : Vector3.zero;
public Vector3 ControllerRotationOffset => _offsets != null ? _offsets.Rotation : Vector3.zero;
[Header("Debugging")]
[Tooltip("Enable to test live updating of the Misc offset fields.")]
public bool LiveUpdateOffsets;
[SerializeField] private Vector3 TargetGrabPointPositionOffset;
[SerializeField] private Vector3 TargetGrabPointRotationOffset;
[SerializeField] private Vector3 GrabPointPositionOffset;
[SerializeField] private Vector3 GrabPointRotationOffset;
[SerializeField] private Vector3 MiscPositionOffset;
[SerializeField] private Vector3 MiscRotationOffset;
private Quaternion _teleportStartRotation;
public bool _updatingRotation;
public bool _updatingPosition;
protected virtual void Awake()
{
if (Teleport)
{
_teleportStartRotation = Teleport.localRotation;
}
}
private void Start()
{
if (HandSide == HVRHandSide.Left)
{
if (HVRInputManager.Instance.LeftController)
ControllerConnected(HVRInputManager.Instance.LeftController);
HVRInputManager.Instance.LeftControllerConnected.AddListener(ControllerConnected);
}
else
{
if (HVRInputManager.Instance.RightController)
ControllerConnected(HVRInputManager.Instance.RightController);
HVRInputManager.Instance.RightControllerConnected.AddListener(ControllerConnected);
}
}
public void Update()
{
if (_updatingRotation)
{
GrabPointRotationOffset = Vector3.Lerp(GrabPointRotationOffset, TargetGrabPointRotationOffset, GrabPointSmoothSpeed * Time.deltaTime);
if (Vector3.Distance(GrabPointRotationOffset, TargetGrabPointRotationOffset) < .01f)
{
_updatingRotation = false;
GrabPointRotationOffset = TargetGrabPointRotationOffset;
ApplyOffsets();
}
}
if (_updatingPosition)
{
GrabPointPositionOffset = Vector3.Lerp(GrabPointPositionOffset, TargetGrabPointPositionOffset, GrabPointSmoothSpeed * Time.deltaTime);
if (Vector3.Distance(GrabPointPositionOffset, TargetGrabPointPositionOffset) < .01f)
{
_updatingPosition = false;
GrabPointPositionOffset = TargetGrabPointPositionOffset;
ApplyOffsets();
}
}
if (LiveUpdateOffsets || _updatingPosition || _updatingRotation)
{
ApplyOffsets();
}
}
public void SetMiscPositionOffset(Vector3 position, Vector3 rotation)
{
MiscPositionOffset = position;
MiscRotationOffset = rotation;
ApplyOffsets();
}
public void SetGrabPointOffsets(Vector3 position, Vector3 rotation)
{
TargetGrabPointPositionOffset = position;
TargetGrabPointRotationOffset = rotation;
_updatingRotation = true;
_updatingPosition = true;
}
public void ResetGrabPointOffsets()
{
TargetGrabPointPositionOffset = Vector3.zero;
TargetGrabPointRotationOffset = Vector3.zero;
_updatingRotation = true;
_updatingPosition = true;
}
public void ApplyOffsets()
{
var position = ControllerPositionOffset + GrabPointPositionOffset + MiscPositionOffset;
if (HandSide == HVRHandSide.Left)
{
position.x *= -1f;
}
transform.localPosition = position;
var controllerRotation = Quaternion.Euler(ControllerRotationOffset);
var grabPointRotation = Quaternion.Euler(GrabPointRotationOffset);
var miscRotation = Quaternion.Euler(MiscRotationOffset);
var finalRotation = controllerRotation * grabPointRotation * miscRotation;
var angles = finalRotation.eulerAngles;
if (HandSide == HVRHandSide.Left)
{
angles.y *= -1f;
angles.z *= -1f;
}
if (Teleport)
{
Teleport.localRotation = _teleportStartRotation * Quaternion.Inverse(grabPointRotation);
}
transform.localEulerAngles = angles;
}
private void ControllerConnected(HVRController controller)
{
var offsets = HVRInputManager.Instance.ControllerOffsets;
if (!offsets)
{
Debug.LogWarning($"HVRInputManager.ControllerOffsets are not assigned.");
return;
}
_offsets = offsets.GetDeviceOffset(controller.Side);
ApplyOffsets();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 59ef9bc5899042d588a09adb8a254aa9
timeCreated: 1601145324

View File

@@ -0,0 +1,146 @@
using System;
using System.Runtime.InteropServices.WindowsRuntime;
using HurricaneVR.Framework.ControllerInput;
using HurricaneVR.Framework.Shared;
using UnityEngine;
using UnityEngine.Serialization;
namespace HurricaneVR.Framework.Components
{
/// <summary>
/// Stores Unity SDK and Device offset combinations to handle the differences between SDK's and the position and rotation values they report.
/// </summary>
[CreateAssetMenu(menuName = "HurricaneVR/Controller Offsets", fileName = "ControllerOffset")]
public class HVRControllerOffsets : ScriptableObject
{
[Header("Unity XR")]
public HVRDevicePoseOffset Oculus;
public HVRDevicePoseOffset WMR;
public HVRDevicePoseOffset Pico;
[Header("SteamVR")]
public HVRDevicePoseOffset OculusSteamVR;
public HVRDevicePoseOffset WMRSteamVR;
public HVRDevicePoseOffset ReverbG2SteamVR;
public HVRDevicePoseOffset CosmosSteamVR;
public HVRDevicePoseOffset ViveSteamVR;
public HVRDevicePoseOffset KnucklesSteamVR;
[Header("OpenXR")]
public HVRDevicePoseOffset OculusOpenXR;
public HVRDevicePoseOffset WMROpenXR;
public HVRDevicePoseOffset ReverbG2OpenXR;
public HVRDevicePoseOffset ViveOpenXR;
public HVRDevicePoseOffset CosmosOpenXR;
public HVRDevicePoseOffset KnucklesOpenXR;
public HVRDevicePoseOffset PicoOpenXR;
public HVRDevicePoseOffset GetDeviceOffset(HVRHandSide side)
{
if (side == HVRHandSide.Left)
return GetDeviceOffset(HVRInputManager.Instance.LeftController.ControllerType);
return GetDeviceOffset(HVRInputManager.Instance.RightController.ControllerType);
}
public HVRDevicePoseOffset GetDeviceOffset(HVRControllerType type)
{
var steamVR = HVRInputManager.Instance.IsSteamVR;
var openXr = HVRInputManager.Instance.IsOpenXR;
switch (type)
{
case HVRControllerType.Oculus:
if (steamVR)
{
return OculusSteamVR;
}
else if (openXr)
{
return OculusOpenXR;
}
else
{
return Oculus;
}
case HVRControllerType.WMR:
if (steamVR)
{
return WMRSteamVR;
}
else if (openXr)
{
return WMROpenXR;
}
else
{
return WMR;
}
case HVRControllerType.Vive:
if (steamVR)
{
return ViveSteamVR;
}
else if (openXr)
{
return ViveOpenXR;
}
break;
case HVRControllerType.Knuckles:
if (steamVR)
{
return KnucklesSteamVR;
}
else if (openXr)
{
return KnucklesOpenXR;
}
break;
case HVRControllerType.Cosmos:
if (steamVR)
{
return CosmosSteamVR;
}
else if (openXr)
{
return CosmosOpenXR;
}
break;
case HVRControllerType.ReverbG2:
if (steamVR)
{
return ReverbG2SteamVR;
}
else if (openXr)
{
return ReverbG2OpenXR;
}
break;
case HVRControllerType.Pico:
if (steamVR) //is there a way to detect pico when using steamvr? it's emulated as a oculus device
return null;
if (openXr)
return PicoOpenXR;
return Pico;
}
return null;
}
}
[Serializable]
public class HVRDevicePoseOffset
{
public Vector3 Position;
[FormerlySerializedAs("_rotation")]
[SerializeField]
public Vector3 Rotation;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ab72751261f74e5ebaa6986a0464cd15
timeCreated: 1635035319

View File

@@ -0,0 +1,50 @@
using HurricaneVR.Framework.Weapons;
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
public class HVRDamageHandler : HVRDamageHandlerBase
{
public float Life = 100f;
public bool Damageable = true;
public Rigidbody Rigidbody { get; private set; }
public HVRDestructible Desctructible;
void Start()
{
Rigidbody = GetComponent<Rigidbody>();
if (!Desctructible)
Desctructible = GetComponent<HVRDestructible>();
}
public override void TakeDamage(float damage)
{
if (Damageable)
{
Life -= damage;
}
if (Life <= 0)
{
if (Desctructible)
{
Desctructible.Destroy();
}
}
}
public override void HandleDamageProvider(HVRDamageProvider damageProvider, Vector3 hitPoint, Vector3 direction)
{
base.HandleDamageProvider(damageProvider, hitPoint, direction);
if (Rigidbody)
{
Rigidbody.AddForceAtPosition(direction.normalized * damageProvider.Force, hitPoint, ForceMode.Impulse);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d2fba50691c4c804ea3f345eb904cc95
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,21 @@
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
public abstract class HVRDamageHandlerBase : MonoBehaviour
{
public virtual void TakeDamage(float damage)
{
}
public virtual void HandleDamageProvider(HVRDamageProvider damageProvider, Vector3 hitPoint, Vector3 direction)
{
TakeDamage(damageProvider.Damage);
}
public virtual void HandleRayCastHit(HVRDamageProvider damageProvider, RaycastHit hit)
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e49c8bf2db256ba438a090d0d46c85ba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,19 @@
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
public class HVRDamageProvider : MonoBehaviour
{
public float Damage = 25f;
public float Force;
[Tooltip("Player transform for ai frameworks like emerald ai")]
public Transform Player;
protected virtual void Start()
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 029f5fabd456e3d4ca818afd570d575d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,26 @@
using System;
using UnityEngine;
using UnityEngine.Events;
namespace HurricaneVR.Framework.Components
{
/// <summary>
/// Publishes the destroyed event so others may know, used by the hand grabber to know if the held object is destroyed
/// </summary>
public class HVRDestroyListener : MonoBehaviour
{
public HVRDestroyedEvent Destroyed = new HVRDestroyedEvent();
private void OnDestroy()
{
Destroyed.Invoke(this);
Destroyed.RemoveAllListeners();
}
}
[Serializable]
public class HVRDestroyedEvent : UnityEvent<HVRDestroyListener>
{
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1d73b26c803e4bbb886b5776121804b2
timeCreated: 1600231143

View File

@@ -0,0 +1,22 @@
using System.Collections;
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
/// <summary>
/// Destroys this game object after a timeout
/// </summary>
public class HVRDestroyTimer : MonoBehaviour
{
public void StartTimer(float timeout)
{
StartCoroutine(Cleanup(timeout));
}
private IEnumerator Cleanup(float timeout)
{
yield return new WaitForSeconds(timeout);
Destroy(this.gameObject);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6751180565d247fba4285903a5b605aa
timeCreated: 1601087539

View File

@@ -0,0 +1,75 @@
using HurricaneVR.Framework.Core;
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
/// <summary>
/// Helper component that spawns a prefab game object when the Destroy function is called.
/// If the spawned game object has a rigidbodies then they will have force added to them based on the
/// fields provided.
/// </summary>
public class HVRDestructible : MonoBehaviour
{
public GameObject DestroyedVersion;
public float ExplosionRadius = .1f;
public float ExplosionPower = 1;
public float ExplosionUpwardsPower = 1;
public bool RemoveDebris = true;
public float RemoveDebrisTimerUpper = 10f;
public float RemoveDebrisTimerLower = 5f;
public bool IgnorePlayerCollision = true;
public bool Destroyed { get; protected set; }
public virtual void Destroy()
{
if (Destroyed) return;
if (DestroyedVersion)
{
var destroyed = Instantiate(DestroyedVersion, transform.position, transform.rotation);
foreach (var rigidBody in destroyed.GetComponentsInChildren<Rigidbody>())
{
var v = new Vector3(Random.Range(-1f, 1f), Random.Range(-1f, 1f), Random.Range(-1f, 1f));
rigidBody.AddForce(v * ExplosionPower, ForceMode.VelocityChange);
if (RemoveDebris)
{
var delay = Random.Range(RemoveDebrisTimerLower, RemoveDebrisTimerUpper);
if (delay < .1f)
{
delay = 3f;
}
var timer = rigidBody.gameObject.AddComponent<HVRDestroyTimer>();
timer.StartTimer(delay);
}
if (IgnorePlayerCollision)
{
var colliders = rigidBody.gameObject.GetComponentsInChildren<Collider>();
HVRManager.Instance?.IgnorePlayerCollision(colliders);
}
rigidBody.transform.parent = null;
}
if (RemoveDebris)
{
var timer = destroyed.gameObject.AddComponent<HVRDestroyTimer>();
var delay = RemoveDebrisTimerUpper;
if (delay <= .1f)
delay = 3f;
timer.StartTimer(delay);
}
}
Destroyed = true;
Destroy(gameObject);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 66f0a0ab295247a681b1b569944a2bf9
timeCreated: 1599957568

View File

@@ -0,0 +1,244 @@
using HurricaneVR.Framework.Core;
using HurricaneVR.Framework.Core.Grabbers;
using HurricaneVR.Framework.Core.Utils;
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
/// <summary>
/// Deprecated and left in for older projects. Superceded by HVRPhysicsDial and HVRRotationTracker
/// </summary>
[RequireComponent(typeof(HVRGrabbable))]
public class HVRDial : MonoBehaviour
{
public DialSteppedEvent DialStepChanged = new DialSteppedEvent();
public DialTurnedEvent DialTurned = new DialTurnedEvent();
public Transform RotationTarget;
public int Steps;
public bool SnapTarget = true;
public bool ClampMaxAngle;
public float MaximumAngle = 360f;
public AudioClip AudioClip;
public float Deadzone = 1f;
public Vector3 LocalRotationAxis;
public Vector3 LocalAxisStart;
public bool DiscardAngle;
public float StepSize;
private float _previousAngle;
private Vector3 _originalVector;
private Rigidbody _rigidBody;
private Rigidbody _targetRigidBody;
private float _previousAngleFromStart;
public int Step { get; protected set; }
public HVRGrabbable Grabbable { get; private set; }
public HVRHandGrabber PrimaryGrabber { get; private set; }
protected virtual void Start()
{
_rigidBody = transform.GetComponent<Rigidbody>();
_targetRigidBody = RotationTarget.GetComponent<Rigidbody>();
if (Steps <= 1)
StepSize = 0f;
else if (Steps > 1 && Mathf.Approximately(StepSize, 0f))
StepSize = MaximumAngle / Steps;
LocalAxisStart.Normalize();
LocalRotationAxis.Normalize();
_originalVector = transform.localRotation * LocalAxisStart;
Grabbable = GetComponent<HVRGrabbable>();
Grabbable.Grabbed.AddListener(OnGrabbed);
Grabbable.Released.AddListener(OnReleased);
_previousAngle = 0f;
_previousAngleFromStart = 0f;
}
protected virtual void FixedUpdate()
{
//Debug.DrawLine(transform.position, transform.position + transform.TransformDirection(currentVector).normalized * 1f, Color.red);
CheckForRotation();
}
protected void CheckForRotation(bool force = false)
{
if (force || PrimaryGrabber)
{
if (!force && Mathf.Abs(PrimaryGrabber.HVRTrackedController.DeltaEulerZ) < Deadzone)
return;
var currentVector = transform.localRotation * LocalAxisStart;
var rotationAxis = LocalRotationAxis;
var controllerRotation = new Vector3(
PrimaryGrabber.HVRTrackedController.DeltaEulerZ,
PrimaryGrabber.HVRTrackedController.DeltaEulerZ,
PrimaryGrabber.HVRTrackedController.DeltaEulerZ);
controllerRotation.Scale(-rotationAxis);
var rotation = Quaternion.Euler(controllerRotation);
var newRotation = transform.localRotation * rotation;
var newVector = newRotation * LocalAxisStart;
var delta = -PrimaryGrabber.HVRTrackedController.DeltaEulerZ;
var angleFromStart = Vector3.SignedAngle(_originalVector, newVector, rotationAxis);
if (angleFromStart < 0)
angleFromStart = 360 + angleFromStart;
var clockWise = delta >= 0f;
if (ClampMaxAngle)
{
if (clockWise && _previousAngleFromStart + delta >= MaximumAngle - .1)
{
angleFromStart = MaximumAngle;
}
else if (!clockWise && _previousAngleFromStart + delta <= 0.1)
{
angleFromStart = 0;
}
else if (angleFromStart > MaximumAngle)
{
float upDiff = 360 - angleFromStart;
float lowerDiff = angleFromStart - MaximumAngle;
if (upDiff < lowerDiff) angleFromStart = 0;
else angleFromStart = MaximumAngle;
}
}
var stepAngle = angleFromStart;
if (Steps > 1)
{
Step = Mathf.RoundToInt(angleFromStart / StepSize);
stepAngle = Step * StepSize;
if (Mathf.Approximately(stepAngle, 360))
{
Step = 0;
}
}
var finalVector = Quaternion.AngleAxis(angleFromStart, rotationAxis) * _originalVector;
var syncAngle = Vector3.SignedAngle(currentVector, finalVector, rotationAxis);
var syncRotation = Quaternion.AngleAxis(syncAngle, rotationAxis) * transform.localRotation;
if (Steps > 1)
{
if (!Mathf.Approximately(stepAngle, _previousAngle))
{
OnStepChanged(Step, true);
_previousAngle = stepAngle;
if (SnapTarget)
{
var percent = Mathf.Approximately(0f, MaximumAngle) ? 0f : stepAngle / MaximumAngle;
OnAngleChanged(stepAngle, syncAngle, percent, true);
}
}
}
if (!SnapTarget && !Mathf.Approximately(_previousAngleFromStart, angleFromStart))
{
var percent = Mathf.Approximately(0f, MaximumAngle) ? 0f : angleFromStart / MaximumAngle;
OnAngleChanged(angleFromStart, syncAngle, angleFromStart / percent, true);
}
var steppedVector = Quaternion.AngleAxis(stepAngle, rotationAxis) * _originalVector;
var steppedAngle = Vector3.SignedAngle(currentVector, steppedVector, rotationAxis);
var steppedRotation = Quaternion.AngleAxis(steppedAngle, rotationAxis) * transform.localRotation;
var targetRotation = SnapTarget ? steppedRotation : syncRotation;
//if (_targetRigidBody)
//{
// _targetRigidBody.MoveRotation(_targetRigidBody.rotation * targetRotation);
//}
//else
{
RotationTarget.localRotation = targetRotation;
}
//if (_rigidBody)
//{
// _rigidBody.MoveRotation(_rigidBody.rotation * syncRotation);
//}
//else
{
transform.localRotation = syncRotation;
}
_previousAngleFromStart = angleFromStart;
}
}
protected void OnGrabbed(HVRGrabberBase grabber, HVRGrabbable hvrGrabbable)
{
PrimaryGrabber = grabber as HVRHandGrabber;
if (DiscardAngle)
{
if (_rigidBody)
{
_rigidBody.MoveRotation(RotationTarget.rotation);
}
else
{
transform.localRotation = RotationTarget.localRotation;
}
}
}
protected void OnReleased(HVRGrabberBase grabber, HVRGrabbable hvrGrabbable)
{
PrimaryGrabber = null;
if (DiscardAngle)
{
if (_rigidBody)
{
_rigidBody.MoveRotation(RotationTarget.rotation);
}
else
{
transform.localRotation = RotationTarget.localRotation;
}
}
}
protected virtual void OnStepChanged(int step, bool raiseEvents)
{
if (AudioClip)
if(SFXPlayer.Instance) SFXPlayer.Instance.PlaySFXRandomPitch(AudioClip, transform.position, .9f, 1.1f);
if (raiseEvents)
DialStepChanged.Invoke(step);
}
protected virtual void OnAngleChanged(float angle, float delta, float percent, bool raiseEvents)
{
if (raiseEvents)
DialTurned.Invoke(angle, delta, percent);
}
protected virtual void Update()
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a95f97577cc01a746a9d0a3e11b90b8c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,12 @@
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
public class HVRDontDestroy : MonoBehaviour
{
private void Awake()
{
DontDestroyOnLoad(gameObject);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2e5b279a1a044cee9d64e0ca5d85f175
timeCreated: 1601039791

View File

@@ -0,0 +1,34 @@
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
public abstract class HVRGrabbableHoverBase : MonoBehaviour
{
public HVRHoverPosition HoverPosition = HVRHoverPosition.GrabPoint;
public bool LookAtCamera = true;
protected virtual void Start()
{
}
protected virtual void Update()
{
}
public abstract void Hover();
public abstract void Unhover();
public abstract void Enable();
public abstract void Disable();
}
public enum HVRHoverPosition
{
GrabPoint,
Self,
Transform
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6c25cecd1cd34cd7a4346c955a0add93
timeCreated: 1600204770

View File

@@ -0,0 +1,28 @@
using HurricaneVR.Framework.Core;
namespace HurricaneVR.Framework.Components
{
public class HVRGrabbableImpactHaptics : HVRImpactHapticsBase
{
public HVRGrabbable Grabbable;
protected override void Awake()
{
base.Awake();
if (!Grabbable) TryGetComponent(out Grabbable);
}
protected override void Vibrate(float duration, float amplitude, float frequency)
{
for (var i = 0; i < Grabbable.HandGrabbers.Count; i++)
{
var h = Grabbable.HandGrabbers[i];
if (!h.IsMine) break;
h.Controller.Vibrate(amplitude, Data.Duration, Data.Frequency);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 77dbe74f0338a6c438b0af580dd1253e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,48 @@
using HurricaneVR.Framework.Core.Grabbers;
using HurricaneVR.Framework.Core.Player;
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
public class HVRHandImpactHaptics : HVRImpactHapticsBase
{
public HVRHandGrabber Hand;
[Tooltip("If true and the hand is holding something, haptics will not play.")]
public bool HandGrabbingPrevents = true;
[Tooltip("If true the max force for the haptic amplitude calc will use the hand's default max force.")]
public bool UseHandMaxStrength = true;
private HVRJointHand _hand;
public override float MaxForce
{
get
{
if (UseHandMaxStrength)
{
if ( _hand && _hand.JointSettings)
return _hand.JointSettings.XDrive.MaxForce;
}
return base.MaxForce;
}
}
protected override void Awake()
{
base.Awake();
if (!Hand) TryGetComponent(out Hand);
TryGetComponent(out _hand);
}
protected override void Vibrate(float duration, float amplitude, float frequency)
{
if (HandGrabbingPrevents && Hand.IsGrabbing) return;
Hand.Controller.Vibrate(amplitude, duration, frequency);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 735256ec9b08e024a8efea6924dd44b5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,178 @@
using System;
using System.Collections;
using HurricaneVR.Framework.Core;
using HurricaneVR.Framework.Core.HandPoser;
using HurricaneVR.Framework.Shared;
using UnityEngine;
#if ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem;
#endif
namespace HurricaneVR.Framework.Components
{
public class HVRHandPoseRecorder : MonoBehaviour
{
#if ENABLE_LEGACY_INPUT_MANAGER
public KeyCode LeftHandSaveKey = KeyCode.L;
public KeyCode RightHandSaveKey = KeyCode.R;
#endif
#if ENABLE_INPUT_SYSTEM
public Key LeftSaveKey = Key.L;
public Key RightSaveKey = Key.R;
#endif
public HVRPosableHand LeftHand;
public HVRPosableHand RightHand;
public HVRHandPhysics LeftPhysics;
public HVRHandPhysics RightPhysics;
public float FadeTimer = 10f;
public bool RemoveClones = true;
public bool DisablePhysics;
private bool _previousDisable;
public string Folder;
public int Counter = 0;
public void Start()
{
Folder = DateTime.Now.ToString("yyyyMMdd_HH_mm");
}
#if UNITY_EDITOR
void Update()
{
if (DisablePhysics && !_previousDisable)
{
if (LeftPhysics)
{
LeftPhysics.DisableCollision();
}
if (RightPhysics)
{
RightPhysics.DisableCollision();
}
}
else if (!DisablePhysics && _previousDisable)
{
if (LeftPhysics)
{
LeftPhysics.EnableCollision();
}
if (RightPhysics)
{
RightPhysics.EnableCollision();
}
}
_previousDisable = DisablePhysics;
CheckSnapshot();
}
#endif
private void CheckSnapshot()
{
HVRPosableHand hand = null;
#if ENABLE_LEGACY_INPUT_MANAGER
if (Input.GetKeyDown(LeftHandSaveKey))
{
hand = LeftHand;
}
else if (Input.GetKeyDown(RightHandSaveKey))
{
hand = RightHand;
}
else
return;
#elif ENABLE_INPUT_SYSTEM
if (Keyboard.current[LeftSaveKey].wasPressedThisFrame)
{
hand = LeftHand;
}
else if (Keyboard.current[RightSaveKey].wasPressedThisFrame)
{
hand = RightHand;
}
else
return;
#endif
if (!hand)
return;
Snapshot(hand);
}
public void SnapshotLeft()
{
if (!gameObject.activeSelf)
return;
if (LeftHand)
{
Snapshot(LeftHand);
}
}
public void SnapshotRight()
{
if (!gameObject.activeSelf)
return;
if (RightHand)
{
Snapshot(RightHand);
}
}
private void Snapshot(HVRPosableHand hand)
{
#if UNITY_EDITOR
var pose = hand.CreateFullHandPoseWorld(hand.MirrorAxis);
HVRSettings.Instance.SaveRunTimePose(pose, Counter++.ToString(), Folder);
var clone = Instantiate(HVRSettings.Instance.GetPoserHand(hand.Side));
var posableHand = clone.GetComponent<HVRPosableHand>();
if (posableHand != null)
{
posableHand.Pose(pose.GetPose(hand.Side));
clone.transform.position = hand.transform.position;
clone.transform.rotation = hand.transform.rotation;
}
var colliders = clone.GetComponentsInChildren<Collider>();
foreach (var c in colliders)
{
c.enabled = false;
}
if (RemoveClones)
{
StartCoroutine(RemoveClone(clone));
}
#endif
}
public IEnumerator RemoveClone(GameObject clone)
{
yield return new WaitForSeconds(FadeTimer);
Destroy(clone);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 60ac0a61a75ecf64e8c54dcb82a08821
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,57 @@

using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace HurricaneVR.Framework.Components
{
[CreateAssetMenu(menuName = "HurricaneVR/Impact Haptics", fileName = "ImpactHaptics")]
public class HVRImpactHaptics : ScriptableObject
{
public float Timeout = .3f;
[Tooltip("Collision Velocity Square Magnitude cut off filter")]
public float SqrMagThreshold = .30f;
[Tooltip("Collision force divided by this and fed into the AmpCurve. ")]
public float MaxForce = 300f;
public AnimationCurve AmpCurve;
[Tooltip("Duration fed into haptics API.")]
public float Duration = .25f;
[Tooltip("Frequency fed into haptics API")]
public float Frequency = 150f;
public void Reset()
{
Frequency = 150f;
Duration = .25f;
MaxForce = 600f;
SqrMagThreshold = .30f;
Timeout = .3f;
AmpCurve = new AnimationCurve(new Keyframe(0f, .35f, 0f, 0f, 0f, 0f)
{
weightedMode = WeightedMode.None
}, new Keyframe(1f, 1f, 2f, 2f, 0f, 0f)
{
weightedMode = WeightedMode.None
});
}
#if UNITY_EDITOR
//unity bugged Reset conext menu on some versions
[MenuItem("CONTEXT/ImpactHaptics/Reset (Custom)")]
static void CReset(MenuCommand command)
{
(command.context as HVRImpactHaptics)?.Reset();
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b775075551dfdd947bebcfce71c1a7c1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,67 @@
using System.Collections;
using System.Collections.Generic;
using HurricaneVR.Framework.Core;
using HurricaneVR.Framework.Core.Grabbers;
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
public abstract class HVRImpactHapticsBase : MonoBehaviour
{
public HVRImpactHaptics Data;
[Header("Debug")]
public float Force;
private float _lastHaptic;
private Rigidbody Rb;
public virtual float MaxForce => Data.MaxForce;
protected virtual void Awake()
{
TryGetComponent(out Rb);
if (!Data)
{
Data = ScriptableObject.CreateInstance<HVRImpactHaptics>();
Data.Reset();
}
}
protected virtual void OnCollisionEnter(Collision other)
{
if (!Rb) TryGetComponent(out Rb);
if (!Rb || Rb.velocity.sqrMagnitude < Data.SqrMagThreshold)
return;
if (Time.realtimeSinceStartup - _lastHaptic < Data.Timeout)
return;
float impulse;
if (other.impulse.sqrMagnitude > 0f)
{
impulse = other.impulse.magnitude;
}
else
{
impulse = other.relativeVelocity.magnitude * Rb.mass;
}
Force = impulse / Time.fixedDeltaTime;
var amp = Data.AmpCurve.Evaluate(Force / MaxForce);
Vibrate(Data.Duration, amp, Data.Frequency);
_lastHaptic = Time.realtimeSinceStartup;
}
protected virtual void Vibrate(float duration, float amplitude, float frequency)
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 38a18e75cd9699f428ac9902c039394c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,246 @@
using HurricaneVR.Framework.Core;
using HurricaneVR.Framework.Core.Grabbers;
using HurricaneVR.Framework.Core.Utils;
using HurricaneVR.Framework.Shared;
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
/// <summary>
/// Depecrecated and left in for older projects, superceded by HVRPhysicsLever and HVRRotationTracker
/// </summary>
[RequireComponent(typeof(HVRGrabbable))]
[RequireComponent(typeof(HingeJoint))]
public class HVRLever : MonoBehaviour
{
[Header("Transforms")]
[Tooltip("Target transform to rotate if the visual handle is not this object")]
public Transform RotationTarget;
[Tooltip("Used to track the delta angle change, use with TrackerAxis.")]
public Transform Tracker;
[Header("Settings")]
[Tooltip("When the lever is released with Snap mode, should the grabbable handle snap to the RotationTarget")]
public bool DiscardAngle;
[Tooltip("Amount of steps the lever will click")]
public int Steps;
[Tooltip("Rotation Target snaps to the step angle size")]
public bool SnapTarget = true;
[Tooltip("Angle offset the lever starts in relation to LocalAxisStart field")]
public float Offset;
[Tooltip("Vector of the starting 0 angle of the lever.")]
public Vector3 LocalAxisStart;
[Tooltip("Choose an axis that isn't the same as the hinge axis")]
public Vector3 TrackerAxis = new Vector3(0, 1, 0);
[Tooltip("SFX to play whenever the lever snaps")]
public AudioClip AudioClip;
protected HingeJoint HingeJoint;
[Header("Debug")]
public float MinAngle;
public float MaxAngle;
public Vector3 LocalRotationAxis;
public float StepSize;
public int CurrentStep;
public float CurrentAngle;
public LeverMovedEvent Moved = new LeverMovedEvent();
public LeverSteppedEvent Stepped = new LeverSteppedEvent();
private float _previousAngle;
private Vector3 _previousVector;
private Vector3 _originalVector;
private float _previousAngleFromStart;
public HVRGrabbable Grabbable { get; private set; }
public Rigidbody Rigidbody => Grabbable.Rigidbody;
public HVRGrabberBase PrimaryGrabber => Grabbable.PrimaryGrabber;
public float Range { get; set; }
protected void Start()
{
if (!RotationTarget)
RotationTarget = transform;
Grabbable = GetComponent<HVRGrabbable>();
HingeJoint = GetComponent<HingeJoint>();
MinAngle = HingeJoint.limits.min;
MaxAngle = HingeJoint.limits.max;
Range = MaxAngle - MinAngle;
LocalRotationAxis = HingeJoint.axis;
if (Steps <= 1)
{
StepSize = 0f;
}
else if (Steps > 1 && Mathf.Approximately(StepSize, 0f))
{
StepSize = Range / Steps;
}
_originalVector = transform.TransformDirection(LocalAxisStart);
_previousVector = LocalAxisStart;
SetupStartRotation();
transform.rotation = RotationTarget.rotation;
Grabbable.Grabbed.AddListener(OnGrabbed);
Grabbable.Released.AddListener(OnReleased);
}
protected virtual void FixedUpdate()
{
CheckForRotation();
}
private void CheckForRotation(bool force = false)
{
if (!force && Rigidbody.isKinematic && !PrimaryGrabber)
return;
var currentVector = Tracker.TransformDirection(TrackerAxis);
var worldRotationAxis = transform.TransformDirection(LocalRotationAxis);
var angleFromStart = Vector3.SignedAngle(_originalVector, currentVector, worldRotationAxis);
angleFromStart += Offset;
if ((int)angleFromStart < 0)
angleFromStart = 360 + angleFromStart;
var deltaAngle = Vector3.SignedAngle(_previousVector, currentVector, worldRotationAxis);
CurrentAngle = angleFromStart;
var stepAngle = angleFromStart;
if (Steps > 1)
{
CurrentStep = Mathf.RoundToInt(angleFromStart / StepSize);
stepAngle = CurrentStep * StepSize;
if (!Mathf.Approximately(stepAngle, _previousAngle))
{
OnStepChanged(CurrentStep);
_previousAngle = stepAngle;
if (SnapTarget)
{
var percent = Mathf.Approximately(0f, Range) ? 0f : stepAngle / Range;
OnAngleChanged(stepAngle, deltaAngle, percent);
}
}
}
_previousVector = currentVector;
var steppedVector = Quaternion.AngleAxis(stepAngle - Offset, worldRotationAxis) * _originalVector;
var steppedAngle = Vector3.SignedAngle(currentVector, steppedVector, worldRotationAxis);
var steppedRotation = Quaternion.AngleAxis(steppedAngle, worldRotationAxis) * transform.rotation;
if (!SnapTarget && !Mathf.Approximately(_previousAngleFromStart, angleFromStart))
{
var percent = Mathf.Approximately(0f, Range) ? 0f : angleFromStart / Range;
OnAngleChanged(angleFromStart, deltaAngle, percent);
}
if (SnapTarget)
{
RotationTarget.rotation = steppedRotation;
}
else
{
RotationTarget.rotation = transform.rotation;
}
_previousAngleFromStart = angleFromStart;
}
private void SetupStartRotation()
{
var currentVector = transform.TransformDirection(TrackerAxis);
var worldRotationAxis = transform.TransformDirection(LocalRotationAxis);
var angleFromStart = Vector3.SignedAngle(_originalVector, currentVector, worldRotationAxis);
angleFromStart += Offset;
if ((int)angleFromStart < 0)
angleFromStart = 360 + angleFromStart;
CurrentAngle = angleFromStart;
var stepAngle = angleFromStart;
if (Steps > 1)
{
CurrentStep = Mathf.RoundToInt(angleFromStart / StepSize);
stepAngle = CurrentStep * StepSize;
if (!Mathf.Approximately(stepAngle, _previousAngle))
{
_previousAngle = stepAngle;
}
}
_previousVector = currentVector;
var steppedVector = Quaternion.AngleAxis(stepAngle - Offset, worldRotationAxis) * _originalVector;
var steppedAngle = Vector3.SignedAngle(currentVector, steppedVector, worldRotationAxis);
var steppedRotation = Quaternion.AngleAxis(steppedAngle, worldRotationAxis) * transform.rotation;
if (SnapTarget)
{
RotationTarget.rotation = steppedRotation;
}
else
{
RotationTarget.rotation = transform.rotation;
}
_previousAngleFromStart = angleFromStart;
}
protected void OnGrabbed(HVRGrabberBase grabber, HVRGrabbable hvrGrabbable)
{
if (DiscardAngle)
{
transform.rotation = RotationTarget.rotation;
transform.position = RotationTarget.position;
}
}
protected void OnReleased(HVRGrabberBase grabber, HVRGrabbable hvrGrabbable)
{
if (DiscardAngle)
{
transform.rotation = RotationTarget.rotation;
transform.position = RotationTarget.position;
}
}
protected virtual void OnStepChanged(int step, bool raiseEvents = true)
{
if (AudioClip)
if(SFXPlayer.Instance) SFXPlayer.Instance.PlaySFXRandomPitch(AudioClip, transform.position, .9f, 1.1f);
if (raiseEvents)
Stepped.Invoke(step);
}
protected virtual void OnAngleChanged(float angle, float delta, float percent, bool raiseEvents = true)
{
if (raiseEvents)
Moved.Invoke(angle, delta, percent);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4e395cf0cf494656baa6b1eddceceb24
timeCreated: 1600542387

View File

@@ -0,0 +1,72 @@
using System.Collections.Generic;
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
public class HVRMeshGlowHighlight : HVRGrabbableHoverBase
{
private readonly HashSet<GameObject> _highlightObjects = new HashSet<GameObject>();
private readonly HashSet<MeshRenderer> _renderers = new HashSet<MeshRenderer>();
void Awake()
{
var meshes = GetComponentsInChildren<MeshFilter>();
foreach (var mesh in meshes)
{
var go = mesh.gameObject;
var meshRenderer = go.GetComponent<MeshRenderer>();
if (meshRenderer)
{
_renderers.Add(meshRenderer);
}
var highlightObj = new GameObject("HighlightMesh");
highlightObj.transform.SetParent(go.transform, false);
var clone = highlightObj.AddComponent<MeshFilter>();
clone.sharedMesh = mesh.sharedMesh;
var newRenderer = highlightObj.AddComponent<MeshRenderer>();
newRenderer.sharedMaterial = Resources.Load<Material>("GrabbableHighlight");
_highlightObjects.Add(highlightObj);
highlightObj.SetActive(false);
}
}
public override void Hover()
{
foreach (var highlight in _highlightObjects)
{
highlight.SetActive(true);
}
foreach (var r in _renderers)
{
r.enabled = false;
}
}
public override void Unhover()
{
foreach (var highlight in _highlightObjects)
{
highlight.SetActive(false);
}
foreach (var r in _renderers)
{
r.enabled = true;
}
}
public override void Enable()
{
}
public override void Disable()
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4e7c9a97934caec4897562447669e7e7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,38 @@
using HurricaneVR.Framework.Shared;
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
[RequireComponent(typeof(MeshRenderer))]
public class HVRMeshRendererHighlight : HVRGrabbableHoverBase
{
public MeshRenderer Renderer { get; private set; }
protected override void Start()
{
base.Start();
Renderer = GetComponent<MeshRenderer>();
}
public override void Hover()
{
Renderer.enabled = true;
}
public override void Unhover()
{
Renderer.enabled = false;
}
public override void Enable()
{
Renderer.enabled = true;
}
public override void Disable()
{
Renderer.enabled = false;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 86ae87a96c3b7dd4c8c15912e79812f9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,21 @@
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
/// <summary>
/// Helper component to parent this transform to the assigned transform on game start
/// </summary>
public class HVRParentOnStart : MonoBehaviour
{
public Transform Parent;
public bool WorldPositionStays = true;
private void Start()
{
if (Parent)
{
transform.SetParent(Parent, WorldPositionStays);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b06dd533ee37487db0603f3b328f972f
timeCreated: 1602523768

View File

@@ -0,0 +1,197 @@
using System;
using HurricaneVR.Framework.Core.ScriptableObjects;
using HurricaneVR.Framework.Core.Utils;
using HurricaneVR.Framework.Shared;
using UnityEngine;
using UnityEngine.Events;
namespace HurricaneVR.Framework.Components
{
/// <summary>
/// Creates a joint constrained along one axis with limits based on start and end points defined in the inspector editor.
/// </summary>
[RequireComponent(typeof(Rigidbody))]
public class HVRPhysicsButton : MonoBehaviour
{
[Header("Settings")]
[Tooltip("Axis the button will travel on in local space.")]
public HVRAxis Axis;
[Tooltip("Rigidbody this button will connect to with a joint.")]
public Rigidbody ConnectedBody;
[Tooltip("Spring value of the joint")]
public float Spring = 1000f;
[Tooltip("Damper of the joint")]
public float Damper = 50f;
[Header("Button Positions")]
[Tooltip("How far the button must travel to become pressed.")]
public float DownThreshold;
[Tooltip("Threshold to hit on the return to allow the button to be pressed again.")]
public float ResetThreshold;
[Tooltip("The resting position of the button")]
public Vector3 StartPosition;
[Tooltip("Furthest position the button can travel")]
public Vector3 EndPosition;
[Header("SFX")]
public AudioClip SFXButtonDown;
public AudioClip SFXButtonUp;
public HVRButtonEvent ButtonDown = new HVRButtonEvent();
public HVRButtonEvent ButtonUp = new HVRButtonEvent();
[Header("Debug")]
public bool IsPressed = false;
public bool InvokeButtonDown;
public bool UpdateSpring;
public Rigidbody Rigidbody { get; private set; }
private Vector3 _axis;
private ConfigurableJoint _joint;
private ConfigurableJoint _limitJoint;
protected virtual void Awake()
{
transform.localPosition = StartPosition;
Rigidbody = GetComponent<Rigidbody>();
_axis = Axis.GetVector();
Rigidbody.useGravity = false;
SetupJoint();
}
private void SetupJoint()
{
var worldStartPosition = StartPosition;
if (transform.parent)
worldStartPosition = transform.parent.TransformPoint(StartPosition);
var worldEndPosition = EndPosition;
if (transform.parent)
worldEndPosition = transform.parent.TransformPoint(EndPosition);
_joint = gameObject.AddComponent<ConfigurableJoint>();
_joint.autoConfigureConnectedAnchor = false;
_joint.connectedBody = ConnectedBody;
_joint.anchor = Vector3.zero;
if (ConnectedBody)
{
_joint.connectedAnchor = ConnectedBody.transform.InverseTransformPoint(worldStartPosition);
}
else
{
_joint.connectedAnchor = worldStartPosition;
}
_joint.SetXDrive(Spring, Damper, Spring);
_joint.LimitXMotion();
_joint.LockYMotion();
_joint.LockZMotion();
_joint.LockAllAngularMotion();
_joint.axis = _axis;
_joint.secondaryAxis = _joint.axis.OrthogonalVector();
_joint.SetLinearLimit(Vector3.Distance(StartPosition, EndPosition));
_limitJoint = gameObject.AddComponent<ConfigurableJoint>();
_limitJoint.autoConfigureConnectedAnchor = false;
_limitJoint.connectedBody = ConnectedBody;
_limitJoint.anchor = Vector3.zero;
if (ConnectedBody)
{
_limitJoint.connectedAnchor = ConnectedBody.transform.InverseTransformPoint(worldEndPosition);
}
else
{
_limitJoint.connectedAnchor = worldEndPosition;
}
_limitJoint.LockYMotion();
_limitJoint.LockZMotion();
_limitJoint.LockAllAngularMotion();
_limitJoint.axis = _axis;
_limitJoint.secondaryAxis = _joint.axis.OrthogonalVector();
_limitJoint.LimitXMotion();
_limitJoint.SetLinearLimit(Vector3.Distance(StartPosition, EndPosition));
}
private void FixedUpdate()
{
if (UpdateSpring)
{
_joint.SetXDrive(Spring, Damper, Spring);
UpdateSpring = false;
}
var distance = Mathf.Abs(GetDistance());
if (!IsPressed && distance >= DownThreshold || InvokeButtonDown)
{
InvokeButtonDown = false;
IsPressed = true;
OnButtonDown();
}
else if (IsPressed && distance < ResetThreshold)
{
IsPressed = false;
OnButtonUp();
}
}
private float GetDistance()
{
switch (Axis)
{
case HVRAxis.X:
case HVRAxis.NegX:
return transform.localPosition.x - StartPosition.x;
case HVRAxis.Y:
case HVRAxis.NegY:
return transform.localPosition.y - StartPosition.y;
case HVRAxis.Z:
case HVRAxis.NegZ:
return transform.localPosition.z - StartPosition.z;
}
return 0f;
}
protected virtual void OnButtonDown()
{
if (SFXButtonDown)
{
if(SFXPlayer.Instance) SFXPlayer.Instance.PlaySFX(SFXButtonDown, transform.position);
}
ButtonDown.Invoke(this);
}
protected virtual void OnButtonUp()
{
if (SFXButtonUp)
{
if(SFXPlayer.Instance) SFXPlayer.Instance.PlaySFX(SFXButtonUp, transform.position);
}
ButtonUp.Invoke(this);
}
}
[Serializable]
public class HVRButtonEvent : UnityEvent<HVRPhysicsButton> { }
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0fb388898ff2f814f84c5cfa8b56235f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,188 @@
using HurricaneVR.Framework.Core;
using HurricaneVR.Framework.Core.Grabbers;
using HurricaneVR.Framework.Core.ScriptableObjects;
using HurricaneVR.Framework.Core.Utils;
using HurricaneVR.Framework.Shared;
using UnityEngine;
using UnityEngine.Serialization;
namespace HurricaneVR.Framework.Components
{
/// <summary>
/// Simple component to help setup a joint constrained on one axis. With bonus rotation friction option when held.
/// </summary>
[RequireComponent(typeof(Rigidbody))]
public class HVRPhysicsDial : MonoBehaviour
{
[Header("Settings")]
[Tooltip("Local axis of rotation")]
public HVRAxis Axis;
[Tooltip("Rigidbody to connect the joint to")]
public Rigidbody ConnectedBody;
[Tooltip("If true the angular velocity will be zero'd out on release.")]
public bool StopOnRelease = true;
[Tooltip("Defaults to true to prevent rotation when not held")]
public bool DisableGravity = true;
[Header("Joint Limits")]
public bool LimitRotation;
[Tooltip("Minimum Angle about the axis of rotation")]
public float MinAngle;
[Tooltip("Maximum rotation about the axis of rotation")]
public float MaxAngle;
[Header("Joint Settings")]
[Tooltip("Angular Damper when the dial is grabbed")]
public float GrabbedDamper = 3;
[Tooltip("Angular Damper when the dial is not grabbed")]
public float Damper = 3;
[Tooltip("Optional spring value of the joint to return to starting rotation if desired")]
public float Spring;
[Header("Editor")]
[SerializeField]
protected Quaternion JointStartRotation;
[Header("Debugging Tools")]
public float TargetAngularVelocity = 0f;
public Rigidbody Rigidbody { get; private set; }
public HVRGrabbable Grabbable { get; private set; }
public ConfigurableJoint Joint { get; set; }
protected virtual void Awake()
{
Rigidbody = this.GetRigidbody();
Rigidbody.useGravity = !DisableGravity;
Grabbable = GetComponent<HVRGrabbable>();
if (Grabbable)
{
Grabbable.HandGrabbed.AddListener(OnDialGrabbed);
Grabbable.HandReleased.AddListener(OnDialReleased);
}
FixAngle(ref MinAngle, ref MaxAngle);
SetupJoint();
AfterJointCreated(Joint);
}
protected virtual void Start()
{
}
protected virtual void Update()
{
if (TargetAngularVelocity > 0f || TargetAngularVelocity < 0f) Joint.targetAngularVelocity = new Vector3(TargetAngularVelocity, 0f, 0f);
}
protected virtual void OnDialReleased(HVRHandGrabber arg0, HVRGrabbable arg1)
{
Joint.SetAngularXDrive(Spring, Damper, 10000f);
if (StopOnRelease)
{
Rigidbody.angularVelocity = Vector3.zero;
}
}
protected virtual void OnDialGrabbed(HVRHandGrabber arg0, HVRGrabbable arg1)
{
Joint.SetAngularXDrive(0f, GrabbedDamper, 10000f);
}
protected virtual void SetupJoint()
{
var currentRotation = transform.localRotation;
transform.localRotation = JointStartRotation;
Joint = gameObject.AddComponent<ConfigurableJoint>();
Joint.connectedBody = ConnectedBody;
Joint.LockLinearMotion();
Joint.LockAngularYMotion();
Joint.LockAngularZMotion();
Joint.anchor = Vector3.zero;
Joint.axis = Axis.GetVector();
if (LimitRotation)
{
ResetLimits();
}
else
{
Joint.angularXMotion = ConfigurableJointMotion.Free;
}
Joint.secondaryAxis = Joint.axis.OrthogonalVector();
Joint.SetAngularXDrive(Spring, Damper, 10000f);
Joint.projectionAngle = 1f;
Joint.projectionDistance = .01f;
Joint.projectionMode = JointProjectionMode.PositionAndRotation;
transform.localRotation = currentRotation;
}
protected virtual void AfterJointCreated(ConfigurableJoint joint)
{
}
/// <summary>
/// Sets the rotation limits on the Joint's main rotation axis
/// </summary>
public void SetLimits(float minAngle, float maxAngle)
{
FixAngle(ref minAngle, ref maxAngle);
Joint.LimitAngularXMotion();
Joint.SetAngularXHighLimit(minAngle);
Joint.SetAngularXLowLimit(maxAngle);
}
/// <summary>
/// Resets the limit's to the -MinAngle and MaxAngle values of this component.
/// </summary>
public void ResetLimits()
{
Joint.LimitAngularXMotion();
Joint.SetAngularXHighLimit(-MinAngle);
Joint.SetAngularXLowLimit(-MaxAngle);
}
/// <summary>
/// Frees the axis of rotation
/// </summary>
public void RemoveLimits()
{
Joint.angularXMotion = ConfigurableJointMotion.Free;
}
/// <summary>
/// Sanity check on the angles for the joint high and low settings
/// </summary>
private void FixAngle(ref float min, ref float max)
{
if (min > 0)
{
min *= -1;
}
if (max < 0)
{
max *= -1;
}
min = Mathf.Clamp(min, min, 0);
max = Mathf.Clamp(max, 0, max);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2cd0e73266090f94ea7927da1368c184
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,422 @@
using System.Collections;
using HurricaneVR.Framework.Core;
using HurricaneVR.Framework.Core.ScriptableObjects;
using HurricaneVR.Framework.Core.Utils;
using HurricaneVR.Framework.Shared;
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
/// <summary>
/// This component helps setup the basic functionality of a hinged door.
/// Includes locking and latching capability. Handle required rotation for unlatching.
/// </summary>
[HelpURL("https://cloudwalker2020.github.io/HurricaneVR-Docs/manual/components/door.html")]
[RequireComponent(typeof(HVRRotationTracker))]
[RequireComponent(typeof(Rigidbody))]
public class HVRPhysicsDoor : MonoBehaviour
{
[Header("Settings")] [Tooltip("Local axis of rotation")]
public HVRAxis Axis;
[Tooltip("Door's rigidbody mass.")] public float Mass = 10f;
public bool DisableGravity = true;
[Tooltip("If true the door and it's handle will have their joint limit's locked on start.")]
public bool StartLocked;
[Tooltip("Rigidbody to connect the joint to")]
public Rigidbody ConnectedBody;
[Header("Door Closing Settings")] [Tooltip("Angle threshold to determine if the door is closed or not.")]
public float CloseAngle = 5f;
[Tooltip("The door will automatically shut over this amount of time once it's close enough to be closed.")]
public float CloseOverTime = .25f;
[Tooltip("How long the door angle must be below 'CloseAngle' to become closed.")]
public float CloseDetectionTime = .5f;
[Header("SFX")] [Tooltip("Angle threshold to play opening and closing sound effects.")]
public float SFXThresholdAngle = 2.5f;
public float SFXResetThreshold = 1f;
public AudioClip SFXOpened;
public AudioClip SFXClosed;
[Tooltip("Delay before the open / close sfx can be played again")]
public float SFXTimeout = 1f;
[Tooltip("Optional transform to define the position of the open / close sound fx.")]
public Transform SFXPosition;
[Header("Handle")] [Tooltip("If true the handle must rotate beyond 'HandThreshold' amount of degrees before it will unlatch, if false the door will not latch automatically.")]
public bool HandleRequiresRotation;
[Tooltip("Required handle rotation to unlatch the door.")]
public float HandleThreshold = 45f;
[Tooltip("The rotation tracker that reports the amount of rotation of the handle.")]
public HVRRotationTracker HandleRotationTracker;
[Tooltip("If provided (and held) the door will not automatically shut when it is below 'CloseAngle' in degrees.")]
public HVRGrabbable HandleGrabbable;
[Tooltip("Rotational physics component that let's this door component lock the door handle's rotation when the door locks.")]
public HVRPhysicsDial DoorKnob;
[Tooltip("The rotation tracker that reports the amount of rotation of the handle.")]
public HVRRotationTracker SecondHandleRotationTracker;
[Tooltip("If provided (and held) the door will not automatically shut when it is below 'CloseAngle' in degrees.")]
public HVRGrabbable SecondHandleGrabbable;
[Tooltip("Rotational physics component that let's this door component lock the door handle's rotation when the door locks.")]
public HVRPhysicsDial SecondDoorKnob;
[Header("Joint Limits")] public bool LimitRotation = true;
[Tooltip("Minimum Angle about the axis of rotation")]
public float MinAngle;
[Tooltip("Maximum rotation about the axis of rotation")]
public float MaxAngle;
[Header("Joint Settings")] [Tooltip("Angular Damper of the door hinge.")]
public float Damper = 10;
[Tooltip("Angular Spring that will return the door to it's starting rotation")]
public float Spring;
//[Header("Editor")]
//[SerializeField]
//protected Quaternion JointStartRotation;
[Header("Debugging")] public float TargetAngularVelocity = 0f;
public bool DoorLatched;
public bool DoorClosed;
public bool Opened;
public bool Closed;
public bool PreviousDoorLatched;
public bool PreviousClosed;
public bool VerboseLogging;
public bool Locked;
public Rigidbody Rigidbody { get; private set; }
public HVRRotationTracker Tracker { get; private set; }
protected ConfigurableJoint Joint { get; set; }
private Quaternion _startRotation;
private bool _doorClosing;
private float _detectionTimer;
private float _lastClosedSFXTime;
private float _lastOpenedSFXTime;
public virtual void Start()
{
Rigidbody = this.GetRigidbody();
Rigidbody.mass = Mass;
Rigidbody.useGravity = !DisableGravity;
Tracker = GetComponent<HVRRotationTracker>();
if (MinAngle > 0)
{
MinAngle *= -1;
}
if (MaxAngle < 0)
{
MaxAngle *= -1;
}
MinAngle = Mathf.Clamp(MinAngle, MinAngle, 0);
MaxAngle = Mathf.Clamp(MaxAngle, 0, MaxAngle);
if (HandleRequiresRotation)
{
if (!HandleRotationTracker)
{
Debug.LogError("HandleRotationTracker not assigned.");
}
DoorLatched = true;
}
DoorClosed = true;
PreviousDoorLatched = DoorLatched;
PreviousClosed = DoorClosed;
SetupJoint();
_startRotation = transform.localRotation;
if (DoorLatched)
{
LockDoorJoint();
}
if (StartLocked)
{
Lock();
}
//set initial values to prevent sfx on start
if (Tracker.UnsignedAngle < SFXThresholdAngle)
{
Closed = true;
}
else if (Tracker.UnsignedAngle > SFXThresholdAngle)
{
Opened = true;
}
}
protected virtual void Update()
{
Joint.targetAngularVelocity = new Vector3(TargetAngularVelocity, 0f, 0f);
if (_doorClosing)
return;
if (Tracker.UnsignedAngle < CloseAngle)
{
_detectionTimer += Time.deltaTime;
}
else if (Tracker.UnsignedAngle >= CloseAngle)
{
_detectionTimer = 0f;
DoorClosed = false;
}
if (HandleGrabbable && HandleGrabbable.IsBeingHeld || SecondHandleGrabbable && SecondHandleGrabbable.IsBeingHeld)
{
_detectionTimer = 0f;
}
if (_detectionTimer > CloseDetectionTime)
{
DoorClosed = true;
}
if (!PreviousClosed && DoorClosed)
{
OnDoorClosed();
}
else if (PreviousClosed && !DoorClosed)
{
OnDoorOpened();
}
var reset = SFXResetThreshold;
if (SFXResetThreshold > SFXThresholdAngle)
{
reset = SFXThresholdAngle * .5f;
}
if (!Opened && Tracker.UnsignedAngle > SFXThresholdAngle && Time.time - _lastOpenedSFXTime > SFXTimeout)
{
_lastOpenedSFXTime = Time.time;
Opened = true;
PlayOpenedSFX();
}
else if (!Closed && Tracker.UnsignedAngle < SFXThresholdAngle && Time.time - _lastClosedSFXTime > SFXTimeout)
{
_lastClosedSFXTime = Time.time;
Closed = true;
PlayClosedSFX();
}
else if (Opened && Tracker.UnsignedAngle < SFXThresholdAngle - reset)
{
Opened = false;
}
else if (Closed && Tracker.UnsignedAngle > SFXThresholdAngle + reset)
{
Closed = false;
}
if (HandleRequiresRotation)
{
if (HandleRotationTracker.UnsignedAngle >= HandleThreshold ||
(SecondHandleRotationTracker && SecondHandleRotationTracker.UnsignedAngle >= HandleThreshold))
{
DoorLatched = false;
}
else if (HandleRotationTracker.UnsignedAngle < HandleThreshold &&
(!SecondHandleRotationTracker || SecondHandleRotationTracker.UnsignedAngle < HandleThreshold) &&
Tracker.UnsignedAngle < CloseAngle)
{
DoorLatched = true;
}
if (!Locked)
{
if (PreviousDoorLatched && !DoorLatched)
{
OnDoorUnLatched();
}
else if (!PreviousDoorLatched && DoorLatched)
{
OnDoorLatched();
}
}
}
PreviousDoorLatched = DoorLatched;
PreviousClosed = DoorClosed;
}
protected virtual Vector3 GetSFXPosition()
{
var position = transform.position;
if (SFXPosition)
{
position = SFXPosition.position;
}
return position;
}
protected virtual void PlayClosedSFX()
{
if (SFXPlayer.Instance) SFXPlayer.Instance.PlaySFX(SFXClosed, GetSFXPosition());
}
protected virtual void PlayOpenedSFX()
{
if (SFXPlayer.Instance) SFXPlayer.Instance.PlaySFX(SFXOpened, GetSFXPosition());
}
public virtual void OnDoorUnLatched()
{
if (VerboseLogging)
Debug.Log($"OnDoorUnLatched");
UnlockDoorJoint();
}
public virtual void OnDoorLatched()
{
if (VerboseLogging)
Debug.Log($"OnDoorLatched");
LockDoorJoint();
}
// ReSharper disable Unity.PerformanceAnalysis
protected virtual void OnDoorClosed()
{
if (VerboseLogging)
Debug.Log($"OnDoorClosed");
StartCoroutine(DoorCloseRoutine());
}
// ReSharper disable Unity.PerformanceAnalysis
protected virtual void OnDoorOpened()
{
if (VerboseLogging)
Debug.Log($"OnDoorOpened");
}
protected virtual void LockDoorJoint()
{
if (!LimitRotation)
return;
Joint.SetAngularXHighLimit(0);
Joint.SetAngularXLowLimit(0);
}
protected virtual void UnlockDoorJoint()
{
Joint.SetAngularXHighLimit(-MinAngle);
Joint.SetAngularXLowLimit(-MaxAngle);
}
/// <summary>
/// Locks the door joint, and the door knob's joint.
/// </summary>
public virtual void Lock()
{
Locked = true;
LockDoorJoint();
LockDoorKnob();
}
/// <summary>
/// Unlocks the door and allows the door handle to rotate.
/// </summary>
public virtual void Unlock()
{
Locked = false;
if (!DoorLatched)
{
UnlockDoorJoint();
}
UnlockDoorKnob();
}
protected virtual void LockDoorKnob()
{
if (DoorKnob) DoorKnob.SetLimits(0, 0);
if (SecondDoorKnob) SecondDoorKnob.SetLimits(0, 0);
}
protected virtual void UnlockDoorKnob()
{
if (DoorKnob) DoorKnob.ResetLimits();
if (SecondDoorKnob) SecondDoorKnob.ResetLimits();
}
protected IEnumerator DoorCloseRoutine()
{
_doorClosing = true;
var startRotation = transform.localRotation;
var elapsed = 0f;
while (elapsed < CloseOverTime)
{
transform.localRotation = Quaternion.Lerp(startRotation, _startRotation, elapsed / CloseOverTime);
elapsed += Time.deltaTime;
yield return null;
}
transform.localRotation = _startRotation;
_doorClosing = false;
}
protected virtual void SetupJoint()
{
var currentRotation = transform.localRotation;
//transform.localRotation = JointStartRotation;
Joint = gameObject.AddComponent<ConfigurableJoint>();
Joint.connectedBody = ConnectedBody;
Joint.LockLinearMotion();
Joint.LockAngularYMotion();
Joint.LockAngularZMotion();
Joint.anchor = Vector3.zero;
Joint.axis = Axis.GetVector();
if (LimitRotation)
{
Joint.LimitAngularXMotion();
Joint.SetAngularXHighLimit(-MinAngle);
Joint.SetAngularXLowLimit(-MaxAngle);
}
else
{
Joint.angularXMotion = ConfigurableJointMotion.Free;
}
Joint.secondaryAxis = Joint.axis.OrthogonalVector();
Joint.SetAngularXDrive(Spring, Damper, 10000f);
transform.localRotation = currentRotation;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4eeb3f37d2fe3e547a9a248b2bbf0139
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,217 @@
using System;
using HurricaneVR.Framework.Core.ScriptableObjects;
using HurricaneVR.Framework.Core.Utils;
using HurricaneVR.Framework.Shared;
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
/// <summary>
/// Helper component to constrain a drawer along the desired movement axis handling the joint creation and limiting for you.
/// Joint is constrained between the start and end position which is defined in the component inspector.
/// </summary>
[RequireComponent(typeof(Rigidbody))]
public class HVRPhysicsDrawer : MonoBehaviour
{
[Header("Settings")]
[Tooltip("Axis the drawer will travel on in local space.")]
public HVRAxis Axis;
[Tooltip("Rigidbody to joint to.")]
public Rigidbody ConnectedBody;
[Tooltip("Optional spring that will return to the starting position")]
public float Spring = 0;
[Tooltip("Damper to provide 'friction' to the drawer.")]
public float Damper = 10;
[Header("SFX")]
public float SFXResetThreshold = .02f;
public AudioClip SFXOpened;
public AudioClip SFXClosed;
[Header("Editor Fields")]
[Tooltip("The resting position of the button")]
public Vector3 StartPosition;
[Tooltip("Furthest position the button can travel")]
public Vector3 EndPosition;
public Vector3 OpenPosition;
[Header("Debug")]
public bool UpdateSpring;
public bool PreviousOpened;
public bool Opened;
public bool PreviousClosed;
public bool Closed;
public Rigidbody Rigidbody { get; private set; }
private Vector3 _axis;
private ConfigurableJoint _joint;
private ConfigurableJoint _limitJoint;
protected virtual void Awake()
{
transform.localPosition = StartPosition;
Rigidbody = GetComponent<Rigidbody>();
_axis = Axis.GetVector();
Rigidbody.useGravity = false;
SetupJoint();
//set initial values to prevent sfx on start
GetValues(out var distance, out var openedDistance, out var resetThreshold);
if (distance > openedDistance)
{
Opened = true;
}
else if (distance < openedDistance)
{
Closed = true;
}
}
private void SetupJoint()
{
_joint = gameObject.AddComponent<ConfigurableJoint>();
_joint.connectedBody = ConnectedBody;
_joint.autoConfigureConnectedAnchor = false;
_joint.anchor = Vector3.zero;
var worldStartPosition = StartPosition;
if (transform.parent)
worldStartPosition = transform.parent.TransformPoint(StartPosition);
var worldEndPosition = EndPosition;
if (transform.parent)
worldEndPosition = transform.parent.TransformPoint(EndPosition);
if (ConnectedBody)
{
_joint.connectedAnchor = ConnectedBody.transform.InverseTransformPoint(worldStartPosition);
}
else
{
_joint.connectedAnchor = worldStartPosition;
}
_joint.SetXDrive(Spring, Damper, Spring);
_joint.LimitXMotion();
_joint.LockYMotion();
_joint.LockZMotion();
_joint.LockAllAngularMotion();
_joint.axis = _axis;
_joint.secondaryAxis = _joint.axis.OrthogonalVector();
_joint.SetLinearLimit(Vector3.Distance(StartPosition, EndPosition));
_limitJoint = gameObject.AddComponent<ConfigurableJoint>();
_limitJoint.connectedBody = ConnectedBody;
_limitJoint.autoConfigureConnectedAnchor = false;
_limitJoint.anchor = Vector3.zero;
if (ConnectedBody)
{
_limitJoint.connectedAnchor = ConnectedBody.transform.InverseTransformPoint(worldEndPosition);
}
else
{
_limitJoint.connectedAnchor = worldEndPosition;
}
_limitJoint.LockYMotion();
_limitJoint.LockZMotion();
_limitJoint.LockAllAngularMotion();
_limitJoint.axis = _axis;
_limitJoint.secondaryAxis = _joint.axis.OrthogonalVector();
_limitJoint.LimitXMotion();
_limitJoint.SetLinearLimit(Vector3.Distance(StartPosition, EndPosition));
}
private void Update()
{
GetValues(out var distance, out var openedDistance, out var resetThreshold);
var closeReset = openedDistance + resetThreshold;
var openReset = openedDistance - resetThreshold;
if (!Opened && distance > openedDistance)
{
Opened = true;
if(SFXPlayer.Instance) SFXPlayer.Instance.PlaySFX(SFXOpened, transform.position);
}
else if (!Closed && distance < openedDistance)
{
Closed = true;
if(SFXPlayer.Instance) SFXPlayer.Instance.PlaySFX(SFXClosed, transform.position);
}
else if (Opened && distance < openReset)
{
Opened = false;
}
else if (Closed && distance > closeReset)
{
Closed = false;
}
PreviousClosed = Closed;
PreviousOpened = Opened;
}
private void GetValues(out float distance, out float openDistance, out float resetDelta)
{
distance = 0f;
openDistance = 0f;
resetDelta = SFXResetThreshold;
switch (Axis)
{
case HVRAxis.X:
case HVRAxis.NegX:
distance = transform.localPosition.x - StartPosition.x;
openDistance = OpenPosition.x - StartPosition.x;
break;
case HVRAxis.Y:
case HVRAxis.NegY:
distance = transform.localPosition.y - StartPosition.y;
openDistance = OpenPosition.y - StartPosition.y;
break;
case HVRAxis.Z:
case HVRAxis.NegZ:
distance = transform.localPosition.z - StartPosition.z;
openDistance = OpenPosition.z - StartPosition.z;
break;
}
distance = Mathf.Abs(distance);
openDistance = Mathf.Abs(openDistance);
if (resetDelta > openDistance)
{
resetDelta = openDistance * .5f;
}
}
private void OnDrawGizmosSelected()
{
var forward = (EndPosition - StartPosition).normalized;
var closeReset = transform.parent.TransformPoint(OpenPosition + (forward * SFXResetThreshold));
var openReset = transform.parent.TransformPoint(OpenPosition - (forward * SFXResetThreshold));
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(closeReset, .005f);
Gizmos.color = Color.green;
Gizmos.DrawWireSphere(openReset, .005f);
Gizmos.color = Color.blue;
Gizmos.DrawWireSphere(transform.parent.TransformPoint(OpenPosition), .005f);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 465d464c79597514bb491631782942cb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,33 @@
using System;
using HurricaneVR.Framework.Core;
using HurricaneVR.Framework.Core.Grabbers;
using HurricaneVR.Framework.Core.Utils;
using HurricaneVR.Framework.Shared;
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
/// <summary>
/// Lever behaves similar to the dial in rigidbody joint behaviour at this time...
/// </summary>
[RequireComponent(typeof(Rigidbody))]
public class HVRPhysicsLever : HVRPhysicsDial
{
public bool DrawGizmos = true;
public void OnDrawGizmosSelected()
{
//if (DrawGizmos)
//{
// var minVector = Quaternion.Euler(MinAngle * _axis) * _orthogonal;
// var maxVector = Quaternion.Euler(MaxAngle * _axis) * _orthogonal;
// Gizmos.color = Color.green;
// Gizmos.DrawLine(transform.position, transform.position + transform.TransformDirection(minVector) * .3f);
// Gizmos.color = Color.yellow;
// Gizmos.DrawLine(transform.position, transform.position + transform.TransformDirection(maxVector) * .3f);
//}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 93ae46635261c874188443083a8deee5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,96 @@
using Assets.HurricaneVR.Framework.Shared.Utilities;
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
/// <summary>
/// Helper component to override various rigidbody properties
/// </summary>
[RequireComponent(typeof(Rigidbody))]
public class HVRRigidBodyOverrides : MonoBehaviour
{
public bool OverrideCOM;
public bool OverrideRotation;
public bool OverrideTensor;
public bool OverrideAngularSpeed;
public bool OverrideMaxDepenetration;
public Vector3 CenterOfMass;
public Vector3 InertiaTensorRotation;
public Vector3 InertiaTensor;
public float MaxAngularVelocity;
public float MaxDepenetration;
[Header("Debug")]
public Vector3 COMGizmoSize = new Vector3(.02f, .02f, .02f);
public bool LiveUpdate;
public bool ShowCOMGizmo;
public Rigidbody Rigidbody;
void Awake()
{
if (!Rigidbody)
{
Rigidbody = GetComponent<Rigidbody>();
}
this.ExecuteNextUpdate(ApplyOverrides);
}
protected virtual void OnValidate()
{
if (!Rigidbody) TryGetComponent(out Rigidbody);
}
public void ApplyOverrides()
{
if (OverrideCOM)
{
Rigidbody.centerOfMass = CenterOfMass;
}
if (OverrideTensor)
{
Rigidbody.inertiaTensor = InertiaTensor;
}
if (OverrideRotation)
{
Rigidbody.inertiaTensorRotation = Quaternion.Euler(InertiaTensorRotation);
}
if (OverrideAngularSpeed)
{
Rigidbody.maxAngularVelocity = MaxAngularVelocity;
}
if (OverrideMaxDepenetration) Rigidbody.maxDepenetrationVelocity = MaxDepenetration;
}
void FixedUpdate()
{
if (LiveUpdate)
{
ApplyOverrides();
}
}
void OnDrawGizmosSelected()
{
if (ShowCOMGizmo)
{
Gizmos.color = Color.yellow;
if (OverrideCOM)
{
Gizmos.DrawCube(transform.TransformPoint(CenterOfMass), COMGizmoSize);
}
else if(Rigidbody)
{
Gizmos.DrawCube(Rigidbody.worldCenterOfMass, COMGizmoSize);
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f1a0e712065e5ec4484d6ca15e0f4563
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,83 @@
using System;
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
/// <summary>
/// Helps constrain loose joints on the desired axis.
/// </summary>
public class HVRRigidbodyLocker : MonoBehaviour
{
public LockOptions Locks;
private Vector3 _position;
private Vector3 _rotation;
public void Start()
{
_position = transform.localPosition;
_rotation = transform.localEulerAngles;
}
public void Update()
{
Lock();
}
private void Lock()
{
var position = transform.localPosition;
var rotation = transform.localEulerAngles;
if ((Locks & LockOptions.PosX) == LockOptions.PosX)
{
position.x = _position.x;
}
if ((Locks & LockOptions.PosY) == LockOptions.PosY)
{
position.y = _position.y;
}
if ((Locks & LockOptions.PosZ) == LockOptions.PosZ)
{
position.z = _position.z;
}
if ((Locks & LockOptions.RotX) == LockOptions.RotX)
{
rotation.x = _rotation.x;
}
if ((Locks & LockOptions.RotY) == LockOptions.RotY)
{
rotation.y = _rotation.y;
}
if ((Locks & LockOptions.RotZ) == LockOptions.RotZ)
{
rotation.z = _rotation.z;
}
transform.localPosition = position;
transform.localEulerAngles = rotation;
}
public void LateUpdate()
{
Lock();
}
}
[Flags]
public enum LockOptions
{
None = 0,
PosX = 1,
PosY = 2,
PosZ = 4,
RotX = 8,
RotY = 16,
RotZ = 32
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f268fd1aeab764742be311a0d06b49d3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,111 @@
using System;
using HurricaneVR.Framework.Core;
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
[RequireComponent(typeof(MeshRenderer))]
public class HVRRingHighlight : HVRScaleHighlight
{
public Transform Camera;
[Header("Settings")]
[Tooltip("Ring must be within this distance from the camera to be displayed")]
public float Distance = 5f;
[Header("Line of Sight Settings")]
[Tooltip("Use ray cast to the camera collider to determine if we should show")]
public bool RequireLineOfSight = true;
[Tooltip("Layer mask for checking line of sight, include the layer of the camera(default is Player)")]
public LayerMask LayerMask;
[Tooltip("Check line of sight only if distance greater than this")]
public float LineOfSightThreshold = 1.5f;
[Tooltip("Timeout to check line of sight")]
public float Delay = 1f;
public MeshRenderer Ring { get; private set; }
private bool _hasCamera;
protected override void Start()
{
if (!Camera && HVRManager.Instance)
{
Camera = HVRManager.Instance.Camera;
}
_hasCamera = Camera;
Ring = GetComponent<MeshRenderer>();
}
protected override void Update()
{
base.Update();
if (!_hasCamera)
return;
transform.LookAt(Camera);
if (!RequireLineOfSight)
{
var distance = Vector3.Distance(transform.position, Camera.position);
if (distance > Distance)
{
Ring.enabled = false;
}
else
{
Ring.enabled = true;
}
}
}
private void FixedUpdate()
{
if (!RequireLineOfSight || !_hasCamera)
return;
var distance = Vector3.Distance(transform.position, Camera.position);
if (distance > Distance)
{
Ring.enabled = false;
return;
}
if (distance < LineOfSightThreshold)
{
Ring.enabled = true;
return;
}
if (Physics.Raycast(transform.position, (Camera.position - transform.position).normalized, out var hit, Distance, LayerMask, QueryTriggerInteraction.Ignore))
{
Ring.enabled = hit.collider.gameObject == Camera.transform.gameObject;
}
else
{
Ring.enabled = false;
}
}
public override void Enable()
{
base.Enable();
Ring.enabled = true;
}
public override void Disable()
{
base.Disable();
Ring.enabled = false;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3f12566bd4f6404580f543211008456c
timeCreated: 1600292559

View File

@@ -0,0 +1,115 @@
using System;
using HurricaneVR.Framework.Core.ScriptableObjects;
using HurricaneVR.Framework.Core.Utils;
using HurricaneVR.Framework.Shared;
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
/// <summary>
/// Used to limit rotations beyond the default Physx limit of 177 degrees. Joint is recreated at certain thresholds to allow the wider
/// range of motion, only use this if you need range of motion beyond 177 degrees.
/// </summary>
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(HVRRotationTracker))]
public class HVRRotationLimiter : MonoBehaviour
{
public const float PhysxMaxLimit = 177f;
[Tooltip("Connected Body of the Joint")]
public Rigidbody ConnectedBody;
[Tooltip("Minimum angle of rotation")]
public int MinAngle;
[Tooltip("Maximum angle of rotation")]
public int MaxAngle;
[Tooltip("Distance traveled before the joint is recreated with new limits")]
public float JointResetThreshold = 90f;
public Rigidbody Rigidbody { get; private set; }
public HVRRotationTracker Tracker { get; private set; }
private ConfigurableJoint _joint;
private float _angleAtCreation;
[Header("Debugging")]
public float maxDelta;
public float minDelta;
protected virtual void Start()
{
Rigidbody = this.GetRigidbody();
Tracker = GetComponent<HVRRotationTracker>();
if (MinAngle > 0)
{
MinAngle *= -1;
}
if (MaxAngle < 0)
{
MaxAngle *= -1;
}
MinAngle = Mathf.Clamp(MinAngle, MinAngle, 0);
MaxAngle = Mathf.Clamp(MaxAngle, 0, MaxAngle);
}
protected virtual void FixedUpdate()
{
var angle = Mathf.Clamp(Tracker.Angle, MinAngle, MaxAngle);
minDelta = Math.Abs(MinAngle - angle);
maxDelta = MaxAngle - angle;
var force = false;
if (_joint)
{
var angleFromJointCreation = angle - _angleAtCreation;
var angleDelta = Mathf.Abs(angleFromJointCreation);
if (angleDelta > JointResetThreshold)
{
Destroy(_joint);
force = true;
}
}
if (!_joint || force)
{
if (minDelta < PhysxMaxLimit || maxDelta < PhysxMaxLimit)
{
_joint = gameObject.AddComponent<ConfigurableJoint>();
_joint.axis = Tracker.AxisOfRotation;
_joint.secondaryAxis = Tracker.AxisOfRotation.OrthogonalVector();
_joint.LimitAngularXMotion();
_joint.connectedBody = ConnectedBody;
_angleAtCreation = Tracker.Angle;
if (minDelta < PhysxMaxLimit)
{
_joint.SetAngularXHighLimit(minDelta);
}
else
{
_joint.SetAngularXHighLimit(PhysxMaxLimit);
}
if (maxDelta < PhysxMaxLimit)
{
_joint.SetAngularXLowLimit(-maxDelta);
}
else
{
_joint.SetAngularXLowLimit(-PhysxMaxLimit);
}
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 02aeadf9b7915034d999c6e3161257f6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,56 @@
using HurricaneVR.Framework.Core.Utils;
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
/// <summary>
/// Used in conjunction with the HVRRotationTracker to play SFX based on rotation amount.
/// </summary>
public class HVRRotationSFX : MonoBehaviour
{
[Tooltip("Tracker required to know how far something has rotated")]
public HVRRotationTracker Tracker;
[Tooltip("Clip chosen at random from this list when rotated beyond 'AngleThreshold'")]
public AudioClip[] SFX;
[Tooltip("Rotation distance must exceed this to play another sfx clip")]
public float AngleThreshold = 30f;
[Header("Debug")]
public float AngleAccumulated;
protected virtual void Awake()
{
if (Tracker)
{
Tracker = GetComponent<HVRRotationTracker>();
}
if (Tracker)
{
Tracker.AngleChanged.AddListener(OnAngleChanged);
}
}
private void OnAngleChanged(float angle, float delta)
{
if (SFX != null && SFX.Length > 0)
{
AngleAccumulated += Mathf.Abs(delta);
if (AngleAccumulated > AngleThreshold)
{
var index = Random.Range(0, SFX.Length);
var sfx = SFX[index];
AngleAccumulated = 0;
PlaySFX(sfx);
}
}
}
protected virtual void PlaySFX(AudioClip sfx)
{
if(SFXPlayer.Instance) SFXPlayer.Instance.PlaySFX(sfx, transform.position);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 50bacb0498a77b04b867c1791becba25
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,134 @@
using System;
using HurricaneVR.Framework.Core;
using HurricaneVR.Framework.Core.Utils;
using HurricaneVR.Framework.Shared;
using UnityEngine;
using UnityEngine.Events;
namespace HurricaneVR.Framework.Components
{
/// <summary>
/// Used to track the rotation in degrees about a defined axis of rotation.
/// Degrees are reported from the starting rotation of the transform.
/// </summary>
public class HVRRotationTracker : MonoBehaviour
{
[Tooltip("Local axis of rotation")]
public HVRAxis Axis;
[Tooltip("Number of Steps")]
public int Steps;
[Tooltip("Threshold to reach to fire the Angle changed method and events")]
public float AngleThreshold = 1f;
[Tooltip("Max angle for Step Size calculation, should match limits set on whatever is controlling the rotational limits of this object")]
public float MaximumAngle = 360f;
public RotationTrackerStepEvent StepChanged = new RotationTrackerStepEvent();
public RotationTrackerAngleEvent AngleChanged = new RotationTrackerAngleEvent();
[Header("Debug")]
public float StepSize;
public int Step;
private Vector3 _angleVector;
private float _previousAngle;
private Vector3 _trackAxis;
private Vector3 _startVector;
[SerializeField]
private float _clampedAngle;
[SerializeField]
private float _angleFromStart;
public float ClampedAngle { get; private set; }
public float Angle { get; private set; }
public float UnsignedAngle => Mathf.Abs(Angle);
public Vector3 AxisOfRotation { get; private set; }
protected virtual void Start()
{
AxisOfRotation = Axis.GetVector();
AxisOfRotation.Normalize();
_trackAxis = AxisOfRotation.OrthogonalVector();
_startVector = transform.localRotation * _trackAxis;
if (Steps <= 1)
{
StepSize = 0f;
}
else if (Steps > 1 && Mathf.Approximately(StepSize, 0f))
{
StepSize = MaximumAngle / Steps;
}
_angleVector = _startVector;
}
protected virtual void Update()
{
var currentVector = transform.localRotation * _trackAxis;
ClampedAngle = Vector3.SignedAngle(_startVector, currentVector, AxisOfRotation);
if (ClampedAngle < 0)
{
ClampedAngle += 360;
}
var angleDelta = Vector3.SignedAngle(_angleVector, currentVector, AxisOfRotation);
if (Mathf.Abs(angleDelta) > AngleThreshold)
{
_angleVector = currentVector;
Angle += angleDelta;
OnAngleChanged(ClampedAngle, angleDelta);
}
var stepAngle = ClampedAngle;
if (Steps > 1)
{
Step = Mathf.RoundToInt(ClampedAngle / StepSize);
stepAngle = Step * StepSize;
if (Mathf.Approximately(stepAngle, 360))
{
Step = 0;
}
}
if (Steps > 1)
{
if (!Mathf.Approximately(stepAngle, _previousAngle))
{
OnStepChanged(Step, true);
_previousAngle = stepAngle;
}
}
_angleFromStart = Angle;
_clampedAngle = ClampedAngle;
}
protected virtual void OnStepChanged(int step, bool raiseEvents)
{
StepChanged.Invoke(step);
}
protected virtual void OnAngleChanged(float angle, float delta)
{
AngleChanged.Invoke(angle, delta);
}
}
[Serializable]
public class RotationTrackerAngleEvent : UnityEvent<float, float> { }
[Serializable]
public class RotationTrackerStepEvent : UnityEvent<int> { }
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 80e9f8f507f775b4aa48c08844126549
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,54 @@
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
/// <summary>
/// Specialized hover behaviour that scales this transform over time when a hand or force grabber is hovering it.
/// </summary>
public class HVRScaleHighlight : HVRGrabbableHoverBase
{
public Vector3 HoverScale = new Vector3(1.3f, 1.3f, 1.3f);
public Vector3 UnhoverScale = new Vector3(.1f, .1f, .1f);
public float ScaleSpeed = .05f;
private Vector3 _targetScale;
private Vector3 _originalScale;
private void Awake()
{
_originalScale = transform.localScale;
_targetScale = _originalScale;
}
protected override void Start()
{
base.Start();
}
protected override void Update()
{
transform.localScale = Vector3.Lerp(transform.localScale, _targetScale, ScaleSpeed);
}
public override void Hover()
{
_targetScale = HoverScale;
}
public override void Unhover()
{
_targetScale = UnhoverScale;
}
public override void Enable()
{
_targetScale = UnhoverScale;
}
public override void Disable()
{
_targetScale = Vector3.zero;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 74b910c8c3f34ee5bb44c0358f637ecc
timeCreated: 1600274255

View File

@@ -0,0 +1,67 @@
using HurricaneVR.Framework.ControllerInput;
using HurricaneVR.Framework.Shared;
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
/// <summary>
/// Used to define center of mass of the controller relative to the hand for bonus throwing velocity calculations
/// </summary>
public class HVRThrowingCenterOfMass : MonoBehaviour
{
public HVRHandSide HandSide;
public Transform Oculus;
public Transform Vive;
public Transform WMR;
public Transform Knuckles;
public Transform Fallback;
public Transform CenterOfMass;
private void Start()
{
if (HandSide == HVRHandSide.Left)
{
if (HVRInputManager.Instance.LeftController)
ControllerConnected(HVRInputManager.Instance.LeftController);
HVRInputManager.Instance.LeftControllerConnected.AddListener(ControllerConnected);
}
else
{
if (HVRInputManager.Instance.RightController)
ControllerConnected(HVRInputManager.Instance.RightController);
HVRInputManager.Instance.RightControllerConnected.AddListener(ControllerConnected);
}
}
private void ControllerConnected(HVRController controller)
{
switch (controller.ControllerType)
{
case HVRControllerType.Pico:
case HVRControllerType.Oculus:
CenterOfMass = Oculus;
break;
case HVRControllerType.ReverbG2:
case HVRControllerType.WMR:
CenterOfMass = WMR;
break;
case HVRControllerType.Vive:
CenterOfMass = Vive;
break;
case HVRControllerType.Knuckles:
CenterOfMass = Knuckles;
break;
default:
CenterOfMass = Fallback;
break;
}
if (!CenterOfMass)
CenterOfMass = Fallback;
if (!CenterOfMass)
CenterOfMass = transform;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5f52676b29554048a64664a7a53a126e
timeCreated: 1601260700

View File

@@ -0,0 +1,77 @@
using HurricaneVR.Framework.Core;
using HurricaneVR.Framework.Core.Grabbers;
using UnityEngine;
namespace HurricaneVR.Framework.Components
{
//Useful if this grabbable is a single handed grabbable attached to another.
//If the other grabbable is grabbed this one will have it's two handed settings forced to be used
[RequireComponent(typeof(HVRGrabbable))]
public class HVRTwoHandStrength : MonoBehaviour
{
public HVRGrabbable[] Others;
public HVRGrabbable Grabbable { get; private set; }
protected virtual void Awake()
{
Grabbable = GetComponent<HVRGrabbable>();
Grabbable.HandGrabbed.AddListener(OnHandGrabbed);
Grabbable.HandReleased.AddListener(OnHandReleased);
if (Others != null)
{
foreach (var other in Others)
{
if (other)
{
other.HandGrabbed.AddListener(OnOtherGrabbableHandGrabbed);
other.HandReleased.AddListener(OnOtherGrabbableHandReleased);
}
}
}
}
private void OnHandReleased(HVRHandGrabber arg0, HVRGrabbable arg1)
{
Grabbable.ForceTwoHandSettings = false;
}
private void OnHandGrabbed(HVRHandGrabber arg0, HVRGrabbable arg1)
{
foreach (var other in Others)
{
if (other.HandGrabbers.Count > 0)
{
Grabbable.ForceTwoHandSettings = true;
break;
}
}
}
private void OnOtherGrabbableHandReleased(HVRHandGrabber arg0, HVRGrabbable g)
{
//handle scenario if two "others" are held, then one was released.
var force = false;
for (int i = 0; i < Others.Length; i++)
{
var other = Others[i];
if (other == g || other.HandGrabbers.Count == 0)
continue;
force = true;
break;
}
if (!force)
Grabbable.ForceTwoHandSettings = false;
}
private void OnOtherGrabbableHandGrabbed(HVRHandGrabber arg0, HVRGrabbable arg1)
{
Grabbable.ForceTwoHandSettings = true;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8a09a78d26498d0459d4d698c39e3a28
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: