Pausing game in Sprite Kit: Why doesn't this work? - ios

So I added a pause button to my game and the freezing and unfreezing of the view works. However I want it to display a message saying "PAUSE" over the screen if the view is currently frozen. But if I'm touching my pause button it doesn't display the label. It's really strange, because I discovered that if I'm turning my device to landscape mode, the "PAUSE" message appears. (and vice versa too, so if I touch the button in landscape mode and turn my device to portrait, the pause label appears)
Can anyone figure this out?
BTW: I also tried out different methods to do this, like initializing the pause label as soon as the scene starts and hide/reveal it whenever needed. This didn't work either.
I also tried to do it with an if-statement in the update method ( if the scene is paused => reveal the pause label) this didn't work either.
I asked this question before on here and the apple devforums but no one could solve this problem yet. Someone thought I would present the scene in viewwilllayoutsubviews, which would explain the portrait-landscape behavior, but I never used that method in my whole application code.
-(void)gameScene{
//Pause
pauseButton = [SKSpriteNode spriteNodeWithImageNamed:#"pause.jpg"];
pauseButton.position = CGPointMake(screenWidth/2, screenHeight-80);
pauseButton.zPosition = 5;
pauseButton.name = #"pauseButton";
pauseButton.size = CGSizeMake(80, 80);
[self addChild:pauseButton];
//Pause Label (wenn Pause gedrückt wird)
pauseLabel = [SKLabelNode labelNodeWithFontNamed:#"Arial"];
pauseLabel.text = #"PAUSE";
pauseLabel.fontSize = 70;
pauseLabel.position = CGPointMake(screenWidth/2, screenHeight/2);
pauseLabel.zPosition = 5;
pauseCount = 1;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
//Pause Button
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"pauseButton"]) {
pauseCount++;
if (pauseCount%2 == 0) {
self.scene.view.paused = YES;
[self addChild:pauseLabel];
}
else{
self.scene.view.paused = NO;
[pauseLabel runAction:remove];
}
}
}

