Unity - Популярный игровой движок. Однако, несмотря на множество встроенных возможностей, Unity ограничен в функциях управления вибрацией на устройствах Android. Unity может создать только единичную, не управляемую вибрацию с помощью функции Handheld.Vibrate. Такой способ, чаще всего, не приемлем и хочется использовать все преимущества современных вибро-модулей смартфонов.
В этой статье мы поговорим о том, как можно расширенно управлять вибрацией в Unity, используя Java-функции из официальной документации android.
Конечно, существуют готовые библиотеки, например, такие, как NiceVibration. Но у них могут быть свои недостатки. Это может быть как цена, так и технические проблемы. Тот же найс почему-то конфликтует с аналитикой фейсбука, при сборке под iOS. В некоторых случаях это может стать проблемой.
Все такие библиотеки используют те самые java-функции, просто накручивают удобную обертку-интерфейс вокруг них. Если разобраться, то ни чего сверхсложного там нет.
Типы вибраций
Старые андроид смартфоны оснащались обычным вибромоторчиком. Управлять им можно было только временем работы. Также, у них есть, так называемые, мертвые зоны, когда вибрацию совсем не ощущаешь. В данный момент, в смартфоны все чаще устанавливают вибро модули типа haptic. Он позволяет выдавать более приятную отдачу, которая даже может имитировать нажатия физических кнопок. В таком модуле можно управлять как временем действия, так и амплитудой, что позволяет добиться необходимого эффекта.
Пример минимального Unity-скрипта для вызова методов вибрации из Java:
using UnityEngine;
public static class VibraHands
{
public static AndroidJavaClass unityPlayer;
public static AndroidJavaObject currentActivity;
public static AndroidJavaObject vibrator;
public static AndroidJavaObject context;
public static AndroidJavaClass vibrationEffect;
private static bool initialized = false;
public static void Init()
{
if (initialized) return;
if (Application.isMobilePlatform)
{
unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
vibrator = currentActivity.Call<AndroidJavaObject>("getSystemService", "vibrator");
context = currentActivity.Call<AndroidJavaObject>("getApplicationContext");
if (AndroidVersion >= 26)
{
vibrationEffect = new AndroidJavaClass("android.os.VibrationEffect");
}
}
initialized = true;
}
public static int AndroidVersion
{
get
{
int iVersionNumber = 0;
if (Application.platform == RuntimePlatform.Android)
{
string androidVersion = SystemInfo.operatingSystem;
int sdkPos = androidVersion.IndexOf("API-");
iVersionNumber = int.Parse(androidVersion.Substring(sdkPos + 4, 2).ToString());
}
return iVersionNumber;
}
}
public static void VibrateAndroid(long milliseconds, int Amplitude = -1)
{
Init();
if (Application.isMobilePlatform)
{
if (AndroidVersion >= 26)//на старых андроид смартфонах нормальная вибрация не работает, поэтому отсееваем
{
AndroidJavaObject createOneShot = vibrationEffect.CallStatic<AndroidJavaObject>("createOneShot", milliseconds, Amplitude);
vibrator.Call("vibrate", createOneShot);
}
else
{
vibrator.Call("vibrate", milliseconds);
}
}
}
}
Но по-хорошему, конечно, нужно еще проверять наличие хоть какого нибудь вибро-модуля в устройстве. Поэтому допишем еще и такую проверку.
using UnityEngine;
public static class VibraHands
{
public static AndroidJavaClass unityPlayer;
public static AndroidJavaObject currentActivity;
public static AndroidJavaObject vibrator;
public static AndroidJavaObject context;
public static AndroidJavaClass vibrationEffect;
private static bool initialized = false;
private static bool isHasVibration = true;
public static void Init()
{
if (initialized) return;
if (Application.isMobilePlatform)
{
unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
vibrator = currentActivity.Call<AndroidJavaObject>("getSystemService", "vibrator");
context = currentActivity.Call<AndroidJavaObject>("getApplicationContext");
if (AndroidVersion >= 26)
{
vibrationEffect = new AndroidJavaClass("android.os.VibrationEffect");
}
}
isHasVibration = HasVibrator();
initialized = true;
}
public static void VibrateAndroid(long milliseconds, int Amplitude = -1)
{
Init();
if (Application.isMobilePlatform && isHasVibration)
{
if (AndroidVersion >= 26)//на старых андроид смартфонах нормальная вибрация не работает, поэтому отсееваем
{
AndroidJavaObject createOneShot = vibrationEffect.CallStatic<AndroidJavaObject>("createOneShot", milliseconds, Amplitude);
vibrator.Call("vibrate", createOneShot);
}
else
{
vibrator.Call("vibrate", milliseconds);
}
}
}
public static bool HasVibrator()//проверка наличия модуля вибрации
{
if (Application.isMobilePlatform)
{
AndroidJavaClass contextClass = new AndroidJavaClass("android.content.Context");
string Context_VIBRATOR_SERVICE = contextClass.GetStatic<string>("VIBRATOR_SERVICE");
AndroidJavaObject systemService = context.Call<AndroidJavaObject>("getSystemService", Context_VIBRATOR_SERVICE);
if (systemService.Call<bool>("hasVibrator"))
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
public static int AndroidVersion
{
get
{
int iVersionNumber = 0;
if (Application.platform == RuntimePlatform.Android)
{
string androidVersion = SystemInfo.operatingSystem;
int sdkPos = androidVersion.IndexOf("API-");
iVersionNumber = int.Parse(androidVersion.Substring(sdkPos + 4, 2).ToString());
}
return iVersionNumber;
}
}
}
Модуль мы написали. Теперь, что-бы в нужный момент появилась вибрация, нужно вызвать функцию VibraHands.VibrateAndroid(Время, амплитуда).
В итоге, путем не хитрых манипуляций, мы достучались до глубин андроида и вызвали его родные функции. Нужно отметить, что это не все функции которые нам доступны. Есть еще функция паттернов, которая позволяет "напеть" мелодию вибрацией. Или функция вызова нативно заготовленной вибрации для нажатия кнопки, отмены и тд. Об этом можно почитать в официальной документации Android по ссылке. Удачи в экспериментах!