how can we count the time interval of the animation in cocos2d? - cocoa-touch

I am doing my program in cocos2d.
I am using NSDate to get the current time of the start of animation. And I know my animation takes 3 seconds. So I can get the time at completion of animation by using NSInterval and using the previous time and animation time. But, if If the animation time interval is not fixed how can I calculate the time interval of the animation and time at the completion of the animation ? I am animating a sprite. Please help how can I make it.
Thank You.

The CCIntervalAction class has a property called elapsed that gives you the number of seconds that have elapsed since the action started as a ccTime. Since the CCAnimate action derives from CCIntervalAction, you should have access to this property.
CCAnimation *myAnimation = [CCAnimation animationWithName:#"my animation" delay:0.1f];
CCAnimate *myAnimateAction = [CCAnimate actionWithAnimation:myAnimation];
[sprite runAction:myAnimateAction];
...
ccTime interval = myAnimateAction.elapsed;

Related

SKScene's update method gets a "currentTime" that is different from CFAbsoluteTimeGetCurrent()

The problem can be reproduced in an freshly created SpriteKit project.
After creating a new project, I add two lines to the update: method of the MyScene class:
-(void)update:(CFTimeInterval)currentTime {
NSLog(#" currentTime = %f", currentTime);
NSLog(#"CFAbsoluteTimeGetCurrent() = %f", CFAbsoluteTimeGetCurrent());
}
Running this shows that currentTime is several orders of magnitude smaller than the time returned by CFAbsoluteTimeGetCurrent(). I searched for possible reasons, but found none. The only possibility I could think of is that currentTime is the time frame since some other date than 00:00:00 01.01.2000, but I have no idea what other reference point it would be using, and found nothing when searching online either.
What can I do to get the current time, calculated by the same rules that are used to calculate currentTime?
Nothing, unless you're willing to disassemble and understand the Sprite Kit framework code.
Though the problem is most likely explained by Sprite Kit taking CFAbsoluteTimeGetCurrent at an earlier point in time, which is then passed into the update: method. Simply the overhead of sending that message to the scene means that the two times can't be the same. Or the currentTime may not be obtained by CFAbsoluteTimeGetCurrent but some other means.
What you can do is to get the currentTime of the very first time update: runs and store it in an ivar. You can then use this time as the reference time for the scene.
currentTime is a CFTimeInterval, so I expect it is the interval between some point in time and the current time - probably the interval since a scene was started. If you let your scene running for a minute, do the values get close to 60.0 (60 seconds)?
CFAbsoluteTimeGetCurrent is a CFAbsoluteTime, that is the number of seconds since Jan 1st 2001, so that should always be around 500 million or so.

Accuracy of NSTimer

I am trying to use NSTimer to create a Stop-watch style timer that increments every 0.1 seconds, but it seems to be running too fast sometimes ..
This is how I've done it:
Timer =[NSTimer scheduledTimerWithTimeInterval: 0.1 target:self selector:#selector(updateTimeLabel) userInfo:nil repeats: YES];
and then:
-(void)updateTimeLabel
{
maxTime=maxTime+0.1;
timerLabel.text =[NSString stringWithFormat:#"%.1f Seconds",maxTime];
}
This will display the value of the timer in the Label, and I can later utilize maxTime as the time when the Timer is stopped ...
THe problem is that it runs very inaccurately.
Is there a method where I can make sure that NSTimer fires strictly every 0.1 seconds accurately ? I know that NSTimer isn't accurate , and I'm asking for a tweak to make it accurate.
THanks
According to the NSTimer documentation, it is not meant to be accurate.
Because of the various input sources a typical run loop manages, the effective resolution of the time interval for a timer is limited to on the order of 50-100 milliseconds. If a timer’s firing time occurs during a long callout or while the run loop is in a mode that is not monitoring the timer, the timer does not fire until the next time the run loop checks the timer. Therefore, the actual time at which the timer fires potentially can be a significant period of time after the scheduled firing time.
You may want to use the dispatch_after function from GCD, which is suggested by the official documentation for this exact purpose (creating a timer).
If you want to perform a block once after a specified time interval, you can use the dispatch_after or dispatch_after_f function.
By the way, I agree with Caleb's answer. You probably are going to solve your problems if you don't accumulate error like your doing right now.
If you store the start date and recalculate the time at every iteration using the -timeIntervalSince: method, you're gonna end up with an accurate UI update, regardless of the timer precision.
Here's a class you can use to do what you want:
#interface StopWatch()
#property ( nonatomic, strong ) NSTimer * displayTimer ;
#property ( nonatomic ) CFAbsoluteTime startTime ;
#end
#implementation StopWatch
-(void)dealloc
{
[ self.displayTimer invalidate ] ;
}
-(void)startTimer
{
self.startTime = CFAbsoluteTimeGetCurrent() ;
self.displayTimer = [ NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:#selector( timerFired: ) userInfo:nil repeats:YES ] ;
}
-(void)stopTimer
{
[ self.displayTimer invalidate ] ;
self.displayTimer = nil ;
CFAbsoluteTime elapsedTime = CFAbsoluteTimeGetCurrent() - self.startTime ;
[ self updateDisplay:elapsedTime ] ;
}
-(void)timerFired:(NSTimer*)timer
{
CFAbsoluteTime elapsedTime = CFAbsoluteTimeGetCurrent() - self.startTime ;
[ self updateDisplay:elapsedTime ] ;
}
-(void)updateDisplay:(CFAbsoluteTime)elapsedTime
{
// update your label here
}
#end
The key points are:
do your timing by saving the system time when the stop watch is started into a variable.
when the the stop watch is stopped, calculate the elapsed time by subtracting the stop watch start time from the current time
update your display using your timer. It doesn't matter if your timer is accurate or not for this. If you are trying to guarantee display updates at least every 0.1s, you can try setting your timer interval to 1/2 the minimum update time (0.05s).
maxTime=maxTime+0.1;
This is the wrong way to go. You don't want to use a timer to accumulate the elapsed time because you'll be accumulating error along with it. Use the timer to periodically trigger a method that calculates the elapsed time using NSDate, and then updates the display. So, change your code to do something instead:
maxTime = [[NSDate date] timeIntervalSince:startDate];
NSTimer is not guaranteed to be accurate, although in practice it usually is (if you're not doing anything else on your main thread...). However, it's perfectly reasonable for updating a display... just don't use the callback to calculate your timer. Save the current time when you start your timer, and get the difference between now and when you started every time the timer fires. Then it doesn't really matter how accurately NSTimer is firing, it only impacts how many times a second your on screen display updates.

Time elapsed when battery increased of 5%

I would like to know if there's a way to get the time elapsed when the battery increased of 5%. For example, how can I know how much time elapsed between 60% and 65% ? I think I could do this with NSTimer, but I'm not able to do this, can someone help me ?
Thanks a lot.
If you are doing this for a Mac, please check this question for how to get battery life in Mac;
If you are doing this for iOS, please check this question for how to get battery life in iOS.
Simply use your NSTimer to fire the function to get the battery life every x seconds and when it gets to 60%, capture a timestamp with NSDate, then when it gets to 65%, capture another timestamp and compare the two timestamps to get the time difference: SO question: how to get time between 2 NSDate objects.
Good luck.
EDIT:
All the methods to get the battery percentage are in either the first or second link based on your platform. If you want it to determine the time between now, and 5% up/down:
//both percent and date should be properties or instance variables (NSDate and float, respectively)
//You should probably also make the timer one as well, so you can stop it in any method with [tmr invalidate];
date = [NSDate date];
percent = [self getBatteryPercent];
NSTimer* tmr = [NSTimer scheduledTimerWithInterval:1 target:self selector:#selector(someMethod) userInfo:nil repeats:YES];
- (float)getBatteryPercent
{
//You'll have to get this code from one of those methods (first or second link)
}
- (void)someMethod
{
float newPercent = [self getBatteryPercent];
if(percent - newPercent == 5.0 || percent - newPercent == -5.0)
{
//Get the time between timestamps
NSDate* newDate = [NSDate date];
//See third link for how to get the time difference between date and newDate
}
}
The rest is up to you.

How to Change Speed while Animation CABasicAnimation

In my application i am using CABasicAnimation for animation. I want to change the speed of the animation dynamically so i have added one slider to change the speed. Following is my animation code. But i am not able to change the speed, when i change the value of speed nothing happens.
CABasicAnimation * a = [CABasicAnimation animationWithKeyPath:#"position"];
[a setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
CGPoint startPt = CGPointMake(self.view.bounds.size.width + displayLabel.bounds.size.width / 2,
displayLabel.frame.origin.y);
CGPoint endPt = CGPointMake(displayLabel.bounds.size.width / -2, displayLabel.frame.origin.y);
[a setFromValue:[NSValue valueWithCGPoint:startPt]];
[a setToValue:[NSValue valueWithCGPoint:endPt]];
[a setAutoreverses:NO];
[a setDuration:speeds];
[a setRepeatCount:HUGE_VAL];
[displayLabel.layer addAnimation:a forKey:#"rotationAnimation"];
- (IBAction)speedSlider:(id)sender {
speeds = slider.value;
}
I think the best way to change speed is change your layer's time system
displayLabel.layer.timeOffset =
[displayLabel.layer convertTime:CACurrentMediaTime() fromLayer:nil];
displayLabel.layer.beginTime = CACurrentMediaTime();
displayLabel.layer.speed= slider.value;
You can see this for advance. https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreAnimation_guide/AdvancedAnimationTricks/AdvancedAnimationTricks.html#//apple_ref/doc/uid/TP40004514-CH8-SW2
EDIT: It looks like you will have a further problem, though: it doesn't look like you can change values like that on a running animation. You will have to remove the current animation and add a new one with the altered value. This may need a bit of care to prevent a jarring effect when you add the new animation.
From the thread above, you might be able to do this by not repeating the animation, but by using a delegate (see here) to keep re-adding the animation, and setting the new speed for the next animation cycle instead.
Original post:
You are changing the value that you had originally passed in to the animation. This isn't going to affect the running animation. You'll need to get a reference to that, and change the duration property of the animation object. Something like this in your action method:
CABasicAnimation *a = [displayLabel.layer animationForKey:#"rotationAnimation"];
a.duration = slider.value;
I think jrturton is correct that you can't change properties on an animation that is already running. But what you could do is break the animation into short segments and change the speed for the next segment when the slider value changes.
Instead of animating from point A to point D, you'd animate from A-B, then B-C, then C-D. Use the parent class's animationDidStop to check the current point, check the slider value, and kick off the next animation.
This might produce jerky motion, but if you use very small segments, you might be able to smooth it out.
u should stop the animation and restart a new with a new duration time
but remember to log down the fromValue and the toValue , and use the old toValue as the new fromValue to perform a seamless change
set speed as what you need.
a.duration=0.5;
Try this...
If you just want Autoscrolling text then you can also use one class
http://blog.stormyprods.com/2009/10/simple-scrolling-uilabel-for-iphone.html
It might also work in your case, try it.

NSTimer Count Up

I am trying to create an NSTimer that counts up from 00:00:00 to whenever the user presses stop. I have created an NSDateInterval of 1 second, but I can't get it to count up from 00:00:00, only the current date or a date that I set.
What is the best way to do this? And is NSTimer the best way to do this or should I just get the difference between the time the user presses the first button ([NSDate date] to set it) and the time at that second (a selector fired off by NSTimer)? I want to make sure there is as little accuracy fault as possible, and I am aware of NSTimer's problems with accuracy sometimes.
Save the time the timer starts with [NSDate date], and then record the time it stops as well. The display time will be the difference, displayed as you listed. The NSTimer will be used just to trigger an update, and you can set the interval fairly small, say 1/10th of a second. Something like:
[NSTimer scheduledTimerWithTimeInterval: 0.1f target: self
selector: #selector(updateTimeDisplay) userInfo: nil repeats: YES];