first commit
This commit is contained in:
commit
aa44d3ad70
1585 changed files with 277994 additions and 0 deletions
345
Assets/Resources/Scripts/StepsLink/HapticManager.cs
Normal file
345
Assets/Resources/Scripts/StepsLink/HapticManager.cs
Normal file
|
@ -0,0 +1,345 @@
|
|||
using UnityEngine;
|
||||
using Bhaptics.SDK2;
|
||||
using System;
|
||||
using NaughtyAttributes;
|
||||
|
||||
namespace Tools.Bhaptics
|
||||
{
|
||||
/// <summary>
|
||||
/// Class that manages haptic feedback for the step link.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class is implemented as a singleton. Use <see cref="GetInstance"/> to obtain the instance of the <see cref="HapticManager"/>.
|
||||
/// </remarks>
|
||||
/// <remarks>
|
||||
/// This class is not thread-safe; however, it should not pose a problem for this use case. The <see cref="BhapticsLibrary"/>
|
||||
/// appears to be thread-safe while playing the haptic feedback.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Get the instance of the HapticManager
|
||||
/// HapticManager hapticManager = HapticManager.GetInstance;
|
||||
///
|
||||
/// // Play the haptic feedback for the start of the game
|
||||
/// hapticManager.PlayStartGame();
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class HapticManager : MonoBehaviour
|
||||
{
|
||||
// Const strings
|
||||
private const string EXC_NOT_INIT = "HapticManager not initialized, call Initialize first";
|
||||
private const string LOG_ALREADY_INIT = "HapticManager already initialized";
|
||||
private const string EXC_CAMERA_NULL = "PlayerCamera not set.";
|
||||
private const string EXC_MIN_DISTANCE = "MinDistance_meters must be greater than 0 meters";
|
||||
private const string EXC_REFRATE_MIN = "RefreshRate (in seconds) must be greater than 0.1";
|
||||
private const string LOG_HIGH_REFRESHRATE = "RefreshRate is high, consider using a lower value";
|
||||
private const string LOG_INIT_DONE = "HapticManager initialized";
|
||||
|
||||
/// <summary>
|
||||
/// Haptic feedback for the start of the game
|
||||
/// </summary>
|
||||
private static readonly string STEP_LINK_GAMESTART = BhapticsEvent.GAME_START;
|
||||
|
||||
/// <summary>
|
||||
/// Haptic feedback for the step link update of the haptic feedback
|
||||
/// </summary>
|
||||
private static readonly string STEP_LINK_EFFECT = BhapticsEvent.STEP_LINK;
|
||||
|
||||
/// <summary>
|
||||
/// Haptic feedback for the step link update of the haptic feedback when the user is looking precisely at the object
|
||||
/// </summary>
|
||||
private static readonly string STEP_LINK_EFFECT_PERFECT = BhapticsEvent.HAPTIC_STEP_LINK_PERFECT;
|
||||
|
||||
/// <summary>
|
||||
/// Angle in degrees for the perfect look at the object
|
||||
/// </summary>
|
||||
private static readonly float ANGLE_FOR_PERFECTION = 10f;
|
||||
|
||||
/// <summary>
|
||||
/// The camera of the player, used as center of the reference system
|
||||
/// </summary>
|
||||
[Tooltip("The camera of the player, used as center of the reference system")]
|
||||
[Required]
|
||||
public Camera playerCamera;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum distance starting decreasing the intensity of the haptic feedback
|
||||
/// </summary>
|
||||
[Tooltip("Minimum distance starting decreasing the intensity of the haptic feedback")]
|
||||
[MinValue(0.0f)]
|
||||
public float minDistance_meters = 4f;
|
||||
|
||||
/// <summary>
|
||||
/// Refresh rate of the distance / angle calculation and haptic feedback in seconds
|
||||
/// </summary>
|
||||
[Tooltip("Refresh rate of the distance / angle calculation and haptic feedback in seconds")]
|
||||
[MinValue(0.2f)]
|
||||
public float refreshRate = 0.3f;
|
||||
|
||||
/// <summary>
|
||||
/// If true, when precisely looking at the object, the haptic feedback on head will be used intead
|
||||
/// </summary>
|
||||
[Header("Use Haptic Tactal")]
|
||||
[Tooltip("If true, when precisely looking at the object, use the tactal")]
|
||||
public bool headForPreciseLook = true;
|
||||
|
||||
/// <summary>
|
||||
/// The camera of the player, used as center of the reference system
|
||||
/// </summary>
|
||||
public float MinDistance_meters
|
||||
{
|
||||
get
|
||||
{
|
||||
return minDistance_meters;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value >= 0)
|
||||
{
|
||||
minDistance_meters = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception(EXC_MIN_DISTANCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh rate of the distance / angle calculation and haptic feedback in seconds
|
||||
/// </summary>
|
||||
public float RefreshRate
|
||||
{
|
||||
get
|
||||
{
|
||||
return refreshRate;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value > 0.1f) { refreshRate = value; }
|
||||
else
|
||||
{
|
||||
throw new Exception(EXC_REFRATE_MIN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (playerCamera == null)
|
||||
{
|
||||
throw new Exception(EXC_CAMERA_NULL);
|
||||
}
|
||||
else if (minDistance_meters < 0.1)
|
||||
{
|
||||
throw new Exception(EXC_MIN_DISTANCE);
|
||||
}
|
||||
else if (refreshRate <= 0.1)
|
||||
{
|
||||
throw new Exception(EXC_REFRATE_MIN);
|
||||
}
|
||||
else if (refreshRate >= 5)
|
||||
{
|
||||
Debug.LogWarning(LOG_HIGH_REFRESHRATE);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Play the haptic feedback intended for the start of the game
|
||||
/// </summary>
|
||||
public void PlayStartGame()
|
||||
{
|
||||
BhapticsLibrary.Play(STEP_LINK_GAMESTART);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Play the haptic feedback of the fast heartbeat for the seconds specified
|
||||
/// </summary>
|
||||
public void PlayHeartBeatFast()
|
||||
{
|
||||
if (!BhapticsLibrary.IsPlayingByEventId(BhapticsEvent.HEARTBEAT_FAST))
|
||||
{
|
||||
BhapticsLibrary.PlayParam(BhapticsEvent.HEARTBEAT_FAST,
|
||||
intensity: 1f,
|
||||
duration: 1f,
|
||||
angleX: 0f,
|
||||
offsetY: 0f);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop the haptic feedback of the heartbeat
|
||||
/// </summary>
|
||||
public void StopHeartBeatFast()
|
||||
{
|
||||
BhapticsLibrary.StopByEventId(BhapticsEvent.HEARTBEAT_FAST);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Play the haptic feedback of the fast heartbeat for the seconds specified
|
||||
/// </summary>
|
||||
public void PlayHeartBeat()
|
||||
{
|
||||
if (!BhapticsLibrary.IsPlayingByEventId(BhapticsEvent.HEARTBEAT))
|
||||
{
|
||||
BhapticsLibrary.PlayParam(BhapticsEvent.HEARTBEAT,
|
||||
intensity: 1f,
|
||||
duration: 1f,
|
||||
angleX: 0f,
|
||||
offsetY: 0f);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop the haptic feedback of the heartbeat
|
||||
/// </summary>
|
||||
public void StopHeartBeat()
|
||||
{
|
||||
BhapticsLibrary.StopByEventId(BhapticsEvent.HEARTBEAT_FAST);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Play the haptic feedback of the fbomb for the seconds specified
|
||||
/// </summary>
|
||||
public void PlayBomb(GameObject gameObject)
|
||||
{
|
||||
BhapticsLibrary.PlayParam(BhapticsEvent.EXPLOSION,
|
||||
intensity: 2f,
|
||||
duration: 1f,
|
||||
angleX: ComputeXZAngle(playerCamera.gameObject, gameObject),
|
||||
offsetY: 0f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop all the haptic feedback
|
||||
/// </summary>
|
||||
public void StopAll()
|
||||
{
|
||||
BhapticsLibrary.StopAll();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get XZ angle between the camera and the target
|
||||
/// The XZ is the horizontal plane
|
||||
/// </summary>
|
||||
/// <param name="camera"></param>
|
||||
/// <param name="target"></param>
|
||||
/// <returns></returns>
|
||||
private float ComputeXZAngle(GameObject camera, GameObject target)
|
||||
{
|
||||
Vector2 cameraPositionXZ = new(camera.transform.position.x, camera.transform.position.z);
|
||||
Vector2 targetPositionFromCameraXZ = new(target.transform.position.x, target.transform.position.z);
|
||||
|
||||
Vector2 cameraToNextObjectXZDirection = targetPositionFromCameraXZ - cameraPositionXZ;
|
||||
cameraToNextObjectXZDirection.Normalize();
|
||||
|
||||
Vector2 cameraDirectionXZ = new(camera.transform.forward.x, camera.transform.forward.z);
|
||||
|
||||
return Vector2.SignedAngle(cameraDirectionXZ, cameraToNextObjectXZDirection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute the angle between the camera and the target on the YZ plane
|
||||
/// The YZ is the vertical plane going forward from the camera
|
||||
/// </summary>
|
||||
private float ComputeYZAngle(GameObject camera, GameObject target)
|
||||
{
|
||||
//plane of the, create 2 vectors to use for the angleXZ calculation
|
||||
Vector2 cameraPositionYZ = new(camera.transform.position.y, camera.transform.position.z);
|
||||
Vector2 targetPositionFromCameraYZ = new(target.transform.position.y, target.transform.position.z);
|
||||
|
||||
Vector2 cameraToNextObjectYZDirection = targetPositionFromCameraYZ - cameraPositionYZ;
|
||||
cameraToNextObjectYZDirection.Normalize();
|
||||
|
||||
Vector2 cameraDirectionYZ = new(camera.transform.forward.y, camera.transform.forward.z);
|
||||
|
||||
return Vector2.SignedAngle(cameraDirectionYZ, cameraToNextObjectYZDirection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute the distance between the camera and the target
|
||||
/// </summary>
|
||||
/// <param name="camera"></param>
|
||||
/// <param name="target"></param>
|
||||
/// <returns></returns>
|
||||
private float ComputeDistance(GameObject camera, GameObject target)
|
||||
{
|
||||
float distance = Vector3.Distance(camera.transform.position, target.transform.position);
|
||||
return distance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute the intensity of the haptic feedback based on the distance between the camera and the target
|
||||
/// </summary>
|
||||
/// <param name="camera"></param>
|
||||
/// <param name="target"></param>
|
||||
/// <returns></returns>
|
||||
private float ComputeIntensitiy(GameObject camera, GameObject target)
|
||||
{
|
||||
float distance = ComputeDistance(camera, target);
|
||||
float intensity = distance > minDistance_meters ? 1 : distance /= minDistance_meters;
|
||||
return intensity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute the offsetY of the haptic feedback based on the angleYZ between the camera and the target
|
||||
/// </summary>
|
||||
/// <param name="angleYZ"></param>
|
||||
/// <returns></returns>
|
||||
private float ComputeOffsetY(float angleYZ)
|
||||
{
|
||||
float offsetY = angleYZ / 180f;
|
||||
if (offsetY > 0.5f) offsetY = 0.5f;
|
||||
else if (offsetY < -0.5f) offsetY = -0.5f;
|
||||
|
||||
return -offsetY;
|
||||
}
|
||||
|
||||
// This method plays the haptic feedback based on the distance and angleXZ between
|
||||
// the headset and the parameter object
|
||||
/// <summary>
|
||||
/// Play the haptic feedback based on the distance and angleXZ between the headset and the parameter object
|
||||
/// also the offsetY is calculated based on the angleYZ between the headset and the parameter object
|
||||
/// its possible to use the head for precise look, in this case the haptic feedback on the head will be used
|
||||
/// </summary>
|
||||
/// <param name="linkedObject"></param>
|
||||
/// <returns></returns>
|
||||
public void PlayDirectionalLink(GameObject linkedObject)
|
||||
{
|
||||
float angleXZ = ComputeXZAngle(playerCamera.gameObject, linkedObject);
|
||||
float angleYZ = ComputeYZAngle(playerCamera.gameObject, linkedObject);
|
||||
|
||||
BhapticsLibrary.StopAll();
|
||||
|
||||
// if the user is looking at the object, play head haptic feedback, otherwise play vest haptic feedback
|
||||
if (headForPreciseLook && Mathf.Abs(angleXZ) < ANGLE_FOR_PERFECTION && Mathf.Abs(angleYZ) < ANGLE_FOR_PERFECTION)
|
||||
{
|
||||
BhapticsLibrary.PlayParam(STEP_LINK_EFFECT_PERFECT,
|
||||
intensity: 0.5f,
|
||||
duration: RefreshRate,
|
||||
angleX: 0f,
|
||||
offsetY: 0f);
|
||||
|
||||
// Debug.Log("HEAD: Distance: " + 0 + " m, angleXZ: " + 0 + "°, intensity: " + 0.1f + " duration: " + RefreshRate + " s, offsetY: " + 0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
float intensity = ComputeIntensitiy(playerCamera.gameObject, linkedObject);
|
||||
|
||||
float offsetY = ComputeOffsetY(angleYZ);
|
||||
|
||||
// intensity: 1f, The value multiplied by the original value
|
||||
// duration: 1f, The value multiplied by the original value
|
||||
// angleX: 20f, The value that rotates around global Vector3.up(0~360f)
|
||||
// offsetY: 0.3f, // The value to move up and down(-0.5~0.5)
|
||||
BhapticsLibrary.PlayParam(STEP_LINK_EFFECT,
|
||||
intensity: intensity,
|
||||
duration: RefreshRate,
|
||||
angleX: angleXZ,
|
||||
offsetY: offsetY);
|
||||
|
||||
// Debug.Log("SUIT: Distance: " + distance + " m, angleXZ: " + angleXZ + "°, intensity: " + intensity + " duration: " + RefreshRate + " s, offsetY: " + offsetY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue