NSTimer and CADisplayLink conflict - iphone

I have a CADisplayLink as my main gameloop for a game, and a NSTimer that spawns enemies every ten seconds. I use...
-(void)togglePause{
displayLink.paused = !displayLink.paused;
if (displayLink.paused) {
[self.view addSubview:pauseOverlay];
}else {
[pauseOverlay removeFromSuperview];
}
...to pause the gameloop, but the timer that spawns enemies will continue to go on even after the games paused, if I destroy the timers and then make another one couldn't p just exploit the pause button and just hit it before the 10 seconds go off causing enemies never to spawn?
Is there an easy solution to this?

Ditch the NSTimer and just use a counter that you increment each time the CADisplayLink fires. Once it reaches n spawn your enemies and zero the counter. If the user pauses, when they resume the counter will be the same as it was.

Related

How to stop particle system with a timer?

so I have a simple code that Spawn particle when player stay at a trigger. But the particles keeping turn on and i want to turn off after a few seconds. What i have to do?
if (other.gameObject.tag == "Player" && Input.GetKeyDown(KeyCode.E))
{
PlayerManager.health += 1;
MyParticleEffect.SetActive(true);
Debug.Log("e key was pressed");
}
If you dont want to change this value you can turn off the loop property and play with the duration.
If you want to change this dynamically, its best to use StartCoroutine:
https://docs.unity3d.com/ScriptReference/MonoBehaviour.StartCoroutine.html
This is used like a timer, and you can turn the particle system off when the timer elapses.
Your particle system is probably set to 'loop'. Set the loop property of the particle effect to false.

Sprite is slowing as it approaches touch point

I am using SpriteKit, and my sprite is slowing as it approaches the touched point.
I would like the sprite to remain at a fixed speed as it moved from initial point to the touched point on the screen.
Currently, i have the duration set to 3.0
In the touchesEnded event, i gather the new point of the touch and save the value to a property.
In the scene update event, i perform a
float realMoveDuration = 3.0;
SKAction *actionMove = [SKAction moveTo:self.newPoint duration:realMoveDuration];
[self.player runAction:[SKAction sequence:#[actionMove]]];
So when the player sprite starts getting closer to the touch point, the movement slows down, and gradually reaches the touch point.
This is not the action I am looking for, i would like the player sprite to continue at a constant rate of travel to the touched point.
You should just run your SKAction in the touchesEnded event.
If you have that code in the update event, it's starting a new SKAction 60 times per second. You only ever need to run a new SKAction when there is a change in destination.
If there is a change in destination before you reach the original destination, you should remove the action from that node and then add a new one.
Also, not certain about the specifics of what you are doing, but how quickly an object moves is based on the distance and the time you have specified for it to get there. So if you want a node to move at a consistent speed, regardless of distance, you need to make a calculation to determine the right duration to set for your SKAction.
For example if speed represents pixels/units per second you might do this to calculate your duration :
duration = distance / speed;
If you are using an SKAction to move the sprite, use the timingMode property. The default is SKActionTimingLinear. So if you haven't changed it, the node should move at a constant speed.
Here's a link to the available Action Timing Modes.

animate the increasement of a score

SO I have a label labelscore and it increase of 1000 every time there is a collision between two images. I would like to see labelscore increase of 1000 like an animated score, a running score. How can I do this?
You can use an NSTimer to call a routine every 16.7 to 50 milliseconds. In the routine increment some value and update it to the label. Rinse and repeat until this value is equal to the score. This is the basic technique of an animation game loop.
You have the option to use a repeating timer and invalidate it when you're done with it. Or setting single-shot timers within each update routine for the next iteration. You could also use CADisplayLink as an alternative to NSTimer, which may provide smoother animation under some conditions.

Can't move my character

I am trying to make a UIImageView move when I tap a button. I have set a UIIAction to start an NSTimer. When tap the button, the character(UIImageView) moves fine. When I tap the same button again, the character speeds up and so forth so eventually he is moving a billion mph. How do I solve this? Here is the code:
-(void)animateCharRight:(NSTimer *)theTimer
{
float speed;
if (speed > 10.0f)
{
speed = 10.0f;
}
else
{
speed = 10.0f;
}
CGRect rect = [character frame];
rect.origin.x += speed;
[character setFrame:rect];
}
Also, when you press the opposite button after you already pressed one it bugs up because there is no cancelation of the other timer. How do I do that?
How do I access the key up event? I cant seem to find it.
Sounds like you might be starting a new timer on each button tap. So after 2 taps you now have 2 timers, each of which will call -animateCharRight: to move your view 10, for a total of 20. More taps, faster, and so on. Is that what's going on?
Well you declare speed in your method and do not set it to anything. So it is always going to the else setting the speed to 10.0f. Because in both your if and else you are setting it to 10, so what is the point of the if statement?
Then you rect.origin.x += speed; or add 10 the origin all the time, no exceptions. It is the same as doing rect.origin.x += 10; The image will constantly move.
The speed is always going to be 10, and always add to the rect.origin.x do you want to make speed a instance variable rather than just in the method?
EDIT
Because speed is declared in the method each time it has 0 as a value. Then it is set to 10. If you create speed as a member variable, then each time animateCharRight is called you can do something like
speed += speedIncrementAmount;
or
speed += 1.0f;
Meaning each time the speed is increased by 1 unit or what you need for your game.
Then the you would need a couple of checks depending on your logic.
if (speed > 10.0f)
{
// This will clamp the speed at 10.0f;
speed = 10.0f;
}
if (speed < - 10.0f)
{
// This will make sure you will never go left to quickly
speed = - 10.0f;
}
The above if statements may work or might not. Depends on the use. If the game is a side scooler and you only want to move to the right then you might want to make sure the speed is never less than 0.
Think about the design you want.
Since you are new to game programming I would start with a Tutorial and build upon the skills learned in the tutorial. Here is a link to one I found in a quick Google search.
Remember nobody makes Doom as there first game. Everybody starts with a Brick Breaker rip off or Tetris, it is the 'Hello World' of game programming.
I think you problem is that you are not setting the speed back to zero when you release the key to move. You should either reset as soon as the keyup event fires or if you'd like the character to keep moving for a little while after you release the key you can set the speed to zero after the time has expired with a timer.
That method will always move your character by 10. However, it seems that you are starting a new timer every time the button is pressed. So if you press the button 3 times, the character moves 3x10=30. You need to cancel the existing timer so there is always only one running.
You can cancel an NSTimer instance using [timer invalidate]. (See here)

Is NSTimer expensive?

I am using about 20 UIImageViews of small airplane images (50x50 pixels) doing simple animation on the iPhone screen. The animation is done by shifting the UIImageView center property at timer interval.
[NSTimer scheduledTimerWithTimeInterval:0.01
target:self
selector:#selector(timerFired:)
userInfo:nil
repeats:YES]
What is the best practice: create one NSTimer and loop the 20 UIImageViews to set the center property when timer fired? Or should I just create one NSTimer per UIImageView object? Is NSTimer resource expensive?
I don't think it's that resource intensive, but common sense would seem to dictate that using 1 timer is probably better than 20.
Looks like your timer is set to fire 100 times per second, which seems a bit excessive. Do you animate each sprite at every timer firing? Might want to try firing 20-30 times per second instead (or maybe even less).
You might want to look into the built in view animation functions as well. Seems like they would probably work very well for what you are doing.
you could try both and use the built in profiling tools that ship with Xcode to gauge resource usage.
Timers are actually PRETTY EXPENSIVE, energy-wise.
Every nanosecond that the OS (or apps running on it) are not doing something, the CPU is sleeping in one of several low-power state modes. Waking the system from an idle state incurs an energy cost when the CPU and other systems are awakened from their low-power, idle states. If a timer causes the system to wake, it incurs that cost. The more frequently your timer fires, the higher the energy cost. On mobile devices, it can affect materially battery life.
In that sense, it is much more efficient to wake the system once, do all your work, then let it sleep again for as many nanosecs as possible. Going back to your example, if you profile this, it should be more efficient to use 1 timer rather than 20 timers, each firing 100 times / sec.
Later OS versions allow you to specify a tolerance (in %). This allows the system to group timers together and execute them at the same wake event, to save power. It seems what you are doing is not time critical (in a real-time execution kind of sense), so permitting a tolerance (e.g. 10%) should help.
Example: [myTimer setTolerace:0.3];
More about timer tolerance here: https://developer.apple.com/documentation/foundation/nstimer
Instead of attempting to have a Timer synchronized to the screen redraw (i.e. firing 60x a second), a better solution would be to use CADisplayLink, which fires as on screen redraw and is built for this purpose.
let displayLink = CADisplayLink(target: self, selector: #selector(displayLinkFired))
displayLink.add(to: .main, forMode: .default) // attach to the main run loop
#objc func displayLinkFired() {
// update image view's here
}