Can a sprite know when it is on the scene? - ios

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

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.

iOS - Adding ‘explosion’ effect (which breaks Sprite in to little pieces and then fall) to sprite in SpriteKit

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.

Render SKScene to SKTexture?

Is there a way in Sprite Kit that I can capture the screen (all the current SKScene rendered nodes) to an SKTexture so that I can apply a CIFilter and then assign the SKTexture back to a new SKSpriteNode?
I know that I can set an SKEffectNode, as the parent of my node tree, apply a filter etc and get the result that way but I really need to have a filtered SKTexture (or SKSpriteNode) that I can reuse later?
EDIT:
Possible solution:
textureFromNode:
Renders and returns a Sprite Kit texture that contains the node’s contents.
Yup that works:
SKTexture *texture = [[self view] textureFromNode:[self scene]];
[blurSprite setTexture:texture];
From Apple docs:
textureFromNode: Renders and returns a Sprite Kit texture that
contains the node’s contents.
Code example:
SKTexture *texture = [[self view] textureFromNode:[self scene]];
[blurSprite setTexture:texture];
You should try with snapshotViewAfterScreenUpdates: method from UIView.
Your SKScene is inside an SKView that has this method.
After that you can extract the image from the view, and create a SKNode with it.

iOS Sprite Kit- SKSpriteNode seems to participate in simulation without physics body

I am working on an iOS game using Sprite Kit.
I recently added an SKSpriteNode into my scene and did NOT create a physics body for it. However, when I build and run, when the player's character(which does have a physics body) moves into the SKSpriteNode, it spins and moves away, like it has a physics body -but I didn't create one for the SKSpriteNode.
Even if I type
sprite.physicsBody = nil;
it still behaves like it's part of the simulation.
I wondered if a physics body is created automatically when you make an SKSpriteNode, but I looked at the documentation and searched it on Google and couldn't find anything to suggest that is the case.
Any help or suggestions would be much appreciated.
This is the code I used to create the sprite (the one that should not be affected by the simulation)
-(void)addSprite
{
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"image"];
sprite.position = CGPointMake(40, 30);
sprite.zPosition = 30;
sprite.name = #"spriteName";
[self addChild:sprite];
}
In another part of the project I have
[self addSprite];
to call the method.
Yes every SKSpriteNode comes with a physics body. Its best to change the physics body's properties instead of getting rid of it altogether. If you want your sprite to be on screen and not interact with any other sprite nodes, do the following:
sprite.physicsBody.dynamic = NO;
sprite.physicsBody.collisionBitMask = 0x0;
sprite.physicsBody.contactTestBitMask = 0x0;

Motion effect for SKNode

I'm getting int SpriteKit. And would like to know how to create motion effect on SKNode object.
For UIView I use following method :
+(void)registerEffectForView:(UIView *)aView
depth:(CGFloat)depth
{
UIInterpolatingMotionEffect *effectX;
UIInterpolatingMotionEffect *effectY;
effectX = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:#"center.x"
type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
effectY = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:#"center.y"
type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
effectX.maximumRelativeValue = #(depth);
effectX.minimumRelativeValue = #(-depth);
effectY.maximumRelativeValue = #(depth);
effectY.minimumRelativeValue = #(-depth);
[aView addMotionEffect:effectX];
[aView addMotionEffect:effectY];
}
I haven't found anything similar for SKNode. So my question is is it possible? And if not then how can I implement it.
UIInterpolatingMotionEffect works in deep level and you can't use an arbitrary keyPath like "cloudX". Even after adding motionEffects, the actual value of centre property won't change.
So the answer is, you can't add a motion effect other than UIView. Using arbitrary property other than particular property such as 'center' or 'frame' is not possible either.
UIInterpolatingMotionEffect just maps the device tilt to properties of the view it's applied to -- it's all about what keyPaths you set it up with, and what the setters for those key paths do.
The example you posted maps horizontal tilt to the x coordinate of the view's center property. When the device is tilted horizontally, UIKit automatically calls setCenter: on the view (or sets view.center =, if you prefer your syntax that way), passing a point whose X coordinate is offset proportionally to the amount of horizontal tilt.
You can just as well define custom properties on a custom UIView subclass. Since you're working with Sprite Kit, you can subclass SKView to add properties.
For example... say you have a cloud sprite in your scene that you want to move as the user tilts the device. Name it as a property in your SKScene subclass:
#interface MyScene : SKScene
#property SKSpriteNode *cloud;
#end
And add properties and accessors in your SKView subclass that move it:
#implementation MyView // (excerpt)
- (CGFloat)cloudX {
return ((MyScene *)self.scene).cloud.position.x;
}
- (void)setCloudX:(CGFloat)x {
SKSpriteNode *cloud = ((MyScene *)self.scene).cloud;
cloud.position = CGPointMake(x, cloud.position.y);
}
#end
Now, you can create a UIInterpolatingMotionEffect whose keyPath is cloudX, and it should* automagically move the sprite in your scene.
(* totally untested code)

Resources