Iphone Shaking

Hello,

Does anyone know how to capture a shaking action ( horizontal and vertical) ?

Thanks,

From what I can tell in some of the Apple iPhone sample apps, you basically just set a vector magnitude threshold, set a high-pass filter on the accelerometer values, then if the magnitude of that acceleration vector is ever longer than your set threshold, itā€™s considered a ā€œshakeā€.

Take the example in the Unity docs of how to filter your accelerometer input. That shows you how to do a low-pass filter. Apple sample app then takes the raw accelerometer data for that frame and subtracts the low-pass filtered value from it and thatā€™s your high-pass value. Then you could take the magnitude of it (Appleā€™s sample uses 2.0 as the threshold), which involves a sqrt behind the scenes, but I use sqrMagnitude so as to avoid the sqrt. So if you want to use a threshold of 2, actually check for a sqrmagnitude of 4. If itā€™s over 4, then the user is shaking the device.

Try that and see if it works for you.

Thanks for your answer Brady.

Iā€™am sorry but i do not find the example you are talking about in the unity docs. Could you please tell me where to find it ?

Also, i think i have miss something in understanding the way iphone acceleration works in unity.
When i use ā€˜iPhoneInput.accelerationā€™, i get values that seems to represent orientation.

So if i want to get a real acceleration, i have to compute a diffƩrence between 2 frames.
How can i get the real acceleration for one frame ?

Thanks again,

This probably isnā€™t the best way, but it might be the easiestā€¦

if (iPhoneInput.acceleration.magnitude > 1.5f)
// Is Shaking.

1 Like

Hereā€™s where you can find the low-pass filter sample:
file:///Applications/Unity%20iPhone/Documentation/Components/Advanced%20Unity%20iPhone%20Tips.html

Or just go into the docs, click ā€œReferenceā€ at the upper-right, then search that page for ā€œAdvanced Unity iPhone Tipsā€. Youā€™ll find the code there.

Then just take that filtered value and subtract it from iPhoneInput.acceleration in Update() or FixedUpdate(), depending on how often you want to poll for acceleration. If the vector that results from that subtraction has a squared magnitude above the square of your threshold magnitude, then itā€™s a shake. The example Randy gave you will work, but isnā€™t filtered and also involves a sqrt() inside magnitude. If you use his threshold of 1.5, you could rewrite it as: iPhoneInput.acceleration.sqrMagnitude > 2.25f

That saves you a sqrt() calculation.

Thanks Randy and Brady.

The low pass filter give better result than just computing a difference between 2 frames.

As i wanted to get specific shaking for each axis, i did not use the magnitude calculation.

Here is my code. The low pass filter example has been converted to javascript.

var LowPassKernelWidthInSeconds : float = 1.0;
// The greater the value of LowPassKernelWidthInSeconds, the slower the filtered value will converge towards current input sample (and vice versa). You should be able to use LowPassFilter() function instead of avgSamples(). 

private var AccelerometerUpdateInterval : float = 1.0 / 60.0;
private var LowPassFilterFactor : float = AccelerometerUpdateInterval / LowPassKernelWidthInSeconds; 

private var lowPassValue : Vector3 = Vector3.zero; // should be initialized with 1st sample

private var  IphoneAcc : Vector3;
private var IphoneDeltaAcc : Vector3;
	
///////////////////////
function LowPassFilter(newSample : Vector3) {
		lowPassValue = Vector3.Lerp(lowPassValue, newSample, LowPassFilterFactor);
		return lowPassValue;
}

///////////////////////
function FixedUpdate () {

	IphoneAcc = iPhoneInput.acceleration;
	IphoneDeltaAcc = IphoneAcc-LowPassFilter(IphoneAcc);
	
	if(Mathf.Abs(IphoneDeltaAcc.x)>=.3)
		{
                  // Do something
		}
	if(Mathf.Abs(IphoneDeltaAcc.y)>=.3)
		{
                  // Do something
		}
	if(Mathf.Abs(IphoneDeltaAcc.z)>=.3)
		{
                  // Do something
		}		
}
1 Like

hey there,
I am trying out your shake script and it works great.
I am still new at this though and wanted to use your code to throw some dice. I cant get it to work though, I was wondering if you could have a look? thanks much.

if(Mathf.Abs(IphoneDeltaAcc.x)>=.3)
{

if (iPhoneInput.orientation == iPhoneOrientation.LandscapeLeft){
rigidbody.AddForce (20, 0, 0);
};

if (iPhoneInput.orientation == iPhoneOrientation.LandscapeRight){
rigidbody.AddForce (-20, 0, 0);
};
};

Thanks for posting your code. I could make it fit my needs. And it improved rotation away from jittering.

thanks for the script. with some modifications, it works fantastically. :smile:

