Can we change dynamically the interval of "schedule"? - iphone

I'm making a game on iPhone using cocos2d, and I have a question.
In my init method I'm doing this :
[self schedule:#selector(newMonster:) interval:1];
It create a new monster every second.
But I'd like that the interval change over time. For example :
The 10 first seconds of the game: a new monster appears every 1 second.
Then for 10 seconds: a new monster appears every 0.8 second.
And after every 0.5 second...
How can I do that?
Thanks !

You may try this- (I have copied some code from #bddckr)
Initial time:
self.monsterSpawnStartDate = [NSDate date];
self.lastTimeScheduledBefore = 0;//new var
Scheduled Method to create monster
- (void)scheduledMethod {
NSTimeInterval seconds = [[NSDate date] timeIntervalSinceDate:monsterSpawnStartDate];
[self newMonster]; // Let a new monster spawn
if (seconds >= 20.0 && lastTimeScheduledBefore == 10){
[self unschedule:#(scheduledMethod)];
[self unschedule:#(scheduledMethod) interval:0.5];
lastTimeScheduledBefore = 20;
}
else if (seconds >= 10.0 && lastTimeScheduledBefore == 0){
[self unschedule:#(scheduledMethod)];
[self unschedule:#(scheduledMethod) interval:0.8]
lastTimeScheduledBefore = 10;
}
}
You can modify it as your need. You can use mod if it goes through a cycle.

Make an instance variable and property of NSDate called monsterSpawnStartDate, make it an retaining property and synthesize it.
As soon as your first monster should be created, set it:
self.monsterSpawnStartDate = [NSDate date]; // Current date and time
And replace your code by this:
[self schedule:#selector(adjustMonsterSpawnRate) interval:0.1]; // Seems like you want it to fire every 0.1 seconds
Implement adjustMonsterSpawnRate like this:
- (void)adjustMonsterSpawnRate {
NSTimeInterval seconds = [[NSDate date] timeIntervalSinceDate:monsterSpawnStartDate];
if (((seconds <= 10.0) && (seconds % 1.0)) || ((seconds <= 20.0) && (seconds % 0.8)) ((seconds > 20.0) && (seconds % 0.5)))
[self newMonster]; // Let a new monster spawn
}

I think a better option would be to use a scheduled function for timing and use accumulated time to trigger the enemy creation as follows
self.time_since_last_monster = 0
[self schedule:#selector(new_monster:) interval:0.3];
-(void) new_monster:(ccTime)dt{
self.time_since_last_monster += dt;
if(self.time_since_last_monster > monster_interval){
self.time_since_last_monster -= monster_interval;
[self create_new_monster];
// at this point based on some condition, you can now change monster_interval
}
}
This would give more flexibility.

I'm pretty dumb, so here's what I'd do:
in the .h set a float
float nextMonsterIn;
then in the body
nextMonsterIn = 1.0;
[self scheduleOnce:#selector(newMonster:) interval:nextMonsterIn];
[self schedule:#selector(kickUpDifficulty:) interval:10];
- (void) newMonster : (ccTime) dt
{
[self scheduleOnce:#selector(newMonster:) interval:nextMonsterIn];
//-----make a monster-----
}
- (void) kickUpDifficulty : (ccTime) dt
{
if(nextMonsterIn == 1.0)
nextMonsterIn = 0.8;
else if(nextMonsterIn == 0.8)
{
nextMonsterIn = 0.5;
[self unschedule:#selector(kickUpDifficulty:)];
}
}
Basically every time I call newMonster i'd recursively re-schedule the newMonster based on my current delay, then I can adjust the delay in a timer or every time a monster is killed or wherever.

Related

Sprite Kit Calling a random method

I am currently making a game in sprite kit and i have 8 methods, I have written all the timing code E.T.C so it calls a method every 1 second, but i want it to call a random one of the eight methods, I have been trying to get this working for weeks, any help would be muchly appreciated, Here is my timing code -
- (void)updateWithTimeSinceLastUpdate:(CFTimeInterval)timeSinceLast {
self.lastSpawnTimeInterval += timeSinceLast;
if (self.lastSpawnTimeInterval > 5) {
self.lastSpawnTimeInterval = 0;
[self shoot1];
}
}
- (void)update:(NSTimeInterval)currentTime {
// Handle time delta.
// If we drop below 60fps, we still want everything to move the same distance.
CFTimeInterval timeSinceLast = currentTime - self.lastUpdateTimeInterval;
self.lastUpdateTimeInterval = currentTime;
if (timeSinceLast > 1) { // more than a second since last update
timeSinceLast = 1.0 / 60.0;
self.lastUpdateTimeInterval = currentTime;
}
[self updateWithTimeSinceLastUpdate:timeSinceLast];
}
As you can see instead of [self shoot1]i want it to randomly call one of the eight methods, Also all the methods are named Shoot1, Shoot2, all the way to Shoot8. Thankyou
I can think of two options...
Option 1
Just pick a random number between 1 and 8, and use a switch statement:
- (void)updateWithTimeSinceLastUpdate:(CFTimeInterval)timeSinceLast {
self.lastSpawnTimeInterval += timeSinceLast;
if (self.lastSpawnTimeInterval > 5) {
self.lastSpawnTimeInterval = 0;
int randomNumber = arc4random_uniform(8);
switch(randomNumber) {
case 0:
[self shoot1];
break;
case 1:
[self shoot2];
break;
// ... cases 2-6
case 7:
[self shoot8];
break;
}
}
}
Option 2
Rewrite your shootN methods so that you only have one method that takes an integer as a parameter:
- (void)shoot:(int)index;
Then, you can just do the following:
[self shoot:arc4random_uniform(8)];
You could also live dangerously...
int random = arc4random() % 8;
NSString *selectorName = [NSString stringWithFormat:#"shoot%i", random];
SEL selector = NSSelectorFromString(selectorName);
if ([self respondsToSelector:selector]) {
[self performSelector:selector];
}
This will, by the way, generate a warning when using ARC.

Recursive UIView animation continues after ViewController destroyed, new ViewController created

UPDATE
ViewController is not destroyed, new ViewController is not created. This:
TimerViewController * timerViewController = (TimerViewController *)[segue destinationViewController];
does not create a new instance. However, this
TimerViewController *timerViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"theID"];
does. So a new instance is now being used, but the same problem.
END UPDATE
I have a simple "analog digital timer" where the minutes and seconds animate smoothly to emulate the turning of a number wheel. This is simply a ParentViewController with a list of times, selecting a time performs a segue to the TimerViewController. The TimerViewController uses a recursive UIView animation to simulate the countdown of a clock. This works well.
I want the user to be able to transition to the next timer in the list without having to go back to the ParentViewController in order to select it. This does not work well.
I've tried many variations, the basic pattern is for TimerViewController to ask its delegate (ParentViewController) to pop it and then to seque again to the TimerViewController using the next time from the list of times. The initial animation never terminates, the initial TimerViewController instance never "dies". When I segue to TimerViewController the second time, using what I thought was a new instance, the first animation appears to still be running, the duration of the animation which started at almost 1.0s is off the charts (closer to 0.).
I've tried many different, increasingly desperate, ways to stop the original animation and kill the original instance.
ParentViewController.m
-(void) timerViewControllerDidSwipe (TimerViewController *)controller {
[self.navigationController popViewControllerAnimated:NO];
controller = nil; // ?
[self performSegueWithIdentifier:#"ShowTimer" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"ShowTimer"])
{
TimerViewController * timerViewController = (TimerViewController *)[segue destinationViewController];
[timerViewController setDelegate:self];
}
TimerViewController.m
-(void) swipe:(id)selector {
// swipe left
stop = YES; // ivar for ^after to not call [self animate]
[self.delegate timerViewControllerDidSwipe:self];
}
-(void) animate {
// anim block here..
void (^after) (BOOL) = ^(BOOL f) {
if (duration % 60 == 0 && duration >= 60) {
if (minutesAlt.center.y < minutes.center.y)
{
CGPoint a = minutes.center;
a.y -= 2 * displacement;
minutes.center = a;
minutes.text = [NSString stringWithFormat:#"%02d", (duration / 60) -1];
}
else
{
CGPoint a = minutesAlt.center;
a.y -= 2 * displacement;
minutesAlt.center = a;
minutesAlt.text = [NSString stringWithFormat:#"%02d", (duration / 60) -1];
}
}
if (duration == 0)
{
// end
}
else if (secondsAlt.center.y < seconds.center.y)
{
CGPoint a = seconds.center;
a.y -= 2 * displacement;
seconds.center = a;
duration--;
seconds.text = [NSString stringWithFormat:#"%02d",duration % 60];
if (!stop) {[self animate];}
}
else
{
CGPoint a = secondsAlt.center;
a.y -= 2 * displacement;
duration--;
secondsAlt.center = a;
secondsAlt.text = [NSString stringWithFormat:#"%02d", duration % 60];
if (!stop) {[self animate];}
}
};
[UIView animateWithDuration:0.50
delay:0.50
options:UIViewAnimationOptionCurveLinear
animations:anim
completion:after];
}
Any help greatly appreciated.
Fixed here in a related question. It was a simple syntax error (that took me weeks to find).

Cocos2d schedule interval decreaser

I have made a simple timer but in trying to increase the timers speed over time, so I basically want the timer to have an interval of 1.0 and every 10 seconds I want that timer's interval to decrease by 0.1
How can I go about doing that?
[self schedule:#selector(tick:)interval:1.0];
That's in the init section
-(void)tick:(ccTime)dt{
myTime = myTime+1;
totalTime = myTime;
[timeLabel setString:[NSString stringWithFormat:#"%i", totalTime]];
}
That's the timer.
It's a basic timer which has an interval of 1.0 second, however I would like the interval to decrease by 10% every 10 seconds.
in .h file
#property(nonatomic,readWrite)CGFloat lastUpdatedInterval;
in.m file, initialize self.lastUpdatedInterval = 1.0;
then call
[self schedule:#selector(tick:)interval:self.lastUpdatedInterval];
in update loop,
-(void)tick:(ccTime)dt
{
if(myTime>9 && (myTime%10 == 0))
{
[self unschedule:#selector(tick:)];
self.lastUpdatedInterval = self.lastUpdatedInterval - (self.lastUpdatedInterval*0.1);
[self schedule:#selector(tick:)interval:self.lastUpdatedInterval];
}
myTime = myTime+1;
totalTime = myTime;
[timeLabel setString:[NSString stringWithFormat:#"%i", totalTime]];
}

Gauge animation on iphone

I'm trying to implement a gauge animation using (+ and - buttons) on iphone, but i have no idea where to start? Any help is really welcome. See the image below (this is what I'm trying to do). Thanks for your help.
Here is some open source code (with an example) that implements the gauge view. You of course would still need to do the buttons yourself, and possible add a different visual style.
http://www.cocoacontrols.com/platforms/ios/controls/meterview
You need to rotate the needle based on the angle... Here is the logic
You can refer my answer here... Rotating a UIImageView around a point over 10 seconds?
fireInterval = 10;
//Adjust starting and ending angle
mStartingAngle = 45;
mEndingAngle = 180;
//Implementation
-(void) startTimer
{
mPreviousTime = [NSDate timeIntervalSinceReferenceDate];
}
In the loop
-(void) updateFunction
{
NSTimeInterval timeNow = [NSDate timeIntervalSinceReferenceDate];
//NewValue = (((OldValue - OldMin) * (NewMax - NewMin)) / (OldMax - OldMin)) + NewMin
//Mapping values between mStartAngle and mEndAngle
mCurrentAngle = (((timeNow - mPreviousTime) * (mEndingAngle - mStartingAngle)) / (previousTime+fireInterval - mPreviousTime)) + mStartingAngle;
if( mPreviousTime + fireInterval <= timeNow )
{
NSLog(#"10 seconds completed");
mPreviousTime = timeNow;
}
}
And rotate the needle based on mCurrentAngle....

Rotating a UIImageView around a point over 10 seconds?

I have a UIImageView setup called needleImageView which i need to rotate 360degrees over 10 seconds the point 20.00 20.00 of the ImageView.
Anybody able to show me sample code for this functionality?
Thanks,
-Code
Here is the logic... Try implementing.
On starting timer
//in header file
fireInterval = 10;
mStartingAngle = 0;
mEndingAngle = 360;
//Implementation
-(void) startTimer
{
mPreviousTime = [NSDate timeIntervalSinceReferenceDate];
}
In the loop
-(void) updateFunction
{
NSTimeInterval timeNow = [NSDate timeIntervalSinceReferenceDate];
//NewValue = (((OldValue - OldMin) * (NewMax - NewMin)) / (OldMax - OldMin)) + NewMin
//Mapping values between mStartAngle and mEndAngle
mCurrentAngle = (((timeNow - mPreviousTime) * (mEndingAngle - mStartingAngle)) / (previousTime+fireInterval - mPreviousTime)) + mStartingAngle;
if( mPreviousTime + fireInterval <= timeNow )
{
NSLog(#"10 seconds completed");
mPreviousTime = timeNow;
}
}