I am writing an iPhone camera App. When user is about to take a picture, I would like to check if the iPhone is shaking and wait for the moment that there is no shaking and then capture the phone.
How can I do it?
Anit-shake feature is quite a complicated feature to pull off. I reckon it's a combination of some powerful blur detection/removal algorithms, and the gyroscope on iPhone.
You may start by looking into how to detect motion with the iPhone, and see what kind of results you can get with that. If it's not enough, start looking into shift/blur direction detection algorithms. This is not a trivial problem, but is something that you could probably accomplish given enough time. Hope that Helps!
// Ensures the shake is strong enough on at least two axes before declaring it a shake.
// "Strong enough" means "greater than a client-supplied threshold" in G's.
static BOOL L0AccelerationIsShaking(UIAcceleration* last, UIAcceleration* current, double threshold) {
double
deltaX = fabs(last.x - current.x),
deltaY = fabs(last.y - current.y),
deltaZ = fabs(last.z - current.z);
return
(deltaX > threshold && deltaY > threshold) ||
(deltaX > threshold && deltaZ > threshold) ||
(deltaY > threshold && deltaZ > threshold);
}
#interface L0AppDelegate : NSObject <UIApplicationDelegate> {
BOOL histeresisExcited;
UIAcceleration* lastAcceleration;
}
#property(retain) UIAcceleration* lastAcceleration;
#end
#implementation L0AppDelegate
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[UIAccelerometer sharedAccelerometer].delegate = self;
}
- (void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
if (self.lastAcceleration) {
if (!histeresisExcited && L0AccelerationIsShaking(self.lastAcceleration, acceleration, 0.7)) {
histeresisExcited = YES;
/* SHAKE DETECTED. DO HERE WHAT YOU WANT. */
} else if (histeresisExcited && !L0AccelerationIsShaking(self.lastAcceleration, acceleration, 0.2)) {
histeresisExcited = NO;
}
}
self.lastAcceleration = acceleration;
}
// and proper #synthesize and -dealloc boilerplate code
#end
I Googled it and found at How do I detect when someone shakes an iPhone?
Related
i am currently writing a game with a number of rectangles (say 30), if a user clicks on one of the four sides a certain action will need to be taken. Trouble is each card will have the same side which will perform this action. For example with rectangle 1 the side which will perform this action will be the left, but rectangle 3 the side will be the top side. My guess is a pixel collision detection is needed here. What do you guys think? Also is there a good example on pixel collision using cocos2s out there?
You can simply use following code
if (CGRectIntersectsRect(Rect1, Rect2))
{
//Your code ...
}
USING above CGRectIntersectsRect you can detect the collision pixels within a rectangular area.
yes good point, the solution was a little messy but it worked, this is what i did: I subclassed CCSprite and created a new attribute called state saving the current position if rotated relative to that rotation. Depending on what side was touched i would use that information to determine which side of the rectangle was touched. –
if((spriteCentre1.x < spriteCentre2.x) && (xAxisDiff < yAxisDiff))
{
[HelloWorldLayer whichSideTouched:sprite1 sideTouched:kStateRightPlus touchingSprite:sprite2];
}
+(int)whichSideTouched:(SpriteCard *)sprite sideTouched:(int)touched touchingSprite:(SpriteCard *)sprite2
{
switch (sprite.state) {
case kStateUpPlus:
case kStateUpMinus:
if(touched == kStateDownPlus)
{
NSLog(# "touched up ");
[sprite setTop:sprite2];
retValue = kStateUpPlus;
}
else if(touched == kStateRightPlus)
{
NSLog(# "touch left");
[sprite setRight:sprite2];
retValue = kStateLeftPlus;
}
}
Thanks for that Dhaval, here is the code i wrote a future reference for anyone who wishes to do similar, it can be further fine tuned, if i figure out a more accurate way of doing this i will update this thread accordingly
-(void)touchWhichSide:(CCSprite *)sprite1 collidingSprite:(CCSprite *)sprite2
{
BOOL retValue = NO;
CGPoint spriteCentre1 = sprite1.position;
CGPoint spriteCentre2 = sprite2.position;
NSLog(#"X %f",spriteCentre2.x - spriteCentre1.x);
NSLog(#"Y %f",spriteCentre1.y - spriteCentre2.y);
float xAxisDiff = spriteCentre1.x - spriteCentre2.x;
float yAxisDiff = spriteCentre1.y - spriteCentre2.y;
NSLog(#"x axis %f",xAxisDiff);
NSLog(#"y axis %f",yAxisDiff);
if((spriteCentre1.x < spriteCentre2.x) && (xAxisDiff < yAxisDiff))
{
NSLog(#"right touch");
}
else if((spriteCentre2.x < spriteCentre1.x) && (xAxisDiff > yAxisDiff))
{
NSLog(#"left touch");
}
else if((spriteCentre2.y < spriteCentre1.y) && (yAxisDiff > xAxisDiff))
{
NSLog(#"Bottom touch");
}
else if((spriteCentre1.y < spriteCentre2.y) && (yAxisDiff < xAxisDiff))
{
NSLog(#"Top touch");
}
}
I'm trying to use CMDeviceMotion to track when the iPhone is tilted backwards, and then do something. I've successfully created the CMMotionManager, I'm getting motion data from the system, and I'm filtering for results above a certain acceleration.
What I want to do though, is detect when the device is being tilted backwards or forwards above a certain speed. How do I do that?
Here's the code I have so far:
UPDATE: I think I solved it. I was looking for the rotationRate property, CMRotationRate. I've paired them together, I really only need the x value, so I'll keep working on it. If anyone has some tips at the below code, it's much appreciated.
- (void)startMotionManager{
if (motionManager == nil) {
motionManager = [[CMMotionManager alloc] init];
}
motionManager.deviceMotionUpdateInterval = 1/15.0;
if (motionManager.deviceMotionAvailable) {
NSLog(#"Device Motion Available");
[motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue]
withHandler: ^(CMDeviceMotion *motion, NSError *error){
//CMAttitude *attitude = motion.attitude;
//NSLog(#"rotation rate = [%f, %f, %f]", attitude.pitch, attitude.roll, attitude.yaw);
[self performSelectorOnMainThread:#selector(handleDeviceMotion:) withObject:motion waitUntilDone:YES];
}];
//[motionManager startDeviceMotionUpdates];
} else {
NSLog(#"No device motion on device.");
[self setMotionManager:nil];
}
}
- (void)handleDeviceMotion:(CMDeviceMotion*)motion{
CMAttitude *attitude = motion.attitude;
float accelerationThreshold = 0.2; // or whatever is appropriate - play around with different values
CMAcceleration userAcceleration = motion.userAcceleration;
float rotationRateThreshold = 7.0;
CMRotationRate rotationRate = motion.rotationRate;
if ((rotationRate.x) > rotationRateThreshold) {
if (fabs(userAcceleration.x) > accelerationThreshold || fabs(userAcceleration.y) > accelerationThreshold || fabs(userAcceleration.z) > accelerationThreshold) {
NSLog(#"rotation rate = [Pitch: %f, Roll: %f, Yaw: %f]", attitude.pitch, attitude.roll, attitude.yaw);
NSLog(#"motion.rotationRate = %f", rotationRate.x);
[self showMenuAnimated:YES];
}
}
else if ((-rotationRate.x) > rotationRateThreshold) {
if (fabs(userAcceleration.x) > accelerationThreshold || fabs(userAcceleration.y) > accelerationThreshold || fabs(userAcceleration.z) > accelerationThreshold) {
NSLog(#"rotation rate = [Pitch: %f, Roll: %f, Yaw: %f]", attitude.pitch, attitude.roll, attitude.yaw);
NSLog(#"motion.rotationRate = %f", rotationRate.x);
[self dismissMenuAnimated:YES];
}
}
}
Have you looked at CMAttitude? Sounds like what you need, plus some mathematics I guess.
EDIT: Ok you did.
Quoting Apple documentation, at chapter "Getting the Current Device Orientation" :
If you need to know only the general
orientation of the device, and not the
exact vector of orientation, you
should use the methods of the UIDevice
class to retrieve that information.
Using the UIDevice interface is simple
and does not require that you
calculate the orientation vector
yourself.
Great, they do the maths for us. Then I guess that tracking acceleration like you do + tracking orientation, as above documentation explains, should do the job, using UIDeviceOrientationFaceUp and UIDeviceOrientationFaceDown ?
If none of the UIDeviceOrientation fit your needs, what you will need is to calculate some spatial angles from two CMAttitude references, which provides a rotationMatrix and a quaternion... these school maths are far away for me... I would then suggest to maybe search/ask a math only question with these.
So I a little backstory. I wanted to implement a particle effect and sound effect that both last about 3 sec or so when the user shakes their iDevice. But first issue arrived when the build in UIEvent for shakes refused to work. So I took the advice of a few Cocos veterans to just use some script to get "violent" accelerometer inputs as shakes. Worked great until now.
The problem is that if you keep shaking it just stacks the particle and sounds over and over. Now this wouldn't be that big of a deal except it happens even if you are careful to try and not do so. So what I am hoping to do is disable the accelerometer when the particle effect/sound effect start and then reenable it as soon as they finish. Now I don't know if I should do this by schedule, NStimer, or some other function. I am open to ALL suggestions. here is my current "shake" code.
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
const float violence = 1;
static BOOL beenhere;
BOOL shake = FALSE;
if (beenhere) return;
beenhere = TRUE;
if (acceleration.x > violence * 1.5 || acceleration.x < (-1.5* violence))
shake = TRUE;
if (acceleration.y > violence * 2 || acceleration.y < (-2 * violence))
shake = TRUE;
if (acceleration.z > violence * 3 || acceleration.z < (-3 * violence))
shake = TRUE;
if (shake) {
id particleSystem = [CCParticleSystemQuad particleWithFile:#"particle.plist"];
[self addChild: particleSystem];
// Super simple Audio playback for sound effects!
[[SimpleAudioEngine sharedEngine] playEffect:#"Sound.mp3"];
shake = FALSE;
}
beenhere = FALSE;
}
UIAcceleration has a timestamp property. I would modify your code to save the current timestamp on a succesful shake in a static variable (perhaps static NSTimeInterval timestampOfLastShake?). Then modify if (shake) to if (shake && acceleration.timestamp - 3.0f >= timestampOfLastShake)
resulting code:
static NSTimeInterval timestampOfLastShake = 0.0f;
if (shake && acceleration.timestamp - 3.0f >= timestampOfLastShake ) {
timestampOfLastShake = acceleration.timestamp;
id particleSystem = [CCParticleSystemQuad particleWithFile:#"particle.plist"];
[self addChild: particleSystem];
// Super simple Audio playback for sound effects!
[[SimpleAudioEngine sharedEngine] playEffect:#"Sound.mp3"];
shake = FALSE;
}
You realize that you are doing single axis acceleration checks and you are not ensuring the acceleration is repeated (i.e. shake). In other words, if you drop your phone, your code will think it's someone shaking the device back-and-fourth few times (which is what a shake is) and fire many times a second. So, either apply multi-axis checks against time or simply use the shake UIEvent. All you will need to do is in your UIView (or UIWindow better yet), implement - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event making sure the view becomes first responder. This will take care of all acceleration filtering, etc. and app won't be bombarded by all the acceleration noise (you can drop your phone on the table and it won't mistake that for a shake).
Go here for doc: http://developer.apple.com/library/ios/#documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/MotionEvents/MotionEvents.html
Or:
- (BOOL)canBecomeFirstResponder {
return YES;
}
// Now call [self becomeFirstResponder]; somewhere, say in viewDidAppear of the controller.
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (event.subtype == UIEventSubtypeMotionShake) {
// You've got a shake, do something
}
}
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
}
I know how to do shake for the iPhone has been asked a million times on here, but I can't seem to find anything useful regarding the accelerometer with Cocos2D. Everything i have found involves using views and I don't think i am using any views in Cocos2D, if I am they are hidden from me I think. I want to be able to tell when any sort of shake has occured within a CCLayer class?
I figured it out. In the layer class you need to put these lines;
self.isAccelerometerEnabled = YES;
[[UIAccelerometer sharedAccelerometer] setUpdateInterval:1/60];
shake_once = false;
Then implement this function in the layer class;
-(void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
float THRESHOLD = 2;
if (acceleration.x > THRESHOLD || acceleration.x < -THRESHOLD ||
acceleration.y > THRESHOLD || acceleration.y < -THRESHOLD ||
acceleration.z > THRESHOLD || acceleration.z < -THRESHOLD) {
if (!shake_once) {
int derp = 22/7;
shake_once = true;
}
}
else {
shake_once = false;
}
}
shake_once is just a boolean to stop one shake from being registered more than once.
Is there any way to measure scroll performance in an iPhone app, e.g. updates per second? I'm trying various techniques to improve the scrolling performance but it's sometimes hard to judge if they're actually having an effect.
if you have a scroll delegate, you can implement the method scrollViewDidScroll:. Specifically:
//header:
#interface MyClass : NSObject <UIScrollViewDelegate> {
CFAbsoluteTime last;
int updateCount;
CFTimeInterval timeTotal;
}
#end
//implementation:
#implementation MyClass
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CFAbsoluteTime time = CFAbsoluteTimeGetCurrent();
if (last > 0) {
timeTotal += time - last;
++updateCount;
if (timeTotal > 1) {
NSLog(#"Updates Per Second: %.2f", updateCount / timeTotal);
updateCount = 0;
timeTotal = 0;
}
}
last = time;
}
#end
Something like that. It's untested, so the logging may be incorrect. But to use it, just assign your delegate to the scrollview.delegate property.