Set A bool to true for two seconds then change back to false - ios

As the question states, in spritekit I am trying to find a way to change my value of a bool from false, too true for x amount of seconds then change it back to false.
the logic is i have this var, isStunned, when my player comes in contact with x sprite i call it and my joystick is disabled since the player is "stunned"
if (controlButtonDown) { // If i pressed joystick
if (!isStunned) { // IF i am not stunned
//move player
When my player touches the glue, i set isStunned to yes and the joystick is disabled, however I am wondering of a way to only disable my joystick or not move my sprite for x amount of seconds. I know of SKAction and the wait sequence, but those are actions and I dont think they will apply here. Any help is appreciated.
Thank you

Since you ask specifically with a spritekit tag.
Don't use NSTimer or actions to work with time in a game. Instead use the update function. If you're running at 60fps that means the update function will be called 120 times for two seconds. Using the frames instead of seconds to keep your game updated will avoid the gameplay being changed by lag. Imagine for instance that the players game lags a little, in other words runs slower. 2 seconds is still 2 seconds regardless of game lag, so now he is affected less by the glue than a person who has no lag. So...
First off you should have a variable keeping track of in game time: int _time;
In every update cycle you add one to time: _time++;
You should have a variable keeping track of when the user touched the glue: int _glueTouchedAtTime;
When the user clicks glue you set: _glueTouchedAtTime = time;
You should have a constant defining how long the glue is active: #define GLUE_TIME 120;
When you initiate the game set _glueTouchedAtTime to: _glueTouchedAtTime = -GLUE_TIME; To prevent the glue from being on when (_time == 0)
Testing if the user has touched glue now works like this:
if(_glueTouchedAtTime+GLUE_TIME>time) {
// Glue is active
} else {
// Glue is not active
}
Different glues for different sprites
To have different glues for different sprites I would suggest first doing a general game object (GameObject) as a subclass of either SKNode or SKSpriteNode depending on your needs. Then create a subclass of GameObject called GameObjectGluable. This should have a property called: #property int gluedAtTime;
You glue the glueable game object by: aGameObjectGluable.gluedAtTime = time;
Now you can test:
if(aGameObjectGluable.gluedAtTime +GLUE_TIME>time) {
// Game Object is glued
} else {
// Game object is not glued
}

Set your value-of-interest to true and then fire a NSTimer after two seconds to set it back to false.
For an explanation of how you might use NSTimer, see this SO thread: How do I use NSTimer?

You can use SKAction also. The runBlock method let's you execute blocks of code as actions.
SKAction *wait = [SKAction waitForDuration:2.0];
SKAction *reenableJoystick = [SKAction runBlock:^{
joystick.userInteractionEnabled = TRUE;
}];
SKAction *sequence = [SKAction sequence:#[wait, reenableJoystick]];
[self runAction:sequence];

Related

Xamarin SpriteKit running action with key

I am using SpriteKit with Xamarin. I'm currently making a game similar to flappy bird, except the pipes can also 'chomp' down and kill you like that. So at any given time my pipe sprites can be running two actions, one for movement along the X axis (when the player is moving) and one for movement along the Y axis (closing the pipes). When the player stops moving along the X axis I want to stop the running action of the pipes that moves them horizontally, but still keep the chomping action going. I'm having trouble setting up a key for my action though. Currently, this is what I have.
public override void TouchesEnded (NSSet touches, UIEvent evt)
{
base.TouchesEnded (touches, evt);
//TODO: Optimize by removing action by key instead of all of them. Figure out how to do this and impliment.
//This needs to be done before refining chomp as it will flow into the chomp logic
//by not stopping a chomp mid-way because the player lifted their finger.
poletop.RemoveAllActions();
polebottom.RemoveAllActions();
pole2top.RemoveAllActions ();
pole2bottom.RemoveAllActions ();
background.RemoveAllActions ();
//restarting the chomping action if it was stopped before.
chomped_return (false);
chomped_return (true);
}
So I'm basically stopping all actions, then restarting only the chomp if it was running (gets restarted in chomped_return).
This isn't very efficient and also causes some lag in-game as it stops and starts.
Code for starting the action along the X axis (player movement). This is the SKAction that I want to have the key so I can stop it and it alone. resetpoles is the completion function.
SKAction movebottompole;
SKAction movetoppole;
movebottompole = SKAction.MoveToX (edgebottom.X, flTime);
movetoppole = SKAction.MoveToX (edgetop.X, flTime);
polebottom.RunAction (movebottompole, resetpoles);
poletop.RunAction(movetoppole, resetpoles);
Chomping down on the player is an instant teleport of the pipes, but here's the code that runs to start the action of the pipes returning to their original position along the Y axis. This is setting up the action that I don't want to stop until it has completed.
public void chomped_return(bool blFirstPole)
{
//define our two actions
SKAction topreturn;
SKAction botreturn;
//define our floats for the time calculation
float flTime = 0;
float flMoveSpeed = 750;
float flDistance = 0.0f;
if (blFirstPole == true)
{
flDistance = (float)polebottom.Position.Y;
}
else if (blFirstPole == false)
{
flDistance = (float)pole2bottom.Position.Y;
}
//calculate time based on distance and vector units/second desired.
flTime = flDistance / flMoveSpeed;
//setup our moveto actions and use the time calculated above. Start the action.
topreturn = SKAction.MoveToY (750.0f, flTime);
botreturn = SKAction.MoveToY (0.0f, flTime);
if (blFirstPole == true)
{
poletop.RunAction (topreturn);
polebottom.RunAction (botreturn);
}
else if (blFirstPole == false)
{
pole2top.RunAction (topreturn);
pole2bottom.RunAction (botreturn);
}
return;
}
The variable blFirstPole is used to determine whether or not we are running the action on the first set of poles or second set (as there can be two on screen at once)
Would really appreciate any assistance, if you need any more information please do let me know!
Thanks
You can use RemoveActionForKey to remove an action with an certain key.
https://developer.xamarin.com/api/member/MonoTouch.SpriteKit.SKNode.RemoveActionForKey/p/System.String/
polebottom.RunAction(movebottompole, "movebottompole");
// remove it
polebottom.RemoveActionForKey("movebottompole");
But now you don't have your completion handler anymore. You can solve it by combining these two actions into one Sequence.
var poleactionWithCompletion = SKAction.Sequence(movebottompole, restpoles);
polebottom.RunAction(poleactionWithCompletion, "movebottompole");
If you need this more often, you can implement an extension method like:
public static class SKNodeExtension
{
public static void RunAction(this SKNode node, SKAction action, SKAction completion, string key)
{
var sequence = SKAction.Sequence(action, completion);
node.RunAction(sequence, key);
}
}
and then run your action like:
polebottom.RunAction(movebottompole, restpoles, "movebottompole");

Decrement Integer Spritekit

I have a project in Sprite Kit and I am trying to make it so that a timer gradually decreases. For example if the timer float variable is set at 3.0, this would gradually decrease and be at 0, 3 seconds later. With the way that updates work in sprite kit its a horrible mess trying to get an integer to gradually decrease.
For example:
time+=1;
If I was to put this in an update void, it would increment extremely quickly and differently depending on frames and so forth. Is there a way I can Increment or Decrement a value at a steady rate no despite the fps in Sprite Kit?
You'd be better off getting the current time with each update and comparing it to some initial time to determine when 3 seconds pass.
Declare an ivar in your SKScene subclass:
#implementation MyScene {
NSDate* _timestamp;
}
When your timer starts:
_timestamp = [NSDate timeIntervalSinceReferenceDate];
Check your timer in your update pass:
- (void)update:(NSTimeInterval)currentTime {
if(_timestamp != nil && currentTime - _timestamp.timeIntervalSinceReferenceDate >= 3.0) {
// Perform your timer event
}
// Other updates
}

Triggering the end of a while loop using the completion of SKAction

I'm trying to use a "while loop" while my SKSpritenode is animating and I want to trigger the end of the SKAction to trigger the end of the loop. This is my attempt
while (orgNode.hasActions) {
//render the background
}
what I want to do is create a bottom up sweep effect that changes the sprite nodes texture underneath the originNode's position, so that the user sees the sweeping motion of the node changing the background color.
You can override the update or didEvaluateActions function of the SKScene if you want to update the node every frame. didEvaluateActions function is called in each frame after all the SKActions are evaluated. The update function is called before all actions are evaluated.
override func didEvaluateActions() {
if node.hasActions()
{
println("Running Action")
}
}
The following image shows which all functions are called in each frame and in what order.
For more information : https://developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/Introduction/Introduction.html

Alternative to `CACurrentMediaTime()` for scheduling events in SpriteKit

I'm relatively new to SpriteKit and am wondering what alternatives there are to using the CACurrentMediaTime() for scheduling events. For example, I may implement an algorithm that prevents a player from firing too many times by adding the last time they fired with some 'cool down' period, comparing it to the current media time:
BOOL canFire = self.lastFireInterval + self.coolPeriod < CACurrentMediaTime();
The problem I have run into is that if I decide to alter the speed of a node, or even the entire scene, this logic falls apart. For example, if I gave the player a speed-up power up, I could slow down all other nodes except for the player, but the timings would be messed up for enemies firing.
What other alternatives are there for CACurrentMediaTime() that factor in the speed of the node?
2 options come to mind regarding the timed to fire:
1) Create an ivar/property BOOL readyNextMove; for your player class. Then when your player shoots set the readyNextMove to false and add this code:
SKAction *wait = [SKAction waitForDuration:2.0];
SKAction *block0 = [SKAction runBlock:^{
readyNextMove = true;
}];
[self runAction:[SKAction sequence:#[wait, block0]]];
2) You can use the update:(CFTimeInterval)currentTime method to see how much time has elapsed from, for example you player shooting, and set readyNextMove accordingly.
It's a matter of taste. But for all games I keep an internal frame based clock which handles everything. I firmly believe this is the best way, but of course it depends on the scope of your project. This method is very rigid and guarantees continuity. If you use timers and your game lags, your continuity is thrown off (in some cases a shooter might shoot more times per frame than other parts of your game gets to move).
In aScene.h
long _currentTime = 0;
In aScene.m
-(void) update {
_currentTime++;
for(shooter * aShooter in self.shooters) {
[aShooter updateWithTime:_currentTime];
}
}
In your shooter.m
-(BOOL) tryShoot:(long)globalTime {
if(_lastShootingTime<globalTime-shootingCooldown) {
_lastShootingTime = globalTime;
[self fireBullet];
return YES;
}
return NO;
}
-(void) updateWithTime:(long)globalTime {
//...perform shooters action for that time frame
_lastUpdatedTime = globalTime;
}

How to test a sprites rotation in cocos2d

I want to stop an action once my sprite gets to a certain rotation. For example:
CCAction *rotateUp = [CCRotateTo actionWithDuration:0.3 angle:-35];
[player runAction:rotateUp];
if (player.rotation == -35) {
[player stopAction:rotateUp];
[player runAction:[CCRotateTo actionWithDuration:0.5 angle:65]];
}
Once the player gets to the max rotation I want it to run a different action. But this isn't working. What can I do instead?
You cannot get Action output immediately. So it's good to give completion callback for that.
for ex. (in c++)
CCAction *rotateUp = CCRotateTo::create(0.3f, -35f);
CCCallFuncN *pCall = CCCallFuncN::create(callfunc_selector(<#_SELECTOR#>));
player->runAction(CCSequence::create(rotateUp, pCall, NULL));
Here SELECTOR specified get called when rotation action gets completed. Just convert it in Obj C and try.

Resources