touchBegan timestamp - iphone

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;
}
}

Related

get timespan of swipe action in iPhone app

How can we get the time a user spent to finish a swipe right or swipe left action? Already googled but no luck. If there is not built-in solution already, will touch begin and touch end together work?
Measure the start and end time, and calculate the difference in the UIGestureRecognizer callback method for gestureRecognizer.state == UIGestureRecognizerStateBegan and gestureRecognizer.state == UIGestureRecognizerStateEnded
You can do this by getting the date (which you create as ivar or property) in the touchesBegan method like this:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
touchStartDate = [NSDate date];
}
Then you fetch the difference in the touches end method:
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
NSTimeInterval *touchDuration = [[NSDate date] timeIntervalSinceDate:touchStartDate];
NSLog(#"Touchduration %f",touchDuration);
}
If you only want to get the duration of a swipe left/right gesture you also need to check if the x value of the touch changed. This should be easy.

iOS Loses Touches?

I can't find anything to explain lost UITouch events. If you smash your full hand on the screen enough times, the number of touchesBegan will be different than the number of touchesEnded! I think the only way to actually know about these orphaned touches will be to reference them myself and keep track of how long they haven't moved.
Sample code:
int touchesStarted = 0;
int touchesFinished = 0;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
touchesStarted += touches.count;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
touchesFinished += touches.count;
NSLog(#"%d / %d", touchesStarted, touchesFinished);
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesEnded:touches withEvent:event];
}
Don't forget about touchesCancelled: UIResponder reference
Editing in response to the poster's update:
Each touch object provides what phase it is in:
typedef enum {
UITouchPhaseBegan,
UITouchPhaseMoved,
UITouchPhaseStationary,
UITouchPhaseEnded,
UITouchPhaseCancelled,
} UITouchPhase;
I believe that if a touch starts and ends in the same touch event set, -touchesBegan:withEvent: will be called but will contain touches which have ended or cancelled.
You should change your counting code, then, to look like this:
int touchesStarted = 0;
int touchesFinished = 0;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self customTouchHandler:touches];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self customTouchHandler:touches];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self customTouchHandler:touches];
}
- (void)customTouchHandler:(NSSet *)touches
{
for(UITouch* touch in touches){
if(touch.phase == UITouchPhaseBegan)
touchesStarted++;
if(touch.phase == UITouchPhaseEnded || touch.phase == UITouchPhaseCancelled)
touchesFinished++;
}
NSLog(#"%d / %d", touchesStarted, touchesFinished);
}
Every single touch event will go through both phases of started and finished/cancelled, and so your counts should match up as soon as your fingers are off the screen.
One thing to remember... You may receive some touches that have multiple taps. Don't forget to take tapCount into account.
However, if you still have the problem, you can consider all touches from the event, though it presents some other management issues...
HACK ALERT
I have coded the following HACK to work around this. Sometimes the touchesEnded does not get called, BUT, the touches show up as part of all the touches in the event.
Note, that you can now process the same "canceled" or "ended" touch multiple times. If that is a problem, you have to keep your own state of "pending" touches, and remove them when done.
Yeah, it all is pretty bad, but I don't know how to overcome this issue without a similar hack. The basic solution is to look at all the touches in each event, and process them based on their phase, calling the appropriate ended/canceled when they are seen.
- (void) touchesEndedOrCancelled:(NSSet *)touches
{
__block NSMutableSet *ended = nil;
__block NSMutableSet *canceled = nil;
[touches enumerateObjectsUsingBlock:^(UITouch *touch, BOOL *stop) {
if (touch.phase == UITouchPhaseEnded) {
if (!ended) ended = [NSSet setWithObject:touch];
else [ended addObject:touch];
} else if (touch.phase == UITouchPhaseCancelled) {
if (!canceled) canceled = [NSSet setWithObject:touch];
else [canceled addObject:touch];
}
}];
if (ended) [self touchesEnded:ended withEvent:nil];
if (canceled) [self touchesCancelled:canceled withEvent:nil];
}
Then, call it at the end of touchesBegan and touchesMoved...
[self touchesEndedOrCancelled:event.allTouches];
For this to work, touchesEnded/Canceled needs to not choke on a nil event. Also, the "other" needs to be handled. In touchesEnded...
[self touchesCancelled:[event.allTouches objectsPassingTest:^BOOL(UITouch *touch, BOOL *stop) {
return touch.phase == UITouchPhaseCancelled;
}] withEvent:nil];
and in touchesCanceled...
[self touchesEnded:[event.allTouches objectsPassingTest:^BOOL(UITouch *touch, BOOL *stop) {
return touch.phase == UITouchPhaseEnded;
}] withEvent:nil];
You may need to provide more detail but....
If you run your finger off the edge of the screen this event will show touch started and touch moved but no end as it didn't actually end (lift finger). This could be your problem, that the event didn't happen. Plus there is a limit to the amount of touches in multi touch if you press say 10 times its then up to the system to determine what events are really and what are false, it seems like you are using the hardware incorrectly.

Measuring the time taken when shaking device

I am developing an application that measures the total time of a shake event. In other words, the timer starts when the shake event is started, and the timer stops when the shake event ends.
So my question is, if we hold two iTouches in a single hand and then shake both at the same time, will the shake event generate the same value on both iTouches?
This is my code:
-(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (motion == UIEventSubtypeMotionShake) {
timer = [NSTimer scheduledTimerWithTimeInterval:0.00025 target:self selector:#selector(showactivity) userInfo:nil repeats:YES];
}
}
-(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (motion == UIEventSubtypeMotionShake) {
[timer invalidate];
}
}
I'd just make an instance variable of type NSDate called shakesBegan, or whatever. Then just call this in motionBegan:
-(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (motion == UIEventSubtypeMotionShake) {
shakesBegan = [NSDate date];
}
}
-(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (motion == UIEventSubtypeMotionShake) {
NSDate *shakesFinished = [NSDate date];
NSTimeInterval shakeTime = [shakesFinished timeIntervalSinceDate:shakesBegan];
NSLog(#"Shaken for %f seconds",shakeTime);
}
}
The output should say something like Shaken for 1.233242 seconds. However, I don't have a device to test this so your mileage may vary.
The reason you're getting an error is because you must make shakesBegan an instance variable. Like this in your interface:
#interface Something : UIViewController
{
NSDate *shakesBegan;
}
#end

Cocos2d shake/accelerometer issue

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
{
}

Anyone have code to substitute for UILongPressGestureRecognizer?

I'd like to support pre 3.2 and this is the only symbol that doesn't want to cooperate, anyone know of some touchesmoved code or something i can use in lieu of the UILongPressGestureRecognizer?
Thanks,
Nick
As you know, you should use touchesBegan, Moved, Ended, and Canceled functions for pre 3.2 iOS.
I think implementing only touchesMoved is bad because if the user presses and doesn't move at all until releasing, then touchesMoved won't get called ever.
Instead, I used NSTimer to acheive a long press touch event.
This might not be an optimal solution, but it worked well for my app.
Here's a snippet of code.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
isAvailable = NO;
timer = [NSTimer scheduledTimerWithTimeInterval:DURATION target:self selector:#selector(didPassTime:) userInfo:nil repeats:NO];
}
- (void)didPassTime:(id)sender{
isAvailable = YES;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
if(isAvailable == YES){
// still pressing after 0.5 seconds
}
else{
// still pressing before 0.5 seconds
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
if(isAvailable == YES){
// releasing a finger after 0.5 seconds
}
else {
// releasing a finger before 0.5 seconds
[timer invalidate];
timer = nil;
}
}