Reputation: 79
I have a question in regards to saving the high score in my game using NSUserDefaults. The thing is that the players score is their time and the way I'v added the time can be seen below:
_countTime = 0;
_text = [CCLabelTTF labelWithString:[NSString stringWithFormat:@" %02i", self.countTime] fontName:@"American Typewriter" fontSize:24];
_text.position = ccp(viewSize.width/2 +32,305);
_text.color = [CCColor redColor];
[self addChild:_text z: +5];
_text3 = [CCLabelTTF labelWithString:[NSString stringWithFormat:@" : : "] fontName:@"American Typewriter" fontSize:24];
_text3.position = ccp(viewSize.width/2,307);
_text3.color = [CCColor redColor];
[self addChild:_text3 z: +5];
[self schedule:@selector(countDown:) interval:0.01];// 0.01 second intervals
_countTime2 = 0;
_text2 = [CCLabelTTF labelWithString:[NSString stringWithFormat:@" %02i", self.countTime2] fontName:@"American Typewriter" fontSize:24];
_text2.position = ccp(viewSize.width/2 -3,305);
_text2.color = [CCColor redColor];
[self addChild:_text2 z: 5];
[self schedule:@selector(countDown2:) interval:1.00];// 1.0 second intervals
_countTime4 = 0;
_text4 = [CCLabelTTF labelWithString:[NSString stringWithFormat:@"%02i ", self.countTime4] fontName:@"American Typewriter" fontSize:24];
_text4.position = ccp(viewSize.width/2 -38,305);
_text4.color = [CCColor redColor];
[self addChild:_text4 z: 5];
[self schedule:@selector(countDown4:) interval:60.00];// 1.0 minute intervals
the scheduled methods all look more or less the same:
-(void)countDown:(CCTime)delta {
self.countTime++;
if (_countTime >= 100) {
_countTime = 1;
}
[_text setString:[NSString stringWithFormat:@" %02i", self.countTime]];
}
My question is then, how can I store these values in the UserDefault? What I have so far for my UserDefault code is this:
- (void) addStringToMyDefaults:(NSString *)stringToAdd {
// Step 1: Get our defaults
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
// Step 2: Declare our Strings array
NSMutableArray * myStringsArray;
// Step 3: See if an array already exists
if ([defaults objectForKey:@"myStringArray"]) {
// Step 4a: An array already exists. We will initialize our array with the saved array so we have all the latest strings.
myStringsArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"myStringArray"]];
// Step 5: Add new string to our saved array
[myStringsArray addObject:stringToAdd];
}
else {
// No array exists, let's create a new one with our new string as the first object
myStringsArray = [[NSMutableArray alloc]initWithObjects:stringToAdd, nil];
}
// Save our updated array back to defaults
[defaults setObject:myStringsArray forKey:@"myStringArray"];
}
- (NSArray *) getCurrentlySavedStrings {
return [[NSUserDefaults standardUserDefaults]objectForKey:@"myStringArray"];
}
Then I set the data and retrieve the information inside the init method like this:
[self addStringToMyDefaults:@"Milliseconds"];
[self addStringToMyDefaults:@"Here I want seconds"];
[self addStringToMyDefaults:@"And here I want Minutes"];
NSLog(@"MySavedStrings: %@", [self getCurrentlySavedStrings]);
But I can't figure out how to add the CCLabelTTF data to the Array. Is possible, or do I need to make my timer in a different way? Sorry for the extremely long post.
Upvotes: 0
Views: 379
Reputation: 131491
Your question is an instance of "the XY problem". You are asking how to implement a flawed design, and people are giving you help with that flawed design.
First of all, using your labels to save the time value is bad design. You should keep time information in an instance variable and save that.
(View objects are for displaying data, not saving data.)
Second, running 3 or 4 separate timers is overly complex and subject to measurement errors. You should run 1 timer that updates your UI, and calculate your display values based on the actual amount of time that's passed.
Third, timers are not accurate to .01 second. According to Apple's docs, the resolution of an NSTimer is about 0.05 to 0.1. And even at that, you might skip a timer call if your app doesn't service the run loop often enough. I suggest you read the whole first part of the NSTimer class reference in Apple's docs for more information. In particular, read this bit:
A timer is not a real-time mechanism; it fires only when one of the run loop modes to which the timer has been added is running and able to check if the timer’s firing time has passed. 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. See also Timer Tolerance.
Forth, why are you saving your time as 3 separate values to an array, and adding more elements to the array every time you save? Do you want to record all the user's times from when they first start playing the game?
Refactor your code to record the current time at the beginning of a round, then calculate and display the elapsed time.
Something like this:
(in your header)
NSTimeInterval startTime;
To start a round:
startTime = [NSDate timeIntervalSinceReferenceDate];
Then, start a timer that runs on your shortest interval (.02 seconds)
NSTimer *timer = [scheduledTimerWithTimeInterval: .02
target: self
selector: @selector(updateDisplay:)
userInfo: nil
repeats: YES];
The timer runs every .02 seconds, but the code below will still display the number of elapsed hundredths. They will just update by 2s (approximately.)
EDIT: Note that .02 seconds is probably overkill for updating the screen display. Our eyes can't see much faster than .1 seconds. Half of that, .05, would likely be fine. And since the code I've outlined still calculates actual elapsed time with high precision, you're not losing anything by making your display interval slightly longer.
And your updateDisplay method might look like this:
- (void) updateDisplay: (NSTimer *) timer;
{
//elapsed contains the decimal number of elapsed seconds. (very accurate)
NSTimeInterval elapsed = [NSDate timeItervalSinceReferenceDate] - startTime;
int seconds = elapsed;
int hundredths = (elapsed - seconds) * 100;
int tenths = hundredths / 10;
hundredths = hundredths %10;
/*
Now display seconds to a seconds field,
tenths to a tenths field
and hundredths to a hundredths field.
*/
}
When it's time to save the score, save the NSTimeInterval value to userDefaults as an NSNumber. That way you have a value with very high precision to resolve ties.
As for your actual question, you can't save an object of CCLabelTTF to defaults. You would need to fetch a string value from the field and save that string instead.
Upvotes: 1