How to create a replay button and reset contents? - ios

I'm trying to create a replay button so the user can simply replay instead of going back to the level selection menu in a game I'm creating. The game is a sprite-kit game for iOS 7. The problem I'm having is resetting the integer. For some reason it stays at 0. All the rest of the scene contents replace themselves in the initWithSize method but even though I declare the integers value of 5 in the initWithSize method, it doesn't reset the value, instead it stays at 0.
After the gameOverNode for my game appears, there is a button that says "Replay", which I've set up to load the scene, this is the code:
if ([node.name isEqualToString:#"reTry"]) {
level2 *repeat = [[level2 alloc] initWithSize:self.size];
[self.view presentScene:repeat transition:[SKTransition fadeWithColor:[SKColor whiteColor] duration:0.5]];
}
And as stated, everything in the scene resets (i.e. positions of SKSpriteNodes, etc), except for the value of the integer, which stays at 4.
Why is this, and how can I reset the value of the integer?
Is there a way to clear the scene before reloading it?
This is the initWithSize method:
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
movesRemaining = 4;

What you have declared is a class object. You need to declare it as an instance variable instead.
Instead of
#import <SKScene/SKScene.h>
.
.
int movesRemaining;
You need to declare it like so:
#interface level2 : SKScene
{
int movesRemaining;
}

Related

How to access already initialised node within another method SpriteKit

In an application I have, I initialise a SKSpriteNode (Ball) the didMoveToView method. I have a method that picks up on swipes up (That works fine, I tested it).
When that is called, it calls a method called [self jump];
In this method, I want it to run a few actions using the SKSpriteNode, Ball.
I used to know, but I have forgotten the code that allows you to access already initialised nodes. Say if I was going to write [Ball runAction:myAction];
, the compiler will replace 'Ball' with '_Ball'.
How can I manipulate the SKSpriteNode 'Ball' within another method?
I don't think that I need to add any code but if it helps I will comply.
Thank you in advance.
You can either create a class property for ball to access it everywhere.
#property (nonatomic,strong) SKSpriteNode *ball
You can use self.ball to access the ball in every method.
self.ball = //SKSpriteNode initialize
[self addChild:self.ball]
// Add Child
SKNodes can also be searched by using the unique name of its children.
For example you can set
// Initialise
ball.name = #"Ball"
// Add Child
Then the SKScene can be searched using the function childWithName:
SKSpriteNode *ball = [self childNodeWithName:#"Ball"]
The first method is easier in your case.
As suggested in the comments you could keep a pointer to the ball node, however another way could be to use the name property of SKNode.
When you instantiate the ball node assign the name property with a string identifier -
SKSpriteNode *ball = // create instance here
ball.name = #"TheBall";// set its name
[self addChild:ball];// add to scene and forget about it
Whenever you need to access it from within your SKScene subclass use -
SKSpriteNode *sameBall = (SKSpriteNode*)[self childNodeWithName:#"TheBall"];// don't forget the typecast
Now you can perform whatever actions you need to on the ball node.
An interesting read is the section on Searching The Node Tree found in the SKNode Class Reference by Apple.

When an instance is deallocated under ARC, are all its strong pointers automatically set to nil?

I've looked all over for the answer to this, and all the tutorials of ARC I've seen never make this explicitly clear:
I'm using ARC. If an instance is deallocated (because the last strong pointer to it was set to nil), does that instance's strong pointers also automatically get set to nil and/or whatever objects it points to get deallocated?
For example: I have two classes, Foo and Bar, and I have an instance of Foo, myFoo.
Inside myFoo, I create an instance of Bar (which is strong by default).
Bar *myBar = [Bar new];
Now, in whatever instance that owns myFoo, I deallocated it.
myFoo = nil;
Does the myBar automatically go to nil (or more precisely, its retain count decremented)?
EDIT
I've narrowed it down to this code. I'm using sprite kit. This code is inside an instance which is owned by my Play view.
// tick marks every 20 pix.
for (long h=0; h<20000; h+=20) {
SKShapeNode *line = [SKShapeNode node];
CGMutablePathRef pathToDraw = CGPathCreateMutable();
CGPathMoveToPoint(pathToDraw, NULL, 0, h+yOffset+5);
CGPathAddLineToPoint(pathToDraw, NULL, 10, h+yOffset+5);
line.path = pathToDraw;
line.lineWidth = 1;
[line setStrokeColor:[UIColor grayColor]];
[rulerContainer addChild:line];
CGPathRelease(pathToDraw);
}
(I know I should be lazy loading the tick marks; this puts 1000 of them there all at once, but it runs fast so figured it was ok; and they should be getting deallocated anyway).
When I comment-out [rulerContainer addChild:line], the allocation problem drops to almost zero.
When that line is in, it's 5MB added every time I go back to Menu view.
Why should the lines be hanging around after the scene and view controller are deallocated?
UPDATE
I added this to my VC based on another thread on SO...
- (void) dealloc
{
if (self.scene != nil)
{
[self.scene removeAllActions];
[self.scene removeAllChildren];
self.scene = nil;
[self.skView presentScene:nil];
self.skView = nil;
}
}
.. but you SHOULDN'T have to do this. Anyway, this solved the problem. Hope it helps someone else.

How to move spriteKit node up and down with a button

I want to move my sprite up and down using a button. Since you cant use UIButtons in sprite Kit i am using different SpriteKitNodes. The nodes will be arrow images. But i want to use the arrow images to move my original sprite but simply touching it. I thought that I would use SKAction but I'm stuck. Is it possible to move one sprite using another?
There may not be any provision for buttons in SpriteKit, but one can easily subclass SKSpriteNode to act as a button. I have created such a class which is available on GitHub here.
Using SKAction for movement based on direction buttons is not advisable. Instead, you need to use flags for direction buttons upon which you will move the node in the -update: method.
Maintain the flags as instance variables.
#implementation MyScene
{
BOOL upDirection;
BOOL downDirection;
}
Initialise them to FALSE in the -initWithSize: method.
This is how you should handle the flags in the -update method:
-(void)update:(CFTimeInterval)currentTime
{
/* Called before each frame is rendered */
if (upDirection)
{
myNode.position = CGPointMake(myNode.position.x, myNode.position.y + 5); //Increment value can be adjusted
}
if (downDirection)
{
myNode.position = CGPointMake(myNode.position.x, myNode.position.y - 5); //Decrement value can be adjusted
}
}

Prevent SKLabelNode from receiving Touches

I have a number of SKSpriteNodes with SKLabelNodes as children. What I would like is for any touch within the bounds of the sprite node to be handled by the sprite node, and for its children (the label nodes) to completely ignore touches. I tried doing this:
SKLabelNode *miles = [SKLabelNode labelNodeWithFontNamed:#"Verdana"];
miles.userInteractionEnabled = NO;
But this does not work. The label nodes register touches when I set this property to NO. Next I tried subclassing the label nodes and setting userInterationEnabled = NO in the init, as such:
#implementation BBLabelNode
-(id)init {
if (self = [super init]) {
self.userInteractionEnabled = NO;
}
return self;
}
#end
Sadly, this also did not work. So I am left wondering: how does one properly go about causing a label note to not receive touches at all?
Only SKScene has userInteractionEnabled set to YES by default. So there is no need to set userInteractionEnabled to NO on your objects if you never enabled them.
Double check if something else, an object, node, scene, etc. is setting your label's userInteractionEnabled to YES.

How to run the same block of code - Cocos2d

I am using cocos2d and have 2 AI sprites called TheEvilOne and TheEvilTwo. These 2 sprites call the same bloc of code from the same class sending the sprite as a parameter. However the game gets buggy as I run this code as only one of the sprites preforms the actions and my code stops working. My question is is it possible to call the same bloc of code for multiple sprites simultaneously or is there something wrong with my code. Below is an example of what I am running on my program.
-(void)LoadingEvilActions:(id)sender { //This is Getting Constantly Called
if(loaded == NO) {
theEvilOne = [CCSprite spriteWithFile:#"Evil_Alt_Idle_00.png"]; //These sprites have been declared in my .h file
[self addChild:theEvilOne z:200];
theEvilTwo = [CCSprite spriteWithFile:#"Evil_Alt_Idle_00.png"];
[self addChild:theEvilTwo z:200];
loaded = YES;
}
[self CheckCollision:theEvilOne];
[self setCenterofScreen:theEvilOne];
[self StayonScreen:theEvilOne];
[self AiCharacter:theEvilOne];
[self CheckCollision:theEvilTwo];
[self setCenterofScreen:theEvilTwo];
[self StayonScreen:theEvilTwo];
[self AiCharacter:theEvilTwo];
}
-(void)AiCharacter:(id)sender {
CCSprite *EvilCharacter = (CCSprite *)sender;
if (aiactionrunning == NO){
.... Do More Stuff // For E.G.
id jump_Up = [CCJumpBy actionWithDuration:0.6f position:ccp(randomjump, EvilCharacter.contentSize.height)
height:25 jumps:1];
id jump_Down = [CCJumpBy actionWithDuration:0.42f position:ccp(randomjump,-EvilCharacter.contentSize.height)
height:25 jumps:1];
id seq = [CCSequence actions:jump_Up,jump_Down, [CCCallFuncN actionWithTarget:self selector:#selector(stopAiAction:)], nil];
[EvilCharacter stopAllActions];
[EvilCharacter runAction:seq];
aiactionrunning = YES;
}
}
-(void)stopAiAction:(id)sender {
aiactionrunning = NO;
}
EDIT
I'm running random number generators within my AI Character method and many actions.
The Game already works with just 1 enemy sprite and I decided to try and implement a way to create many.
EDIT 2
I just added the part of the method that stops the sprites being constantly, I was trying to simplify all the code that was irrelevant to my question and forgot to add that part.
I changed the way I am calling my methods like suggested by LearnCocos2d not solving my problem but making it much simpler. I am also not using ARC
One of my sprites is the only one preforming the majority of actions however in some instances the other sprite may preform an actions aswell, but is mainly preformed one sprite. I think my main question is can I call the same method using different sprites passing the sprite as a parameter.
EDIT 3
I have figured out that my problem is there is a Boolean value flag that is enclosing my AiCharacter method, where when one sprite runs through the method it stops the other sprite running the method. Is there some way I can implement an array of records or such so each sprite have their own Boolean flags.
With making this 'array' is it possible to change the Boolean for TheEvilOne and TheEvilTwo using the temp sprite EvilCharacter without doing both separately.
If I understand your question correct you are trying to take one action and run it on more than one character at a time, like:
id move = [CCMoveTo actionWithDuration:0.5f position:ccp(0,0)];
[theEvilOne runAction:move];
[theEvilTwo runAction:move];
That wouldn't work because Evil 1 will not have anything performed on it since it was moved to running on Evil 2. When an action is running it is running on one target. Instead you would:
id move = [CCMoveTo actionWithDuration:0.5f position:ccp(0,0)];
[theEvilOne runAction:move];
[theEvilTwo runAction:[move copy]];
Assuming you are using arc. If not then you'd want to release the copy of course. When it comes to your particular example, why are you trying actions to call methods. Why not just call the method? It seems pointless as LearnCocos2D has pointed out.
Another potential problem you have is that you are constantly creating more and more sprites each time the method is called. Is that on purpose or are there only suppose to be one Evil 1 and one Evil 2? If so then you shouldn't be constantly creating more sprites.
Edit:
Remove your bool. Do something like this instead:
CCSprite *EvilCharacter = (CCSprite *)sender;
if ([EvilCharacter numberOfRunningActions] == 0){
...
}

Resources