I have this line in one of my scenes:
[self schedule:#selector(storeValue:) interval:1.0/30.0];
storeValue is very basic and fast. It just stores the position of a layer on an NSMutableArray. I need this storeValue to be called in as much precise as possible timings, but after making some measurements, these are the intervals measured between each storeValue call:
interval 0 = 0
interval 1 = 0.049962
interval 2 = 0.033345
interval 3 = 0.033332
interval 4 = 0.049994
interval 5 = 0.050050
interval 6 = 0.049968
interval 7 = 0.033998
interval 8 = 0.049331
interval 9 = 0.050015
interval 10 = 0.049979
interval 11 = 0.049999
interval 12 = 0.033357
interval 13 = 0.033307
interval 14 = 0.049997
interval 15 = 0.033322
interval 16 = 0.050317
interval 17 = 0.049743
interval 18 = 0.049973
interval 19 = 0.033322
interval 20 = 0.050024
interval 21 = 0.049975
interval 22 = 0.049987
interval 23 = 0.033316
interval 24 = 0.050038
interval 25 = 0.050149
interval 26 = 0.049852
interval 27 = 0.049989
interval 28 = 0.050011
So, as you see, the method is called with a variety of intervals instead of always being 0.03333 (1 / 30).
I have tried to remove all code from storeValue to see how frequently storeValue is called and obtained the same irregular timings.
The big question is this: what should I do to improve schedule precision? Should I use NSTimer? GCD any other method of doing a scheduled task? Any suggestions?
thanks.
NOTE: I've discovered now that if I put interval = 0, storeValue will be called every frame, that means 1/60s and the precision is awesome. OK, I can make a logic to call storeValue half of the time but it would be nice to know why schedule is so imprecise and if there is a way to improve it.
When you schedule something, the selector and timer frequency are passed to Cocos2d sharedScheduler. Then sharedDirector calls [[Scheduler sharedScheduler] tick: dt]; on each frame. If your timer passes its threshold the scheduler calls your selector.
So the frequency cannot be more precise than mainloop frequency (max 1/60, min - depending on actual scene complexity). Probably 1/30 passes threshold not exactly on each 2nd frame and thats why you have not precise values in log.
I can propose to call your selector on other thread via NSTimer without using Cocos2d scheduler.
iOS isn't a Real-time operating system and thus you can never be guaranteed that your scheduled callback/NSTimer/etc. will ever occur at precisely 1/30 of a second. This precision will always vary based on what else the OS is handling.
What you can do, however, is use the actual time passed between each callback to account for this lack of consistency.
Related
I am using timer.performWithDelay to time how long it takes a player to complete a level. I want it to measure down to the 100th of a second (because the game is multiplayer, and I don't want there to be too many ties).
Here is what I did:
local totaltime = 0
local function counter()
totaltime = totaltime + 0.01
print(totaltime)
end
timer1 = timer.performWithDelay( 10, counter, 0)
It results in each "second" lasting about 4 seconds. Is this just not practical or is there a flaw somewhere?
When timer.preformWithDelay is given a time delay smaller then the time between your frames the timer will wait until the next frame is entered to call the function.
That means if you have a game running at 30 or 60 fps, you would have a 'frame ms' of about 16 or 33ms. So the minimum delay you can put is the delay between your frames.
In your case you want to set your timer every 1/100th of a second, or with 10ms. This means, since your frame is most likely 16ms (60fps), that every logged 10ms you are actually waiting an addional 6ms.
Now you could solve this if you ran with 100 FPS and thus achieved said 10 ms, but this is NOT recommendable.
AlanPlantPot provided the answer for following solution on coronaLabs:
I would use the enterFrame function instead. Your timer won't go up in single milliseconds (it will increase by however many ms have passed in each frame), but nobody would be able to read that fast anyway.
local prevFrameTime, currentFrameTime --both nil
local deltaFrameTime = 0
local totalTime = 0
local txt_counter = display.newText( totalTime, 0, 0, native.systemFont, 50 )
txt_counter.x = 150
txt_counter.y = 288
txt_counter:setTextColor( 255, 255, 255 )
group:insert( txt_counter )
and
local function enterFrame(e)
local currentFrameTime = system.getTimer()
--if this is still nil, then it is the first frame
--so no need to perform calculation
if prevFrameTime then
--calculate how many milliseconds since last frame
deltaFrameTime = currentFrameTime - prevFrameTime
end
prevFrameTime = currentFrameTime
--this is the total time in milliseconds
totalTime = totalTime + deltaFrameTime
--multiply by 0.001 to get time in seconds
txt_counter.text = totalTime * 0.001
end
I am working on a circular timer in iOS. The circle should contain a 24 hour cycle.
The picture below shows how i intend to change the time of the timer.
Instead of the position in degrees of the slider, I want to display the time accordingly to the position of the slider, but I'm kinda struggling to find the right formula. Does anyone have an idea how to tacke this?
Thank you in advance!
Granit
Trick some mathematical formulas;
like 360 / 24 = 15, it means for 1 hour it rotates 15 degree,
so calculate for the others:
1 hour = 15 degree
2 hours = (15 * 2) = 30 degree and so on....
...
24 hours = (15 * 24) = 360 degree.
So if you get 273 value, it means
273/24 = int (11.375) = 11 hours
I am using timer.performWithDelay to time how long it takes a player to complete a level. I want it to measure down to the 100th of a second (because the game is multiplayer, and I don't want there to be too many ties).
Here is what I did:
local totaltime = 0
local function counter()
totaltime = totaltime + 0.01
print(totaltime)
end
timer1 = timer.performWithDelay( 10, counter, 0)
It results in each "second" lasting about 4 seconds. Is this just not practical or is there a flaw somewhere?
When timer.preformWithDelay is given a time delay smaller then the time between your frames the timer will wait until the next frame is entered to call the function.
That means if you have a game running at 30 or 60 fps, you would have a 'frame ms' of about 16 or 33ms. So the minimum delay you can put is the delay between your frames.
In your case you want to set your timer every 1/100th of a second, or with 10ms. This means, since your frame is most likely 16ms (60fps), that every logged 10ms you are actually waiting an addional 6ms.
Now you could solve this if you ran with 100 FPS and thus achieved said 10 ms, but this is NOT recommendable.
AlanPlantPot provided the answer for following solution on coronaLabs:
I would use the enterFrame function instead. Your timer won't go up in single milliseconds (it will increase by however many ms have passed in each frame), but nobody would be able to read that fast anyway.
local prevFrameTime, currentFrameTime --both nil
local deltaFrameTime = 0
local totalTime = 0
local txt_counter = display.newText( totalTime, 0, 0, native.systemFont, 50 )
txt_counter.x = 150
txt_counter.y = 288
txt_counter:setTextColor( 255, 255, 255 )
group:insert( txt_counter )
and
local function enterFrame(e)
local currentFrameTime = system.getTimer()
--if this is still nil, then it is the first frame
--so no need to perform calculation
if prevFrameTime then
--calculate how many milliseconds since last frame
deltaFrameTime = currentFrameTime - prevFrameTime
end
prevFrameTime = currentFrameTime
--this is the total time in milliseconds
totalTime = totalTime + deltaFrameTime
--multiply by 0.001 to get time in seconds
txt_counter.text = totalTime * 0.001
end
I'm attempting to convert MPH into minute miles. I'm currently running code to do this by doing 60 / the miles per hour which gives me the result in minute miles.
For example 60/8mph = 7.5
However the answer I get I need to convert into minutes and seconds so that I would have 7 minutes 30 seconds. Is there a way I can get the numbers after the decimal point so I can multiply it by 60 to convert it to seconds, then add it back to the minutes.
You can use remainder,
double remainder = fmod(a_double, another_double);
should include <math.h>
Well, I don't know whether there is an existing class that handles this, but to answer your specific question, the fractional part of the decimal (mantissa?) would be:
((60 % 8) / 8.0f)
You can multiply that by 60.
Do it in seconds...
3600/8 = 450
450/60 = 7 remainder 30
= 7:30
It's pretty simple, you're on the right path actually.
What you need to do is:
Get Minutes
Get Seconds
Convert seconds from int to real time (0.5 to 30, etc..)
Add seconds to minutes
Get minutes by casting it to an Integer:
int minutes = 60/8;
Get seconds by using the remainder:
float seconds = 60%8;
Convert seconds to real time:
int realSeconds = seconds * 60;
Now get result back by adding both:
int totalSeconds = minuts + realSeconds;
Here's a little function that does it (typed directly to browser, probably won't compile)
#include <math.h>
int getMinuteMiles(float mph){
int minutes = 60/mph;
double seconds = fmod(60, mph);
int realSeconds = seconds * 60;
return minutes+realSeconds;
}
Trying to work out where I have screwed up with trying to create a count down timer which displays seconds and milliseconds. The idea is the timer displays the count down time to an NSString which updates a UILable.
The code I currently have is
-(void)timerRun {
if (self.timerPageView.startCountdown) {
NSLog(#"%i",self.timerPageView.xtime);
self.timerPageView.sec = self.timerPageView.sec - 1;
seconds = (self.timerPageView.sec % 60) % 60 ;
milliseconds = (self.timerPageView.sec % 60) % 1000;
NSString *timerOutput = [NSString stringWithFormat:#"%i:%i", seconds, milliseconds];
self.timerPageView.timerText.text = timerOutput;
if (self.timerPageView.resetTimer == YES) {
[self setTimer];
}
}
else {
}
}
-(void)setTimer{
if (self.timerPageView.xtime == 0) {
self.timerPageView.xtime = 60000;
}
self.timerPageView.sec = self.timerPageView.xtime;
self.timerPageView.countdownTimer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(timerRun) userInfo:Nil repeats:YES];
self.timerPageView.resetTimer = NO;
}
int seconds;
int milliseconds;
int minutes;
}
Anyone got any ideas what I am doing wrong?
You have a timer that will execute roughly 100 times per second (interval of 0.01).
You decrement a value by 1 each time. Therefore, your self.timerPageView.sec variable appears to be hundredths of a second.
To get the number of seconds, you need to divide this value by 100. To get the number of milliseconds, you need to multiply by 10 then modulo by 1000.
seconds = self.timerPageView.sec / 100;
milliseconds = (self.timerPageView.sec * 10) % 1000;
Update:
Also note that your timer is highly inaccurate. The timer will not repeat EXACTLY every hundredth of a second. It may only run 80 times per second or some other inexact rate.
A better approach would be to get the current time at the start. Then inside your timerRun method you get the current time again. Subtract the two numbers. This will give the actual elapsed time. Use this instead of decrementing a value each loop.
You set a time interval of 0.01, which is every 10 milliseconds, 0.001 is every millisecond.
Even so, NSTimer is not that accurate, you probably won't get it to work every 1 ms. It is fired from the run loop so there is latency and jitter.
These calculations look pretty suspect:
seconds = (self.timerPageView.sec % 60) % 60 ;
milliseconds = (self.timerPageView.sec % 60) % 1000;
You are using int type calculations (pretty sketchy in their implementation) on a float value for seconds.
NSUInteger seconds = (NSUInteger)(self.timerPageView.sec * 100); //convert to an int
NSUInteger milliseconds = (NSUInteger) ((self.timerPageView.sec - seconds)* 1000.);