// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary.
using System;
using UnityEngine;
namespace Oculus.Haptics
{
///
/// Provides playback functionality for a single haptic clip by wrapping .
///
///
///
/// If you don't need a MonoBehaviour and a C# class would be more suitable, use HapticClipPlayer
/// instead. It has the same functionality.
///
///
/// Once you have assigned a to the property you can play
/// the HapticSource, enable or disable looping, and modulate the amplitude and frequency.
///
///
/// You can place multiple HapticSource components in your scene, with a different
/// HapticClip assigned to each.
///
public class HapticSource : MonoBehaviour, ISerializationCallbackReceiver
{
// The HapticClipPlayer that HapticSource wraps.
private HapticClipPlayer _player;
// The following are serializable fields that Unity uses to persist the various
// properties and that HapticSourceEditor uses as the PropertyField. The
// default values set here are the ones that the sliders etc. are set to in the
// Unity editor.
// A serializable field for .
[SerializeField]
private HapticClip _clip;
// A serializable field for .
[SerializeField]
Controller _controller = Controller.Both;
// A serializable field for .
[SerializeField]
private bool _loop = false;
// A serializable field for .
[SerializeField]
[Range(0.0f, float.MaxValue)]
private float _amplitude = 1.0f;
// A serializable field for .
[SerializeField]
[Range(-1.0f, 1.0f)]
private float _frequencyShift = 0.0f;
// A serializable field for .
[SerializeField]
[Range(0, 255)]
private uint _priority = 128;
///
/// Awake() creates a new HapticClipPlayer, assigns the
/// serialized clip to it, and applies all serialized properties.
///
void Awake()
{
_player = new HapticClipPlayer();
_player.clip = _clip;
SyncSerializedFieldsToPlayer();
}
///
/// Starts playback of the HapticClip that has been assigned with the clip
/// property from the start of the clip.
///
///
///
///
/// All properties applied to this will be effective during playback.
/// Although multiple HapticSources can play simultaneously, the output of only one will be felt at any given moment on
/// a given controller.
/// See for a description of how this works.
///
///
///
/// For further information on Play() see .
/// For more details on playback states and playback behaviour, see .
///
///
public void Play()
{
_player.Play(_controller);
}
///
/// Starts playback of the HapticClip that has been assigned with the clip
/// property on the controller passed by argument.
///
///
///
/// The controller assigned to this HapticSource will be reassigned to the one
/// passed to this method.
///
///
/// For more information on playback behaviour see and
/// .
///
///
/// The physical controller to play haptics on.
public void Play(Controller controller)
{
this.controller = controller;
_player.Play(_controller);
}
///
/// Pause playback of the HapticSource.
///
///
///
/// See also: .
/// For details on playback states and playback behaviour, see .
///
public void Pause()
{
_player.Pause();
}
///
/// Resume playback of the HapticSource.
///
///
///
/// See also: .
/// For details on playback states and playback behaviour, see .
///
public void Resume()
{
_player.Resume();
}
///
/// Stops playback of the HapticSource.
///
///
///
/// See also: .
/// For details on playback states and playback behaviour, see .
///
public void Stop()
{
_player.Stop();
}
///
/// Seeks the current playback position of the HapticSource.
///
///
///
/// See also: .
/// For details on playback states and playback behaviour, see .
///
public void Seek(float time)
{
_player.Seek(time);
}
///
/// Assigns a HapticClip to this HapticSource.
///
///
///
/// See also: .
/// For details on playback states and playback behaviour, see .
///
public HapticClip clip
{
set
{
_clip = value;
if (_player != null)
{
_player.clip = _clip;
}
}
}
///
/// The duration of the assigned HapticClip in seconds.
///
public float clipDuration => _player.clipDuration;
///
/// Assigns a physical controller to this HapticSource that
/// haptics should output to.
///
///
///
/// The controller will only be applied the next time
/// is called. It can also be assigned as an argument to
/// if preferred.
///
public Controller controller
{
set
{
_controller = value;
}
}
///
[System.ComponentModel.DefaultValue(false)]
public bool loop
{
get => _loop;
set
{
_loop = value;
_player.isLooping = _loop;
}
}
///
/// Sets/gets the HapticSource's amplitude.
///
///
///
/// See for details.
///
///
[System.ComponentModel.DefaultValue(1.0)]
public float amplitude
{
get => _amplitude;
set
{
_amplitude = value;
_player.amplitude = _amplitude;
}
}
///
/// Sets/gets the HapticSource's frequency shift.
///
/// See for details.
///
///
[System.ComponentModel.DefaultValue(0.0)]
public float frequencyShift
{
get => _frequencyShift;
set
{
_frequencyShift = value;
_player.frequencyShift = _frequencyShift;
}
}
///
/// Sets/gets the HapticSource's playback priority.
///
///
///
/// See for details.
///
///
[System.ComponentModel.DefaultValue(128)]
public uint priority
{
get => _priority;
set
{
_priority = value;
_player.priority = _priority;
}
}
private void SyncSerializedFieldsToPlayer()
{
if (_player is null)
{
return;
}
// Send properties to the HapticClipPlayer.
_player.isLooping = _loop;
_player.amplitude = _amplitude;
_player.frequencyShift = _frequencyShift;
_player.priority = _priority;
}
///
/// Serialization callback from the ISerializationCallbackReceiver interface.
/// Unused for now but declared to fulfill the interface.
///
public void OnBeforeSerialize()
{
}
///
/// Serialization callback from the ISerializationCallbackReceiver interface.
/// HapticSource uses it to synchronize the properties of the wrapped
/// HapticClipPlayer to the serializable fields on this HapticSource.
/// This occurs whenever a user updates any of the values on the custom inspector or
/// whenever Unity loads the persisted data.
///
public void OnAfterDeserialize()
{
if (_player != null)
{
SyncSerializedFieldsToPlayer();
}
}
///
/// Ensures that the lifetime of the wrapped HapticClipPlayer matches that of
/// the HapticSource.
///
protected virtual void OnDestroy()
{
_player.Dispose();
}
}
}