I had a similar problem with pausing and found that the problem related to the fact that although the pauseLabel was getting added to the node tree the scene was pausing before it was displayed. The solution I used was to use SKAction to run the label add and pause in sequence.
SKAction *pauseLabelAction = [SKAction runBlock:^{
[[self scene] addChild:pauseLabel];
}];
SKAction *delayAction = [SKAction waitForDuration:0.1]; // Might not be needed.
SKAction *pauseSceneAction = [SKAction runBlock:^{
[[[self scene] view] setPause:YES];
}];
SKAction *sequence = [SKAction sequence:#[pauseLabelAction, delayAction, pauseSceneAction]];
[self runAction:sequence];
I have not tested this but there might also need to be a small delay in there to give the label time to display before the pause is fired.

Related

SpriteKit - Scale in one direction

How do I make an object grow in one direction is SpriteKit?
Ultimately, I would like to have a node be inserted where the user touches and have each end of a line scale until it hits another object in the same category or hits the edge of the screen. I understand how to have it scale on an axis and I can then cancel the scaling after one collision is detected but I want both directions to scale independently until a collision is made for each. I am new to SpriteKit so my searches may be using the wrong terms but I have not been able to find anything relevant.
Code to add a "wall" where the user touches
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch * touch in touches) {
CGPoint location = [touch locationInNode:self];
[self addWallAtLocation:location];
}
}
-(void)addWallAtLocation:(CGPoint)location{
int odd_or_even = arc4random() % 2;
SKSpriteNode * wall = [SKSpriteNode spriteNodeWithColor:[UIColor whiteColor] size:CGSizeMake(2, 2)];
wall.position = location;
wall.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(2, 2)];
wall.physicsBody.dynamic = NO;
wall.physicsBody.categoryBitMask = wallCategory;
wall.physicsBody.contactTestBitMask = ballCategory;
[self addChild:wall];
SKAction * grow;
if (odd_or_even == 1) {
grow = [SKAction scaleYTo:300 duration:2];
} else {
grow = [SKAction scaleXTo:300 duration:2];
}
[wall runAction:grow];
}
How I want it to work:
Touch 1 makes the horizontal line from a point which shoots out in both directions on the x-axis until both ends hit the edge of the screen. Touch 2 would then create a point which shoots out in both directions on the y-axis until the lower end hits the wall created by Touch 1 and the upper end hits the edge of the screen. The lines (scaled points) are generated where the user touches. So I do not want the part of the vertical line that is highlighted by the red box to be there.
Scaling in one direction is just a solution I see to this problem, if there is a better way of going about it I will certainly try that other solution.
EDIT FOR UPDATED QUESTION:
It sounds like you might be asking "how to scale a thing in a single axis, but then stop the scaling of that single axis in one direction only when it collides with another thing"? There isn't a simple way to do that just using actions, but you you could do some slight of hand by manipulating the anchor point of a sprite. Or, probably more appropriately, you should use 2 sprites per touch, with one action sending one north, and another action sending the other one south. Maybe like this:
-(void)someMethod{
SKSpriteNode *touch2TopPart = [SKSpriteNode node];
SKSpriteNode *touch2BottomPart = [SKSpriteNode node];
touch2TopPart.anchorPoint = CGPointMake(.5, 0);
touch2BottomPart.anchorPoint = CGPointMake(.5, 1);
touch2TopPart.name = #"touch2TopPart";
touch2BottomPart.name = #"touch2BottomPart";
SKAction *sendTopPartUp = [SKAction scaleYTo:100 duration:2];
SKAction *sendBottomPartDown = [SKAction scaleYTo:100 duration:2];
[touch2TopPart runAction:sendTopPartUp withKey:#"sendTopPartUp"];
[touch2BottomPart runAction:sendBottomPartDown withKey:#"sendBottomPartDown"];
}
-(void)whateverTheCollisonMethodIsCalledBetweenThingAandThingB
{
SKSpriteNode *somePartMovingInASingleDirection = thingAOrWhatever;
[somePartMovingInASingleDirection removeAllActions];
}
---ORIGINAL ANSWER
I'm not exactly sure what you're asking, but you already have the actions for scaling in just X or Y, so here's the simple usage for removing one of those actions after physics is simulated. You might be looking for how to detect a specific collision between two entities, but I can't be sure.
-(void)someMethod{
SKSpriteNode *thisSprite = [SKSpriteNode node];
thisSprite.name = #"spriteRunningActions";
SKAction *growX = [SKAction scaleXTo:100 duration:2];
SKAction *growY = [SKAction scaleYTo:100 duration:2];
[thisSprite runAction:growX withKey:#"actionCurrentlyScalingX"];
[thisSprite runAction:growY withKey:#"actionCurrentlyScalingY"];
}
-(void)didSimulatePhysics
{
SKSpriteNode *thatSprite = (SKSpriteNode*)[self childNodeWithName:#"spriteRunningActions"];
//stop a given action
[thatSprite removeActionForKey:#"actionCurrentScalingX"];
[thatSprite removeActionForKey:#"actionCurrentScalingY"];
}
Setting the anchor point like below made the "lines" scale in one direction.
wall.anchorPoint = CGPointMake(0, 0);

SpriteKit Detect Press and Hold on Label

I am trying to make an app where the user can press a button to increase the level of some skill. However, I am using the "touchesBegan" method, and if the user wants to upgrade the skill by 25 points, the user will have to click the label 25 times. Here is my code currently to detect if the user has touched the sprite.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if([node.name isEqualTo:#"red-upgrade"]){
if(_xp >= pow(_rpower+1, 2)){
_rpower ++;
_xp -= pow(_rpower, 2);
}
_rpowerlabel.text = [NSString stringWithFormat:#"Red Power: %i", _rpower];
}
}
Is there a way to implement a hold and press gesture on a sprite or a label? So the user can just press and hold on the upgrade button and it will increment many times instead of continuously clicking?
Any and all suggestions are appreciated!
Keep a member referencing the skill button in the enclosing class and then and start an action on the button to increment the needed value in the touch begin method:
if([node.name isEqualTo:#"red-upgrade"]){
[self.skillButton runAction:[SKAction repeatForever:[SKAction sequence:#[[SKAction waitForDuration:0.5],
[SKAction runBlock:^{
if(self.xp >= pow(self.rpower+1, 2)){
self.rpower ++;
self.xp -= pow(self.rpower, 2);
}
self.rpowerlabel.text = [NSString stringWithFormat:#"Red Power: %i", self.rpower]
}]]]]];
}
I used a small delay or else the value will be increased really fast (unless that what you want).
In the touchEnd method stop the node's action with [self.skillButton removeAllActions]; regardless where the location is (since then the user can slide his finger and the location will be different)
Not sure if the cleanest of solutions but that should work for you

SpriteKit: How to implement touchesBegan and touchesEnded to split action into two parts?

I'm trying to do a little archery game and at the moment I have a basic touchesBegan method that uses SKActions to animate an archer and shoot an arrow.
I'm having a hard time getting it so that I hold touch to draw the bow, then when I release, the rest of the animation plays out and the arrow shoots.
I tried it with using two separate NSMutableArray Atlases spread across touchesBegan and touchesEnded methods with userInteractionEnabled flags but that was a bit of a no-go...
Ultimately I want the duration of the hold to dictate a value for applyImpulse:CGVectorMake.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
SKNode *archerNode = [self childNodeWithName:#"archerNode"];
if (archerNode != nil)
{
SKAction *draw = [SKAction animateWithTextures:self.archerDraw timePerFrame:0.075];
[archerNode runAction:draw];
SKAction *shootArrow = [SKAction runBlock:^{
SKNode *arrowNode = [self createArrowNode];
[self addChild:arrowNode];
[arrowNode.physicsBody applyImpulse:CGVectorMake(20.0, 0)];
}];
SKAction *sequence = [SKAction sequence:#[draw, shootArrow]];
[archerNode runAction:sequence];
}
}
Initiate the charging animation in the touchesBegan: section & move the arrow release code in the touchesEnded: (+ touchesCancelled:) function. Keeping the time at which the touches began in an instance variable will help you calculate the impulse vector.

Sprite Kit device lags on first collision

I have a problem in which my Sprite Kit game lags only when the first collision occurs between the main character and any other sprite. After the first collision occurs, every other collision is smooth and runs at 60.0 fps. The odd thing is that when the first collision, the fps only drops to 49-51, but the actual game freezes for a half second. This is also not an issue of setup lag as this occurs no matter how long I wait to start. Does anyone know what the issue is?
-(void)checkForCollisions {
if (_invincible) return;
[self enumerateChildNodesWithName:#"enemy"
usingBlock:^(SKNode *node, BOOL *stop){
SKSpriteNode *enemy = (SKSpriteNode *)node;
CGRect smallerFrame = CGRectInset(enemy.frame, 22, 22);
if (CGRectIntersectsRect(smallerFrame, _sloth.frame)) {
[enemy removeFromParent];
[self runAction:[SKAction playSoundFileNamed:#"eagle.wav" waitForCompletion:NO]];
NSString *burstPath =
[[NSBundle mainBundle] pathForResource:#"ExplosionParticle" ofType:#"sks"];
SKEmitterNode *burstEmitter =
[NSKeyedUnarchiver unarchiveObjectWithFile:burstPath];
burstEmitter.position = _sloth.position;
[self addChild:burstEmitter];
[self changeInLives:-1];
_invincible = YES;
float blinkTimes = 10;
float blinkDuration = 3.0;
SKAction *blinkAction =
[SKAction customActionWithDuration:blinkDuration
actionBlock:
^(SKNode *node, CGFloat elapsedTime) {
float slice = blinkDuration / blinkTimes;
float remainder = fmodf(elapsedTime, slice);
node.hidden = remainder > slice / 2;
}];
SKAction *sequence = [SKAction sequence:#[blinkAction, [SKAction runBlock:^{
_sloth.hidden = NO;
_invincible = NO;
}]]];
[_sloth runAction:sequence];
}
}];
}
The lag is not linked to the emitter node as the game still lags whenever it is commented out.
Let me know if you need any additional information. Thanks in advance!
Here is a link for my Instruments's trace file: https://www.dropbox.com/sh/xvd1xdti37d76au/ySL4UaHuOS
If you look at the trace file, note that the collision occurs when the Time Profiler reaches 118%.
As Cocos mentioned, it is probably the initial loading of your sound file which is causing the one time delay.
In your implementation add the sound action:
SKAction *eagleSound;
In your init method add this:
eagleSound = [SKAction playSoundFileNamed:#"eagle.wav" waitForCompletion:NO];
Then whenever you need to play the sound, use this:
[self runAction:eagleSound];

How to add a Get Ready screen in spritekit

Hi I want to add a title or label that says Get Ready and when you tap it you start spawning custom sprites. Does any one know how to do this. As a reference I want the Get ready screen to act like Flappy Birds or Splashy Fish. Anywhere I can get info on this?
SKSpriteNode *getReady = [SKSpriteNode spriteNodeWithImageNamed:#"myImage.png"];
//remember to set position and other custom stuff here
[self addChild:getReady];
_isFirst = YES; //BOOL property
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if (_isFirst)
{
SKAction *fadeOut = [SKAction fadeOutWithDuration:10];
[getReady runAction:fadeOut];
_isFirst = NO;
} else {
//other time you tap, code here
}
}
You need to creat a SKSpriteNode with the image you want and add to your scene.
SKSpriteNode *getReady = [SKSpriteNode spriteNodeWithImageNamed:#"myImage.png"];
//remember to set position and other custom stuff here
[self addChild:getReady];
Then when the user tap you can run an action to fade it out.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
SKAction *fadeOut = [SKAction fadeOutWithDuration:10];
[getReady runAction:fadeOut];
}
Also, I think you would be great if you check out some tutorials, cause that's really basic stuff. I'm sure you can easily find then.

Resources