Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@munkbusiness
Last active November 24, 2023 00:42
Show Gist options
  • Star 18 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save munkbusiness/9e0a7d41bb9c0eb229fd8f2313941564 to your computer and use it in GitHub Desktop.
Save munkbusiness/9e0a7d41bb9c0eb229fd8f2313941564 to your computer and use it in GitHub Desktop.
Vibration for Unity3d with Android native Call that supports both the new vibrationEffectClass and the old simple vibrate, with fallback to Handlheld.Vibrate().
using UnityEngine;
using System.Collections;
public class Vibration : MonoBehaviour {
public static AndroidJavaClass unityPlayer;
public static AndroidJavaObject vibrator;
public static AndroidJavaObject currentActivity;
public static AndroidJavaClass vibrationEffectClass;
public static int defaultAmplitude;
/*
* "CreateOneShot": One time vibration
* "CreateWaveForm": Waveform vibration
*
* Vibration Effects class (Android API level 26 or higher)
* Milliseconds: long: milliseconds to vibrate. Must be positive.
* Amplitude: int: Strenght of vibration. Between 1-255. (Or default value: -1)
* Timings: long: If submitting a array of amplitudes, then timings are the duration of each of these amplitudes in millis.
* Repeat: int: index of where to repeat, -1 for no repeat
*/
void OnEnable() {
#if UNITY_ANDROID && !UNITY_EDITOR
unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
vibrator = currentActivity.Call<AndroidJavaObject>("getSystemService", "vibrator");
if (getSDKInt() >= 26) {
vibrationEffectClass = new AndroidJavaClass("android.os.VibrationEffect");
defaultAmplitude = vibrationEffectClass.GetStatic<int>("DEFAULT_AMPLITUDE");
}
#endif
}
//Works on API > 25
public static void CreateOneShot(long milliseconds) {
if(isAndroid()) {
//If Android 8.0 (API 26+) or never use the new vibrationeffects
if (getSDKInt() >= 26) {
CreateOneShot(milliseconds, defaultAmplitude);
}
else {
OldVibrate(milliseconds);
}
}
//If not android do simple solution for now
else {
Handheld.Vibrate();
}
}
public static void CreateOneShot(long milliseconds, int amplitude) {
if (isAndroid()) {
//If Android 8.0 (API 26+) or never use the new vibrationeffects
if (getSDKInt() >= 26) {
CreateVibrationEffect("createOneShot", new object[] { milliseconds, amplitude });
}
else {
OldVibrate(milliseconds);
}
}
//If not android do simple solution for now
else {
Handheld.Vibrate();
}
}
//Works on API > 25
public static void CreateWaveform(long[] timings, int repeat) {
//Amplitude array varies between no vibration and default_vibration up to the number of timings
if (isAndroid()) {
//If Android 8.0 (API 26+) or never use the new vibrationeffects
if (getSDKInt() >= 26) {
CreateVibrationEffect("createWaveform", new object[] { timings, repeat });
}
else {
OldVibrate(timings, repeat);
}
}
//If not android do simple solution for now
else {
Handheld.Vibrate();
}
}
public static void CreateWaveform(long[] timings, int[] amplitudes, int repeat) {
if (isAndroid()) {
//If Android 8.0 (API 26+) or never use the new vibrationeffects
if (getSDKInt() >= 26) {
CreateVibrationEffect("createWaveform", new object[] { timings, amplitudes, repeat });
}
else {
OldVibrate(timings, repeat);
}
}
//If not android do simple solution for now
else {
Handheld.Vibrate();
}
}
//Handels all new vibration effects
private static void CreateVibrationEffect(string function, params object[] args) {
AndroidJavaObject vibrationEffect = vibrationEffectClass.CallStatic<AndroidJavaObject>(function, args);
vibrator.Call("vibrate", vibrationEffect);
}
//Handles old vibration effects
private static void OldVibrate(long milliseconds) {
vibrator.Call("vibrate", milliseconds);
}
private static void OldVibrate(long[] pattern, int repeat) {
vibrator.Call("vibrate", pattern, repeat);
}
public static bool HasVibrator() {
return vibrator.Call<bool>("hasVibrator");
}
public static bool HasAmplituideControl() {
if (getSDKInt() >= 26) {
return vibrator.Call<bool>("hasAmplitudeControl"); //API 26+ specific
}
else {
return false; //If older than 26 then there is no amplitude control at all
}
}
public static void Cancel() {
if (isAndroid())
vibrator.Call("cancel");
}
private static int getSDKInt() {
if(isAndroid()) {
using (var version = new AndroidJavaClass("android.os.Build$VERSION")) {
return version.GetStatic<int>("SDK_INT");
}
}
else {
return -1;
}
}
private static bool isAndroid() {
#if UNITY_ANDROID && !UNITY_EDITOR
return true;
#else
return false;
#endif
}
}
@d3m3tr105
Copy link

d3m3tr105 commented Feb 21, 2019

Thanks for posting this! It helped things. I do have a question though.
Do the methods with custom amplitudes work for you guys?
For me it just seems to use max power amplitude no matter what I value I pass it.
That means to say that for a vibration of lets say 500ms there is no difference in amplitude/ intensity of the vibration between lets say 10 and 255 amplitude. I tested on more devices it just feels like a maximum intensity vibration.

CreateVibrationEffect("createWaveform", new object[] { timings, amplitudes, repeat });
has the same behavior as
CreateVibrationEffect("createWaveform", new object[] { timings, repeat });
and likewise for
CreateVibrationEffect("createOneShot", new object[] { milliseconds, amplitude });

I tried to pass it value of 0 just to see if the value is getting through to the java object and indeed android monitor spits an error saying it needs a value between 1 and 255. Even if I pass 1 as value for amplitude, it seems it uses the default max amplitude.
Am I missing something or is this an android issue?
Thanks!

@Sithdown
Copy link

This code gives me an error:

NullReferenceException: Object reference not set to an instance of an object.
at Vibration.CreateVibrationEffect (System.String function, System.Object[] args) [0x00000] in <00000000000000000000000000000000>:0

I used only this line of code:

AndroidVibration.CreateOneShot((long)150);

Am I doing something wrong? (Android 9 Pie)

@d3m3tr105
Copy link

@Sithdown
Can you provide your class implementation for AndroidVibration?
Seems like your are missing the vibration object.

@handcircus
Copy link

Hey, just posted a tweaked version that operates without needing to be a MonoBehaviour (all static) and a little refactored. I've changed to pure android as assuming on iOS you might want to use TapticManager etc - https://gist.github.com/handcircus/0046ce4cc8bd8cfd431c3d0375dda826

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment