Cocos2d, How to make a score timer - iphone

I am still new to cocos2d and was wondering if any of you guys can help me make a score timer. I seen some questions like this but looks like it is for a way older version of cocos2d. I want the timer to start at zero and go up in mins and seconds. Really appreciate any help or advice I get.

Start a NSTimer that runs every second and calls a method that updates the timer.
Define an integer called seconds and another called minutes in the header.
-(void)updateTheTime {
seconds++;
if (seconds == 60) {
seconds = 0;
minutes++;
}
label.text = [NSString stringWithFormat:#"%i:%i", minutes, seconds];
}

Related

Timer drags a bit when too much is happening

I'm working on a timer that needs to do some calculations and run some functions at a certain interval. I'm trying to keep the interval as big as possible, but I need it to be kind of fine grained.
Here is the periodic timer with some of the stuff that needs to happen.
So as you can see, every second (the milliseconds passed % 1000 == 0) it will do some stuff if some conditions are met. But also every 10 milliseconds I need to check some stuff.
It seems this is a bit too much, and after running the timer for 2 minutes it already drags 1 second behind. I guess I'm approaching this the wrong way. Could/should I somehow put all that logic in a function that just runs async so the timer can just keep going.
It's not the end of the world if the timer display drags for a few milliseconds every now and then, if it catches up later. But now the whole timer just drags.
_timer = Timer.periodic(Duration(milliseconds: 10), (timer) {
passedMilliseconds = passedMilliseconds + 10;
// METRONOME BEEP AND BLINK
if (passedMilliseconds % currentTimerSettingsObject.milliSecondDivider == 0) {
_playMetronomeBeep();
_startMetronomeBlink();
}
// ONE SECOND
if (passedMilliseconds % 1000 == 0) {
secondsDuration--;
// COUNTDOWN
if (secondsDuration < currentTimerSettingsObject.countDown + 1) {
_player.play('sounds/beep.mp3');
}
// SET COUNTDOWN START VALUES
if (secondsDuration == currentTimerSettingsObject.countDown) {
isCountdown = true;
}
notifyListeners();
}
// TIME IS UP
if (secondsDuration < 0) {
switchToNextTimer();
notifyListeners();
}
});
}
You cannot rely on a timer to deliver events exactly on time. You need to use a more exact method than simply incrementing a counter by 10 on every tick. One example would be to start a Stopwatch before the timer and then (knowing that your ticks will only be on approximately 10ms intervals) read stopwatch.elapsedMilliseconds and base your decisions on that.
You will need to change your logic a bit. For example, you want to know when you pass a 1 second boundary. Previously, with your exact increments of 10 you knew you would eventually reach a round 1000. Now, you might see 995 followed by 1006, and need to deduce that you've crossed a second boundary to run your per second logic.

Coroutine Countdown Timer being extremely slow

Hey guys so just as a disclaimer I'm relatively new to programming so if I'm making some super obvious mistake please go easy on me
So I'm trying to create a higher customizable Countdown timer for my game, and I want it to be able to be accurate to 0.01 Seconds. I decided I would use the Coroutine method for creating my timer instead of the delta-time one I have seen a couple of times, thinking that this would be a more efficient approach. My game is not very intensive and thus easily runs on hundreds of frames per second, so I thought that using Waitforseconds(0.01) is going to work better because it only needs to be called 100 times every second rather than multiple hundreds. however, I have come into a major issue with my timer. It is EXTREMELY slow. I ran the countdown timer on google and mine side by side starting at 25 seconds and it beat mine out by ten seconds. I even tried adding a artifical delay thinking the waitforseconds function was overshooting, so I would have the time tick down 0.01 seconds when a bit less then that had passed, but my results ended up being sort of inconsistent. Here is my code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class TimerScript : MonoBehaviour
{
public Text textDisplay;
private double secondsLeft = 30;
public bool takingAway = false;
private string Texttodisplay;
public int Milisecondsdigits = 2;
void Start()
{
textDisplay = GetComponent<Text>();
Texttodisplay = "00:" + secondsLeft;
if(Milisecondsdigits == 0)
{
Milisecondsdigits = -1;
}
}
void Update()
{
if (takingAway == false && secondsLeft > 0)
{
StopAllCoroutines();
StartCoroutine(TimerTake());
}
if(Texttodisplay.Length > 8 - (Mathf.Abs(Milisecondsdigits -2)))
{
Texttodisplay = Texttodisplay.Substring(0,8- (Mathf.Abs(Milisecondsdigits -2)));
}
textDisplay.text = Texttodisplay;
}
IEnumerator TimerTake()
{
takingAway = true;
yield return new WaitForSeconds(0.01f);
secondsLeft -= 0.01;
if(secondsLeft < 10)
{
Texttodisplay = "00:0" + secondsLeft;
}
else
{
Texttodisplay = "00:" + secondsLeft;
}
takingAway = false;
}
}
could somebody please let me know how I could cause this to become more accurate or why it's acting extremely inaccurate currently :/
Coroutine events like WaitForSeconds trigger at a defined point in Unity's event cycle, which takes place after Update() is processed (see
https://docs.unity3d.com/Manual/ExecutionOrder.html). This defined execution point might not line up exactly with the timer delay. This means it may wait longer than you want it to.
In your example, you tell it to wait for 0.01 seconds. Let's say that you are running a game at 30 frames per second. The frame time for 30 frames per second is 1/30 seconds, or approximately 0.0333 seconds. WaitForSeconds will then wait for the next frame, and 0.0333 seconds passes until the next frame. Then the next WaitForSeconds event cycle, it sees that the delay has passed and triggers, but you actually waited over 3 times as long as you wanted to because of the delay between event cycles. Since your code assumes that WaitForSeconds had only waited for 0.01 seconds, you will end up waiting longer than you intended to. This normally doesn't matter in a lot of applications, but when accumulating with frequent short delays it certainly does.
To solve this, you have two choices:
Accumulate time manually using Time.deltaTime in Update().
Coroutines likely check their completion status per frame in a
similar way in the yield WaitForSeconds event. If coroutines
check if they need to continue every frame, doing this manually with
Time.deltaTime might not be any less efficient at all than a
coroutine. You will have to benchmark to find out, because coroutines being more efficient isn't a safe assumption.
Use Time.time (or Time.realtimeSinceStartup if you want it unscaled) to measure the actual span of time that elapsed after the WaitForSeconds trigger, and use that as what you subtract from your remaining time.
There is also an additional consideration here that if you want your
text display to update at specific regular intervals, you will want
to dynamically adjust what value you pass into WaitForSeconds to
compensate for its drift.
Do you try to do it without Corroutine in a fixed update? Fixed update refresh every 0.02 seconds by default but you can settup to run in 0.01 seconds in Edit > Settings > Time > Fixed Timestep.
Replace the corroutine with a function in FixedUpdate
There is a link with better explation how works fixedupdate.
FixedUpdate Unity

how to make tabata timer in swift

I'm trying to make countdown timer(user can adjust time) with 30 sec interval function.
It's my first coding!
I've done building interface....so on.
I need some guide with building timer.
The function is like this.
User can set time with +30sec, +1 min, +5min button
total time have to display like this. 00:00
Another '30sec count-down timer' below total time
'30sec count-down' repeats until total time end.(Like tabata interval timer)
That's it...
A little hint can help me a lot.
Thank you!
Timer.scheduledTimer(withTimeInterval: 30, repeats: true) { timer in
foo()
}
Use this timer initializer.

How to loop play midi in MusicPlayer

