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
{
}
Related
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?
We want to count number of shakes done by user.
We tried motionBegan, motionEnded but its of no use.
Because they are fired only when user start a shake or ends a shake but i want to count shakes continuously.
May be something like this, when user moves iPhone to left one side and right one side, I count it as one shake.
Any help would be appreciated.
Thanks
You could use the UIAccelerometer to achieve what you want.
You use motionBegin to detect a start :
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
UIAccelerometer* acc = [UIAccelorometer sharedAccelerometer];
acc.delegate = self;
acc.updateInterval = /* whatever you feel like OK */ 0.1;
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
UIAccelerometer* acc = [UIAccelorometer sharedAccelerometer];
acc.delegate = nil;
}
and in the delegate method :
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
//
// You detect a full shake
//
}
Before implementing this, you should know that UIAccelerometer has been deprecated in iOS5. This means you will have to use what Apple recommands to use instead. I'm not updated yet on the topic.
Here is the documentation about it : link.
In my game I need to calculate duration of touch. I did this by :
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
self.endTime = [NSDate date]; //NSDate *endTime in .h
NSLog(#"%#",self.endTime);
}
-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
tStart = [[NSDate date] timeIntervalSinceDate:self.endTime];
NSLog(#"duration %f",tStart);
I am using this time interval as a factor to calculate height of the jump the player makes.
Less is the tStart, low is the jump and more is the tStart, high is the jump. I am doing this as :
if(tStart/1000<=9.430)
{
[player jump:5.0f];
}
else if(tStart>9.430 && tStart<=9.470)
{
[player jump:7.0f];
}
else if(tStart/1000>9.470)
{
[player jump:8.0f];
}
However I want to perform this action on tochBegan so that player may jump as soon as screen is touched. For this need the value of tStart in touchBegan. How should I do that?
Thanks
For a given touch, the UITouch instance is the same, so at ccTouchBegan save the touch/touches with the oldest timestamp and then wait for ccTouchEnded. When you get the UITouch that you previously saved, it means the player lifted the finger.
update
You can
Jump as soon as the user touches the screen.
Jump and boost the jump while the touch is ongoing up to x milliseconds (suggestion from grapefrukt).
Jump when the user releases the touch or a max time of x milliseconds has elapsed.
Jump and boost the jump until the touch is over.
Option 4 is unpractical because the user can keep pressing as long as he wants. So given that you want to make a variable jump, here is code for option 3:
UITouch *jump = nil;
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
// no jump ongoing, start one
if (jump==nil){
jump = touch;
// jump on a separate thread
[NSThread performSelectorInBackground:(SEL)jump withObject:touch];
}
}
-(void) jump:(UITouch*) touch {
NSInterval time = [touch timestamp]; // I *think* this is the time since system uptime :P
int jumpSpeed = 1;
// increase the jump speed for every 100ms up to 'maxTimeYouAreAllowedToJump' ms
while ( ([NSProcessInfo systemUptime]-time < maxTimeYouAreAllowedToJump) && (jump!=nil) ){
jumpSpeed += 1;
[NSThread sleepForTimeInterval:.1]; // sleep for 100ms
}
[self doTheJumpingWithSpeed:jumpSpeed]; // do the actual jump!
}
-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event {
// user released the touch that initiated the jump
if ((touch!=nil) && (touch==jump)) {
jump =nil;
}
}
I am using http://www.chaosinmotion.com/flowcover.m and would like to know how I may display a label for each image when it is displayed in the flowcover (the current image)
Thanks,
:)
I suggest reading the documentation before posting questions. With no familiarity with this library at all, in 30 seconds, I downloaded the source, found the docs, and skimmed until I found - (void)flowCover:(FlowCoverView *)view didSelect:(int)cover, which appears to be a delegate method.
At a glance, it looks like you're not meant to know what's front and center, only what the user picked (which may very well mean "what just became front and center"). Test it and see.
You can easily get this notification by changing the Delegate Protocol in FlowCoverView.h like this:
#protocol FlowCoverViewDelegate
- (int)flowCoverNumberImages:(FlowCoverView *)view;
- (UIImage *)flowCover:(FlowCoverView *)view cover:(int)cover;
- (void)flowCover:(FlowCoverView *)view didSelect:(int)cover;
- (void)flowCover:(FlowCoverView *)view highlighted:(int)cover; //<-- Added
#end
And replacing your -[FlowCoverView driveAnimation] method in FlowCoverView.m with this:
- (void)driveAnimation
{
double elapsed = CACurrentMediaTime() - startTime;
if (elapsed >= runDelta) [self endAnimation];
else [self updateAnimationAtTime:elapsed];
/*
* Change by Uppfinnarn <uppfinnarn#gmail.com>.
* This will give the Delegate a message every time the highlighted cover (eg. the one in the center) changes.
* Can be used to display subtitles for example.
*/
if (delegate) [delegate flowCover:self highlighted:(int)floor(offset + 0.01)]; // make sure .99 is 1
}
Create a UILabel somewhere and place it on top of the FlowCoverView (make sure it's opaque is NO so that it doesn't create a black box behind it), then monitor -[flowCover:highlighted:] to know which cover is currently in the center and change the text accordingly.
Note that this won't give you a notification for when the view is first created, only once the animation moves. You have to figure out the first title yourself.
Also, this won't give you notifications while the user is dragging with their finger, only when it's "gliding freely".
EDIT:
Replace your -[FlowCoverView touchesMoved:withEvent:] method with this to get callbacks when the user is dragging with their fingers too:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
CGRect r = self.bounds;
UITouch *t = [touches anyObject];
CGPoint where = [t locationInView:self];
double pos = (where.x / r.size.width) * 10 - 5;
if (touchFlag) {
// determine if the user is dragging or not
int dx = fabs(where.x - startTouch.x);
int dy = fabs(where.y - startTouch.y);
if ((dx < 3) && (dy < 3)) return;
touchFlag = NO;
}
int max = [self numTiles]-1;
offset = startOff + (startPos - pos);
if (offset > max) offset = max;
if (offset < 0) offset = 0;
/*
* Change by Uppfinnarn <uppfinnarn#gmail.com>.
* Based on Ilkka Pirttimaa <ilkka.pirttimaa#gmail.com>'s solution for notifications.
* when the animation stops.
*
* This will give the Delegate a message every time the highlighted cover (eg. the one in the center) changes.
* Can be used to display subtitles for example.
*/
if(delegate) [delegate flowCover:self highlighted:(int)floor(offset + 0.01)];
[self draw];
double time = CACurrentMediaTime();
if (time - startTime > 0.2) {
startTime = time;
lastPos = pos;
}
}
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.