Search Unity

Iphone Shaking

Discussion in 'iOS and tvOS' started by RaphDuBus, Nov 17, 2008.

  1. RaphDuBus

    RaphDuBus

    Joined:
    Jun 18, 2008
    Posts:
    19
    Hello,

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

    Thanks,
     
  2. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    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.
     
  3. RaphDuBus

    RaphDuBus

    Joined:
    Jun 18, 2008
    Posts:
    19
    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,
     
  4. Randy-Edmonds

    Randy-Edmonds

    Joined:
    Oct 10, 2005
    Posts:
    1,122
    This probably isn't the best way, but it might be the easiest...

    if (iPhoneInput.acceleration.magnitude > 1.5f)
    // Is Shaking.
     
    nihonjindayo likes this.
  5. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    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.
     
  6. RaphDuBus

    RaphDuBus

    Joined:
    Jun 18, 2008
    Posts:
    19
    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.



    Code (csharp):
    1.  
    2.  
    3. var LowPassKernelWidthInSeconds : float = 1.0;
    4. // 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().
    5.  
    6. private var AccelerometerUpdateInterval : float = 1.0 / 60.0;
    7. private var LowPassFilterFactor : float = AccelerometerUpdateInterval / LowPassKernelWidthInSeconds;
    8.  
    9. private var lowPassValue : Vector3 = Vector3.zero; // should be initialized with 1st sample
    10.  
    11. private var  IphoneAcc : Vector3;
    12. private var IphoneDeltaAcc : Vector3;
    13.    
    14. ///////////////////////
    15. function LowPassFilter(newSample : Vector3) {
    16.         lowPassValue = Vector3.Lerp(lowPassValue, newSample, LowPassFilterFactor);
    17.         return lowPassValue;
    18. }
    19.  
    20. ///////////////////////
    21. function FixedUpdate () {
    22.  
    23.     IphoneAcc = iPhoneInput.acceleration;
    24.     IphoneDeltaAcc = IphoneAcc-LowPassFilter(IphoneAcc);
    25.    
    26.     if(Mathf.Abs(IphoneDeltaAcc.x)>=.3)
    27.         {
    28.                   // Do something
    29.         }
    30.     if(Mathf.Abs(IphoneDeltaAcc.y)>=.3)
    31.         {
    32.                   // Do something
    33.         }
    34.     if(Mathf.Abs(IphoneDeltaAcc.z)>=.3)
    35.         {
    36.                   // Do something
    37.         }      
    38. }
    39.  
     
    eltronix likes this.
  7. hula

    hula

    Joined:
    Jan 22, 2009
    Posts:
    78
    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);
    };
    };
     
  8. synapsemassage

    synapsemassage

    Joined:
    Jul 27, 2009
    Posts:
    334
    Thanks for posting your code. I could make it fit my needs. And it improved rotation away from jittering.
     
  9. crafTDev

    crafTDev

    Joined:
    Nov 5, 2008
    Posts:
    1,820
    thanks for the script. with some modifications, it works fantastically. :D
     
  10. jmpp

    jmpp

    Joined:
    Jun 1, 2010
    Posts:
    93
    Hi RaphDuBus, hi Brady! (yeah, it's me, that pesky EZGUI user! ;)

    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
     
  11. jmpp

    jmpp

    Joined:
    Jun 1, 2010
    Posts:
    93
    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" ;)

    - jmpp
     
  12. jmpp

    jmpp

    Joined:
    Jun 1, 2010
    Posts:
    93
    And alas, it works beautifully! But a couple of simplifications can be made:

    1. 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).
    2. 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:

    Code (csharp):
    1.  
    2. var accelerometerUpdateInterval : float = 1.0 / 60.0;
    3. // The greater the value of LowPassKernelWidthInSeconds, the slower the filtered value will converge towards current input sample (and vice versa).
    4. var lowPassKernelWidthInSeconds : float = 1.0;
    5. // This next parameter is initialized to 2.0 per Apple's recommendation, or at least according to Brady! ;)
    6. var shakeDetectionThreshold : float = 2.0;
    7.  
    8. private var lowPassFilterFactor : float = accelerometerUpdateInterval / lowPassKernelWidthInSeconds;
    9. private var lowPassValue : Vector3 = Vector3.zero;
    10. private var acceleration : Vector3;
    11. private var deltaAcceleration : Vector3;
    12.  
    13.  
    14. function Start()
    15. {
    16.     shakeDetectionThreshold *= shakeDetectionThreshold;
    17.     lowPassValue = Input.acceleration;
    18. }
    19.  
    20.  
    21. function Update
    22. {
    23.     acceleration = Input.acceleration;
    24.         lowPassValue = Vector3.Lerp(lowPassValue, acceleration, lowPassFilterFactor);
    25.         deltaAcceleration = acceleration - lowPassValue;
    26.     if (deltaAcceleration.sqrMagnitude >= shakeDetectionThreshold)
    27.     {
    28.         // 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.
    29.         Debug.Log("Shake event detected at time "+Time.time);
    30.     }
    31. }
    32.  
    33.  
    [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:

    Code (csharp):
    1.  
    2. function PreciseAcceleration() : Vector3
    3. {
    4.     var period : float = 0.0;
    5.     var acc : Vector3 = Vector3.zero;
    6.     for (var event : AccelerationEvent in Input.accelerationEvents)
    7.     {
    8.         acc += event.acceleration*event.deltaTime;
    9.         period += event.deltaTime;
    10.     }
    11.     if (period > 0)
    12.     {
    13.         acc *= 1.0/period;
    14.     }
    15.     return acc;
    16. }
    17.  
    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
     
    Last edited: May 1, 2011
    eltronix and TheDesignZoo like this.
  13. podda999

    podda999

    Joined:
    Apr 26, 2010
    Posts:
    69
    Thanks testing this out now.
     
  14. dcarlson314

    dcarlson314

    Joined:
    Apr 1, 2009
    Posts:
    45
    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!
     
  15. bernardfrancois

    bernardfrancois

    Joined:
    Oct 29, 2009
    Posts:
    373
    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.
     
  16. dcarlson314

    dcarlson314

    Joined:
    Apr 1, 2009
    Posts:
    45
  17. dcarlson314

    dcarlson314

    Joined:
    Apr 1, 2009
    Posts:
    45
    sorry... bumping with quote... even nudging a little too. Really need to know what's going on with this.
     
  18. jmpp

    jmpp

    Joined:
    Jun 1, 2010
    Posts:
    93
    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