using UnityEngine;
namespace Watermelon
{
///
/// Manages haptic feedback across platforms (iOS, Android, WebGL, Editor).
/// Create an instance via new Haptic() (e.g. from ) to activate the module,
/// then use the static facade methods to trigger feedback.
///
public class Haptic
{
private static Haptic instance;
/// Predefined light haptic feedback (short, low intensity).
public static readonly HapticData HAPTIC_LIGHT = new HapticData(0.14f, 0.4f);
/// Predefined medium haptic feedback.
public static readonly HapticData HAPTIC_MEDIUM = new HapticData(0.14f, 0.6f);
/// Predefined strong haptic feedback.
public static readonly HapticData HAPTIC_HARD = new HapticData(0.14f, 0.8f);
/// Predefined light haptic pattern.
public static readonly HapticPattern PATTERN_LIGHT = new HapticPattern("light", new HapticEvent[]
{
new() { Duration = 0.3f, Intensity = 1.0f, Sharpness = 0.0f, StartTime = 0.0f }
});
private bool isActive;
private readonly HapticSave save;
private readonly BaseHapticWrapper wrapper;
/// Fires when haptic is enabled or disabled.
public static event SimpleBoolCallback StateChanged;
///
/// Whether haptic feedback is currently enabled. Persisted across sessions.
/// Returns false if the module has not been created yet.
///
public static bool IsActive
{
get => IsInitialized && instance.isActive;
set
{
if (!IsInstanceExists()) return;
instance.isActive = value;
instance.save.IsActive = value;
SaveController.MarkAsSaveIsRequired();
LogManager.Log($"[Haptic]: State changed: {(value ? "Active" : "Disabled")}", LogCategory.Systems);
StateChanged?.Invoke(value);
}
}
/// Returns true if an instance has been created.
public static bool IsInitialized => instance != null;
///
/// Creates the haptic module instance, loads saved state and sets up the platform wrapper.
/// The constructor registers itself as the active instance.
///
public Haptic()
{
instance = this;
save = SaveController.GetSaveObject("haptic");
isActive = save.IsActive;
wrapper = GetPlatformWrapper();
if (wrapper == null)
{
LogManager.LogWarning("[Haptic]: Unsupported platform", LogCategory.Systems);
return;
}
wrapper.Init();
wrapper.RegisterPattern(PATTERN_LIGHT);
}
///
/// Registers a custom haptic pattern so it can be triggered via or .
///
public static void RegisterPattern(HapticPattern hapticPattern)
{
if (!IsInstanceExists()) return;
if (instance.wrapper == null) return;
instance.wrapper.RegisterPattern(hapticPattern);
}
/// Plays haptic feedback using a preset.
public static void Play(HapticData hapticData)
{
Play(hapticData.Duration, hapticData.Intensity);
}
/// Plays haptic feedback with the given duration (seconds) and intensity (0–1).
public static void Play(float duration, float intensity = 1.0f)
{
if (!IsActive) return;
if (instance.wrapper == null) return;
if (duration <= 0) return;
instance.wrapper.Play(duration, intensity);
}
/// Plays a registered .
public static void Play(HapticPattern pattern)
{
if (!IsActive) return;
if (instance.wrapper == null) return;
instance.wrapper.Play(pattern.ID);
}
/// Plays a registered haptic pattern by its string ID.
public static void Play(string patternID)
{
if (!IsActive) return;
if (instance.wrapper == null) return;
instance.wrapper.Play(patternID);
}
/// Releases the module instance and clears all state.
public static void Unload()
{
instance = null;
StateChanged = null;
}
private static bool IsInstanceExists()
{
if (instance == null)
{
LogManager.LogWarning("[Haptic]: Module is not initialized. Create a Haptic instance first.", LogCategory.Systems);
return false;
}
return true;
}
public static BaseHapticWrapper GetPlatformWrapper()
{
#if UNITY_EDITOR
return new EditorHapticWrapper();
#elif UNITY_IOS
return new IOSHapticWrapper();
#elif UNITY_ANDROID
return new AndroidHapticWrapper();
#elif UNITY_WEBGL
return new WebGLHapticWrapper();
#else
return null;
#endif
}
}
}