I'm using below code to play a midi file in ios, but how can I loop the song?
is there sth like player.loop = true?
NewMusicSequence(&mySequence);
NSURL * midiFileURL = [NSURL fileURLWithPath:midPath];
MusicSequenceFileLoad(mySequence, (__bridge CFURLRef)midiFileURL, 0, kMusicSequenceLoadSMF_ChannelsToTracks);`
NewMusicPlayer(&player);
MusicPlayerSetSequence(player, mySequence);
MusicPlayerPreroll(player);
MusicPlayerStart(player);
Thanks in advance.
I resolved it by setting loop info for each of the track.
UInt32 tracks;
if (MusicSequenceGetTrackCount(sequence, &tracks) != noErr)
for (UInt32 i = 0; i < tracks; i++) {
MusicTrack track = NULL;
MusicTimeStamp trackLen = 0;
UInt32 trackLenLen = sizeof(trackLen);
MusicSequenceGetIndTrack(sequence, i, &track);
MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength, &trackLen, &trackLenLen);
MusicTrackLoopInfo loopInfo = { trackLen, 0 };
MusicTrackSetProperty(track, kSequenceTrackProperty_LoopInfo, &loopInfo, sizeof(loopInfo));
NSLog(#"track length is %f", trackLen);
}
In case someone is having a problem looping a music sequence and following Rick Li's instructions, or the instructions here: https://gist.github.com/genedelisa/7b440f128db96c3ba66f , I found that MusicTrackLoopInfo code only worked when the duration of my track was actually short enough that I had the patience to sit and listen to it loop. For example, I tried to loop a track with a single click at the beginning so that it would repeat every second. But the duration of the single note was actually 100 seconds, so even if I set the looping as Rick Li explains above, I would have to wait nearly two minutes to hear it loop. I hope this helps someone who is confused or frustrated that they cannot get a music sequence / MIDI track to loop in iOS / AudioToolbox. Make sure to call GetProperty kSequenceTrackProperty_TrackLength and check that your trackLen is not some large value, even if you are convinced it is some small value!

Memory Leaks - Formatting a String To Display Time, Each Second

Hey guys. I have a method that gets called each second which I want to use to display the time that my app has been doing it's work. Currently the class I'm using (Which I did not create) has a property named progress which stores the total number of seconds.
I have already written some code which takes these seconds and formats it into a readable string. I'm new to this, so pardon me if it's not the best code. I welcome any suggestions:
// hours, minutes, and seconds are instance variables defined as integers
int totalSeconds = (int)streamer.progress;
hours = totalSeconds / (60 * 60);
if (hours > 0)
formattedTimeString = [NSString stringWithFormat:#"%d:", hours]; // WRONG
minutes = (totalSeconds / 60) % 60;
seconds = totalSeconds % 60;
[formattedTimeString stringByAppendingFormat:#"%d:%d", minutes, seconds]; // WRONG
Basically I want it to appear as "3:35" for example to show 3 minutes, 35 seconds. I only want to show the hour section if it has been an hour, in which case it would be "2:3:35" for example (Can anyone recommend a better way to format this?).
The problem I am having is where I actually create/set the string (The lines tagged WRONG). Since this is being done every second, I would easily get a leak if I keep asking for a new string object. I figure I can solve this by releasing the foramttedTimeString at the end of the method, but is this the correct way to accomplish this? Would an NSMutableString help in any way? Is there a better, Cocoa way of doing this? I already asked in #iphonedev # freenode and they said I would have to write this method myself, but I figured I'd ask again.
To provide context: this is an internet radio streaming app (I know there are many already, but I'm just practicing). I want to be able to show the amount of time the stream has been playing for.
Sorry if this question is stupid, heh, like I said I'm new to this.
I would do it something like:
int totalSeconds = (int)streamer.progress;
hours = totalSeconds / (60 * 60);
minutes = (totalSeconds / 60) % 60;
seconds = totalSeconds % 60;
if ( hours > 0 ) {
formattedTimeString = [NSString stringWithFormat:#"%d:%02d:%02d", hours, minutes, seconds];
} else {
formattedTimeString = [NSString stringWithFormat:#"%d:%02d", minutes, seconds];
}
Now at the end, formattedTimeString is the desired time, but you do not "own" it - you must retain it, or store it in a "copy" property if you wish to keep it around.
Note that the %02d gives you a guarenteed two digits, zero filled number, which is usually what you want for numbers in parts of times.
To see how you would do it with stringByAppendingFormat, it would look something like this:
NSString* formattedTimeString = #"";
if ( hours > 0 ) {
formattedTimeString = [formattedTimeString stringByAppendingFormat:#"%d:", hours];
}
formattedTimeString = [formattedTimeString stringByAppendingFormat:#"%d:%02d", minutes, seconds];
However in this case, you'll get times like 3:4:05, rether than a more desirable 3:04:05.
Note that formattedTimeString is being overwritten each time, but that is OK bvecause you do not "own" it at any time, so you are not responsible for releasing it.
Finally, to see it with a mutable string, it might look like this:
NSMutableString* formattedTimeString = [NSMutableString string];
if ( hours > 0 ) {
[formattedTimeString appendFormat:#"%d:", hours];
}
[formattedTimeString appendFormat:#"%d:%02d", minutes, seconds];
Again, the time result is the undesirable 3:4:05, and again you do not own formattedTimeString at the end, so it must be retained or stored with a copy property to keep it around.
For knowing the deltas as time units, you can also do something like this:
// as part of init...
self.gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
// in the timer or wherever you are tracking time deltas...
static NSUInteger unitFlags =
NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
NSDateComponents *components = [gregorian components:unitFlags
fromDate:myBaseTime
toDate:[NSDate date] options:0];
Then you can reference the parts with something like this [components minute].
Remember, you'll have to release the calendar in dealloc.
Your code looks pretty good. You're not leaking any memory because the string objects you create have a retain count of zero and will be cleaned up by the system. However, if formattedTimeString is not a local variable in your function, you need to retain it at the end to prevent this from happening! To do that, you would add [formattedTimeString retain] to the end of your code block, and then before replacing the string object you would add [formattedTimeString release].
As a general rule, functions with names containing "alloc", "copy", "create", and "new" return objects that have already been retained, (meaning their retain count is +1). It's your responsibility to call release or autorelease on these objects when you're done using them - or they will just start piling up in memory.
Functions like "stringWithFormat:", "imageNamed:", and "arrayWithCapacity:" all return objects with a retain count of zero - so you can safely discard them (as you are in the code sample). If you want to keep them around, you should call retain to make sure they are not cleaned up while you're using them.
All that said, I think the main problem is your use of stringByAppendingFormat:. Since the NSString you're using isn't mutable, that call returns a new string. You'd want to say:
formattedTimeString = [formattedTimeString stringByAppendingFormat:#"%d:%d", minutes, seconds];
Alternatively, you could use an NSMutableString. Since this is something you'll be doing over and over again, I'd recommend doing that. Technically, either way is fine though.
Hope that helps! The whole retain/release thing can get confusing. Just remember that each object has a "retainCount" and once it hits zero there's no telling what happens to the object or it's data.
Hey thanks guys I appreciate the responses.
I ended up doing this, and it works, but I would like to know if you guys see any problems with it:
int totalSeconds = (int)streamer.progress;
[formattedTimeString setString:#""];
hours = totalSeconds / (60 * 60);
if (hours > 0)
[formattedTimeString appendFormat:#"%d:", hours];
minutes = (totalSeconds / 60) % 60;
seconds = totalSeconds % 60;
[formattedTimeString appendFormat:#"%02d:%02d", minutes, seconds];
And then of course in viewDidLoad I instantiate the instance variable formattedTimeString:
formattedTimeString = [[NSMutableString alloc] initWithCapacity:8];
I did not do any retaining/releasing in the first code snippet because I didn't think it was necessary, but I could be wrong. I am, however, releasing in the dealloc method, so I should be fine there.