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.
Related
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
}
}
I want to apply the same effect to a number of different sprites in my iOS game so am looking in to a general approach rather than an animation created in another program and using its images to create a UIImageView animation.
This effect is an ‘explosion’ type animation where my sprite’s image gets chopped in to different pieces, then, using the physics engine, those pieces get exploded out in different directions before falling down.
I’m new to SpriteKit, but am assuming I have to do this in Quartz? Then re-add the new images as sprites and apply the animation?
I don’t really know where to start, let alone how to continue with the rest of the steps.
Does anyone have any ideas?
Thanks.
1) Upon initialization of said exploding object, first we create a SKTextureAtlas which holds all of the images being animated. You must have the texture atlas resources available.
2) Load all of the relevant SKTextures into an array. This array should be private to the object to which the explosion is happening.
A safe way to do this:
- (void)loadContactImages {
NSMutableArray *frames = [NSMutableArray array];
SKTextureAtlas *explosionFrames = [SKTextureAtlas atlasNamed:#"explosionFrames"];
for(int i = 0; i < explosionFrames.textureNames.count; i++) {
NSString *textureName = [NSString stringWithFormat:#"explosionFrame_%d" , i];
SKTexture *texture = [explosionFrames textureNamed:textureName];
if(texture) {
[frames addObject:texture];
}
}
self.contactFrames = frames;
}
3) You will then define a method which will animate the explosion. Look into the SKAction header for more info. Plug in time per frame that makes sense to your explosion
- (SKAction *)runExplosion {
return [SKAction animateWithTextures:self.contactFrames timePerFrame:0.1];
}
4) As for the exploding pieces, you should preload them, so to limit the overhead of sending the pieces into random directions (with this we won't have the extra overhead of creating all those new sprites at the time of the explosion). Don't forget to give them physics bodies (SKPhysicsBody) and set the isAffectedByGravity property to YES!
- (SKAction *)explosionOfPieces {
return [SKAction runBlock:^ {
for(SKSpriteNode *piece in self.explodingPieces) {
piece.hidden = NO;
piece.position = self.position;
[self addChild:piece];
[piece.physicsBody applyImpulse:CGVectorMake(dx , dy)]; //specify the direction here
}
}];
}
5) Bringing it all together, expose a method to return a sequence of these actions. If you would like to have these two actions occur together, there is also a class method on SKAction called [SKAction group: (NSArray *)]
- (void)explosionSequence {
[self runAction: [SKAction sequence:#[ [self runExplosion] , [self explosionOfPieces]]]];
}
Note: Physics reactions occur in SKScene's. We expose this action so the scene can run it on your object within the scene. You will also want to conform to a protocol called SKPhysicsContactDelegate within your scene and call:
[myExplodingObject explosionSequence];
within the delegate method:
- (void)didBeginContact:(SKPhysicsContact *)contact
Within this SKPhysicsContact object lie two colliding bodies. Both of these bodies ("exploding object" and "explosion trigger") must have their SKPhysicsBody property initialized and their contactTestBitMask property set (so this object knows what it can collide with). I would also set the categoryBitMask property so we can directly introspect the bodies' type. We reference these colliding bodies like this:
- (void)didBeginContact:(SKPhysicsContact *)contact {
if(contact.bodyA.categoryBitMask == explodingObjectCategory && contact.bodyB.categoryBitMask == explodingTriggerCategory) {
[self.myExplodingObject explosionSequence];
}
}
Please look into Apple's docs for more info.
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;
}
I am subclassing SKSpriteNode class to use on a custom sprite. I would like to keep everything as self contained as possible.
Is there a way for a SKSpriteNode know when it is being used on a scene? I mean, suppose another class does this:
MySprite *sprite = [[MySprite alloc] init];
and ages later does this
[self addChild:sprite];
Can the sprite know by its own when it is added as a child of some scene or another node?
SKNode has an property called scene. If this property returns nil it means it isn't in any scene. You can do the following to check that.
if(!MyNode.scene){
//Do something
}
You can also check that at SKNode Doc.
https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKNode_Ref/Reference/Reference.html
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){
...
}