Hi RaphDuBus, hi Brady! (yeah, itā€™s me, that pesky EZGUI user! :wink:

I need to catch an iOS shaking event so Googling took me to this very useful post. I have Unity 3.3 but I also keep Unity iPhone 1.7 installed just in case. Nevertheless, I canā€™t find that Advanced Unity iPhone Tips html document that you talk about, Brady, anywhere (not in either of the two Unity installations I have nor on the Unity3D support site nor Googling for ā€œAdvanced Unity iPhone Tipsā€!). Any idea where I could find it now adays?

RaphDuBus, is that script you pasted the gist of the low-pass filter sample example Brady talks about? Iā€™d need something that would work for shaking in any orientation, which suggests using the magnitude as originally suggested. You seem to have found the documentation, but Iā€™m not having such luck!

Cheers!

  • jmpp

So, for future reference, I kept looking over and over for the Advanced Unity iPhone tips page but always kept coming back empty-handed, so looking instead for the low-pass filter sample I came to find it in the Input page of the manual (Unity 3.3):

http://unity3d.com/support/documentation/Manual/Input.html

Now lets hope it works for my ā€œshaking needsā€ :wink:

  • jmpp

And alas, it works beautifully! But a couple of simplifications can be made:

  • No need to poll the deviceā€™s acceleration in FixedUpdate() because itā€™s not a physics related event, so a regular Update() loop will work just fine (unless, of course, you need to use the result of the acceleration polling in a physics related event, but read note [1] below).
  • No need for the LowPassFilter() function because all it does is wrap-up a Mathf.Lerp() call and the return of the result, but no such return is needed because the result is stored in the lowPassValue variable, which is a class member and therefore already accessible from anywhere inside the class.

So, all in all, my code looks as follows:

var accelerometerUpdateInterval : float = 1.0 / 60.0;
// The greater the value of LowPassKernelWidthInSeconds, the slower the filtered value will converge towards current input sample (and vice versa).
var lowPassKernelWidthInSeconds : float = 1.0;
// This next parameter is initialized to 2.0 per Apple's recommendation, or at least according to Brady! ;)
var shakeDetectionThreshold : float = 2.0;

private var lowPassFilterFactor : float = accelerometerUpdateInterval / lowPassKernelWidthInSeconds; 
private var lowPassValue : Vector3 = Vector3.zero;
private var acceleration : Vector3;
private var deltaAcceleration : Vector3;


function Start()
{
    shakeDetectionThreshold *= shakeDetectionThreshold;
    lowPassValue = Input.acceleration;
}


function Update
{
    acceleration = Input.acceleration;
        lowPassValue = Vector3.Lerp(lowPassValue, acceleration, lowPassFilterFactor);
        deltaAcceleration = acceleration - lowPassValue;
    if (deltaAcceleration.sqrMagnitude >= shakeDetectionThreshold)
    {
        // Perform your "shaking actions" here, with suitable guards in the if check above, if necessary to not, to not fire again if they're already being performed.
        Debug.Log("Shake event detected at time "+Time.time);
    }
}

[1] Depending on your game, physics and rendering settings (Edit menu ā†’ Project Settings in the Unity editor), the FixedUpdate() loop will run any number of times per rendering Update() pass, but the default settings set the ratio to two FixedUpdate()'s per each Update() pass, if Iā€™m not mistaken. Nevertheless, neither of those settings will have any bearing on how many times per rendering frame Unity polls the device for acceleration data, that is completely out of our programmatic control. So, in the end, where in which of those two you poll for acceleration data will only have a consequence on measurement accuracy, again depending on your project settings. If what youā€™re looking for is maximum accuracy then what you should be doing is using the Input.accelerationEvents facility, as per the recommendation of the Input page in the Unity manual I linked above, which gives you a full summary of all the acceleration measurement events that took place in the previous frame. Below is the suggested code, which I brought up-to-date with the most recent Unity API:

function PreciseAcceleration() : Vector3
{
    var period : float = 0.0;
    var acc : Vector3 = Vector3.zero;
    for (var event : AccelerationEvent in Input.accelerationEvents)
    {
        acc += event.acceleration*event.deltaTime;
        period += event.deltaTime;
    }
    if (period > 0)
    {
        acc *= 1.0/period;
    }
    return acc;
}

Presumably, you should use the returned acceleration vector either in FixedUpdate() or in Update(), depending on your needs (will you bee using it for a physics related event? the former; on the contrary, the latter). This will give you the most precise acceleration information of the previous rendering frame (which you will also have to smooth out as per the low-pass code if what you want is to detect shaking, i.e. all that the return value of the PreciseAcceleration() method does is replace the reading of Input.acceleration in the current frame).

One last suggestion is to use Vector3.Slerp() rather than Vector3.Lerp() for improved low-pass filtering, at the expense of a certain increased processing burden due to spherical interpolationā€™s higher CPU requirements (it has greater mathematical complexity). Experience will tell you if the trade-off is worth it.

HTH!

  • jmpp
2 Likes

Thanks testing this out now.

Iā€™ve been out of the Unity loop for a while nowā€¦ so Iā€™m trying to get back into it. Months ago Iā€™ve tested the Input.accelerationEvents and tried printing the results to the screen while using the Unity Remote app to control tiltā€¦ but it always stayed at (0, 0, 0). Iā€™ve updated Unity recently and tried it again but still nothing. Is this only a problem through Unity Remote?

thanks!

Note that if you donā€™t do it at a fixed rate, input detection will be frame rate dependent. This means some shakes could be detected at one frame rate, but not at an other frame rate.

With the same motion/shaking made with the iPhone, the deltaā€™s in the acceleration will be bigger when thereā€™s more time between frames.

bump

sorryā€¦ bumping with quoteā€¦ even nudging a little too. Really need to know whatā€™s going on with this.

That is indeed true, if polled in Update then indeed your shake detection will be frame rate dependant. Nevertheless, that might still be a desired scenario, so users of this functionality are just going to have to test to determine what suits their needs better.

Thanks for pointing that out, though! Regards,

  • jmpp