Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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
}
}
@munkbusiness

This comment has been minimized.

Copy link
Owner Author

@munkbusiness munkbusiness commented Feb 7, 2018

Adapted from https://gist.github.com/aVolpe/707c8cf46b1bb8dfb363 and https://gist.github.com/andyrbacon/4ff08fcdf7ab0c023118874f5339bf7a

It is a bit messy because I needed to check the SDK values on startup so I couldn't make it static. I am open for cleaner solutions.

@LucianoAlmeida

This comment has been minimized.

Copy link

@LucianoAlmeida LucianoAlmeida commented Dec 19, 2018

That works great!
Thanks for the script!

@veerxyz

This comment has been minimized.

Copy link

@veerxyz veerxyz commented Dec 21, 2018

if UNITY_ANDROID && !UNITY_EDITOR
unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
currentActivity = unityPlayer.GetStatic("currentActivity");
vibrator = currentActivity.Call("getSystemService", "vibrator");
if (getSDKInt() >= 26) {
vibrationEffectClass = new AndroidJavaClass("android.os.VibrationEffect");
defaultAmplitude = vibrationEffectClass.GetStatic("DEFAULT_AMPLITUDE");
}
#endif
This area (complete code in between) becomes grey and it feels like its inaccesible or something.
This Compiles well in unity, but when i deploy and run it on my android(s), it doesn't do anything?

Tested on : Samsung S6 (Android 7.0) and Oppo Find 7a (Android 5.0)
The previous code was working fine but then that wouldn't work with later/newer APIs.
If this works for both, so this should be amazing!!!
I'm pretty sure this would work. So could you help me out? Thanks. :D

@d3m3tr105

This comment has been minimized.

Copy link

@d3m3tr105 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

This comment has been minimized.

Copy link

@Sithdown Sithdown commented Feb 22, 2019

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

This comment has been minimized.

Copy link

@d3m3tr105 d3m3tr105 commented Feb 22, 2019

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

@handcircus

This comment has been minimized.

Copy link

@handcircus handcircus commented Jul 16, 2019

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