Emitted particles follow parent node - sprite-kit

I am setting up an SKNode as follows:
- (id)init {
self = [super init];
if (self) {
SKTextureAtlas *atlas = [SKTextureAtlas atlasNamed:#"Sprites"];
SKSpriteNode *spriteNode = [SKSpriteNode spriteNodeWithTexture:[atlas textureNamed:#"Cat"]];
[self addChild:spriteNode];
// Create particle trail
SKEmitterNode *emitterNode = [NSKeyedUnarchiver unarchiveObjectWithFile:[[NSBundle mainBundle] pathForResource:#"Hearts" ofType:#"sks"]];
emitterNode.position = CGPointMake(0, 20);
[self insertChild:emitterNode atIndex:0];
// Setting up the physics
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:spriteNode.frame.size];
self.physicsBody.dynamic = YES;
self.physicsBody.affectedByGravity = NO;
}
return self;
}
When this node moves around horizontally along the X-axis, the source of the particle trail moves along with it, which is expected. What is not expected is that emitted particles do this as well, when instead I expect them to move straight up along the Y-axis from their original X position.
Is there a way to prevent this behaviour?
Creation (in scene):
SPAPlayer *player = [[SPAPlayer alloc] init];
player.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
[self addChild:player];
Movement code:
[node.physicsBody applyForce:CGVectorMake(20 * data.acceleration.x, 0)];
Image for reference:

Make the emitter a child of the sprite (as you already have).
To make the particles move independently from where the player node is, assign the SKEmitterNode's targetNode property, preferably assign it the scene as in:
// be sure self.scene isn't still nil when you do this, for instance in node's init
emitter.targetNode = self.scene;
This will spawn the particles at the player sprite position but they won't travel along with it.

Related

myParticleEmitter weirdness (creates artifact at 0.0) while drawing from point A to point B

A little weirdness here, I have a particle emitter added to an SKShapeNode
(the particles should follow the line)
yet when it does it also has a bit of particle system sitting at 0,0
Ive moved it up a little so you can see it better
Any thoughts to how this might be happening?,
I'll post the code below, but still I cant find anything, I have tested with other textures setting setStrokeTexture - incase the line is making some sort of sneaky extra part - but no it seems fine....and creates a line between the sprites...
in LinkWithNumberClass //SKSpriteNode SKPhysicsContactDelegate
if (isUsingParticles == YES) {
//create new line
SKShapeNode*lineNode02 = [SKShapeNode node];
lineNode02.lineWidth = 10;
lineNode02.name = #"lineNode";
//clear line of colour
lineNode02.fillColor = [SKColor clearColor];
lineNode02.strokeColor = [SKColor clearColor];
//create the path the line follows (line joins two sprites)
CGMutablePathRef pathToDraw = CGPathCreateMutable();
CGPathMoveToPoint(pathToDraw, NULL, spriteA.position.x, spriteA.position.y);
CGPathAddLineToPoint(pathToDraw, NULL, spriteB.position.x, spriteB.position.y);
lineNode02.path = pathToDraw;
[self addChild:lineNode02];
//Add Particles
myParticlePath = [[NSBundle mainBundle] pathForResource:_parName ofType:#"sks"];
myParticleEmitter = [NSKeyedUnarchiver unarchiveObjectWithFile:myParticlePath];
[self addChild:myParticleEmitter];
CGFloat distancebtween = SDistanceBetweenPoints(spriteA.position, spriteB.position);
myParticleEmitter.particlePositionRange = CGVectorMake( particleLength,0 );
myParticleEmitter.position = CGPointMake(distancebtween/2, +10);
self->myParticleEmitter.targetNode = self.scene;
int particleTime = 3;
SKAction *followTrack =
[SKAction followPath:pathToDraw
asOffset:YES
orientToPath:YES
duration:particleTime];
SKAction *forever = [SKAction repeatActionForever:followTrack];
self->myParticleEmitter.particleAction = forever;
}
In Game Layer
-(void)addStaticLinkedSpriteWithParticles
{
twoSpritesWithParticlesBridge =
[[LinkWithNumber alloc]initWithlinkSpriteA:#"Object"
spriteB:#"Object"
andPlistAnimation:#"Aniamtions"
distbetween:100
hasParticles:YES
ParticlesNamed:#"Fire"];
[self addChild:self->twoSpritesWithParticlesBridge];
}
With thanks :)
Natalie.
In the end I go it to work not needing to draw a line at all.
Create particle system Position on SpriteA
find distance between
the two sprites (to get length for particles)
Adjust length of the particles in emitter properties
reposition the
particles so they are centered between the two sprites
if (isUsingParticles == YES) {
//create particles
myParticlePath = [[NSBundle mainBundle] pathForResource:_parName ofType:#"sks"];
myParticleEmitter = [NSKeyedUnarchiver unarchiveObjectWithFile:myParticlePath];
[self addChild:myParticleEmitter];//try moving it to the line node
//find length needed for particles
CGFloat distancebtween = SDistanceBetweenPoints(spriteA.position, spriteB.position);
//Adjust length of particles
myParticleEmitter.particlePositionRange = CGVectorMake( particleLength,0 );
//center particles between the two sprites
myParticleEmitter.position = CGPointMake(spriteA.position.x+distancebetween/2, spriteA.position.y);
myParticleEmitter.targetNode = self.scene;
}
Hope it helps someone else :)

Drawing Trail Lines for SKSpriteNode

I have a custom LineBall class as shown below:
#implementation LineBall
-(instancetype) init {
self = [super initWithImageNamed:LINE_BALL_IMAGE];
self.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:self.size.width/2];
self.physicsBody.categoryBitMask = BBPhysicsCategoryLineBall;
self.name = #"lineBall";
self.physicsBody.friction = 0.0f;
self.physicsBody.restitution = 1.0f;
self.physicsBody.linearDamping = 0.0f;
self.physicsBody.allowsRotation = NO;
self.physicsBody.dynamic = YES;
return self;
}
Later on I add this to the GameScene and it works as expected. The problem is that now I want to draw lines wherever the LineBall travels. How can I do that?
Here's one way to draw a trail behind a moving ball.
In the didSimulatePhysics method:
Store the ball's positions over time in a mutable array. Insert at index = 0.
Remove the oldest (last) array element if the number of elements exceeds the max trail size
Create a mutable CGPath by looping over the elements of the array using CGPathAddLineToPoint to connect each point
Remove the old trail from the scene if it exist
Create the ball's trail by creating an SKShapeNode from the path using shapeNodeWithPath
Add the SKShapeNode to the scene

Multiple sprites with same SKPhysics body object

I am building an airport simulation game using sprite kit. My layout for the game states intact before adding SKPhysics body and once SKPhysicsbody is set for nodes, my sprite nodes goes wary.
This is what I am adding to scene without SKPhysicsBody. Imagine a airport with many gates and flights standing next to the gates. That is what I am trying to achieve with below code.
#implementation MyScene
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
allPlanes = [[NSMutableArray alloc]init];
// setting gravity to 0
[[self physicsWorld] setGravity:CGVectorMake(0, 0)];
[[self physicsWorld] setContactDelegate:self];
[self setBackgroundColor:[UIColor whiteColor]];
// Below method will provide runway and other static objects that can be seen in an airport
[self setupAirport];
/* This is where I am setting up by plane sprites.This method will provide a node which is added to the scene. As you can notice, the sprites are added at specific coordinates until they fill the screen. Imagine three or four gates with flights standing by those gates. That is what I am trying to achieve with below while loop */
int xval = 20;
while (xval < kScreenWidth)
{
[self setupFlightAtPoint:xval];
xval = xval + 60;
}
}
return self;
}
Now code for methods [self setupFlightAtPoint:xPos]
-(void) setupFlightAtPoint:(CGFloat ) xPos
{
// Below code will provide a static gate like object.gateNode is of type Gate class which is a subclass of SKNode
gateNode = [[Gate node] newGate];
[gateNode setPosition:CGPointMake(xPos, kScreenHeight * 0.37)];
[self addChild:gateNode];
// Below code provide plane node and positions it near the gate object.Plane is subclass of SKNode
Plane *plane = [[Plane alloc]init];
imageNode = [plane newPlane];
imageNode.planeIdentifier = xPos;
[imageNode setPosition:CGPointMake(gateNode.frame.origin.x + 12, gateNode.frame.origin.y+15)];
[allPlanes addObject:imageNode];
[self addChild:imageNode];
}
Plane object method
-(instancetype) newPlane
{
[self setScale:0.10];
SKSpriteNode *spriteNode = [SKSpriteNode spriteNodeWithImageNamed:#"plane.png"];
[self addChild:spriteNode];
return self;
}
Till now everything looks fine.Please see attached image called scene1 to see what I see with above code.
Now my problem begins here when I am trying to set physics body to my plane sprites. In my "newPlane" method, I am adding below code
-(instancetype) newPlane
{
[self setScale:0.10];
SKSpriteNode *spriteNode = [SKSpriteNode spriteNodeWithImageNamed:#"plane.png"];
[self addChild:spriteNode];
SKPhysicsBody *planePhysics = [SKPhysicsBody bodyWithRectangleOfSize:spriteNode.frame.size];
[spriteNode setPhysicsBody:planePhysics];
[[spriteNode physicsBody] setAffectedByGravity:NO];
return self;
}
After setting Physicsbodies, my scene looks like this
only one plane sprite is seen in my scene now and I am not able to figure out why?
Try initializing and assigning the physics body before adding the sprite as child:
-(instancetype) newPlane
{
[self setScale:0.10];
SKSpriteNode *spriteNode = [SKSpriteNode spriteNodeWithImageNamed:#"plane.png"];
spriteNode.position = CGPointMake(100, 200);
SKPhysicsBody *planePhysics = [SKPhysicsBody bodyWithRectangleOfSize:spriteNode.frame.size];
spriteNode.physicsBody = planePhysics;
spriteNode.physicsBody.affectedByGravity = NO;
[self addChild:spriteNode];
return self;
}
I also converted the code to use dot notation, I find this easier to type and read.

Creating a trail with SKEmitterNode and particles in SpriteKit

I am trying to make it so a particle I made follows the player whenever the player is moved. The effect I am trying to replicate is like when you are on a website and they have some sort of set of objects following your mouse. I tried to do this by making the particle move by the amount the player does, but it is not reproducing the intended effect. Any suggestions? My code:
Declaring particle
NSString *myParticlePath = [[NSBundle mainBundle] pathForResource:#"trail" ofType:#"sks"];
self.trailParticle = [NSKeyedUnarchiver unarchiveObjectWithFile:myParticlePath];
self.trailParticle.position = CGPointMake(0,0);
[self.player addChild:self.trailParticle];
Move method
-(void)dragPlayer: (UIPanGestureRecognizer *)gesture {
if (gesture.state == UIGestureRecognizerStateChanged) {
//Get the (x,y) translation coordinate
CGPoint translation = [gesture translationInView:self.view];
//Move by -y because moving positive is right and down, we want right and up
//so that we can match the user's drag location (SKView rectangle y is opp UIView)
CGPoint newLocation = CGPointMake(self.player.position.x + translation.x, self.player.position.y - translation.y);
CGPoint newLocPart = CGPointMake(self.trailParticle.position.x + translation.x, self.trailParticle.position.y - translation.y);
//Check if location is in bounds of screen
self.player.position = [self checkBounds:newLocation];
self.trailParticle.position = [self checkBounds:newLocPart];
self.trailParticle.particleAction = [SKAction moveByX:translation.x y:-translation.y duration:0];
//Reset the translation point to the origin so that translation does not accumulate
[gesture setTranslation:CGPointZero inView:self.view];
}
}
Try this:
1) If your emitter is in the Scene, use this emitter's property targetNode and set is as Scene. That means that particles will not be a child of emitter but your scene which should leave a trail..
Not sure if this is correct (I do it in C#):
self.trailParticle.targetNode = self; // self as Scene
And some extra:
2) I think you could rather attach your emitter to self.player as child so it would move together and automatically with your player and then there is no need of this:
self.trailParticle.position = [self checkBounds:newLocPart];
self.trailParticle.particleAction = [SKAction moveByX:translation.x y:-translation.y duration:0];
In your update loop Check if your player is moving along the X axis.
Then create a sprite node each time this is happening. In this example name your player "player1"
The key here is your particle must have a max set in the particles column near birthrate.
The blow code works for me.
-(void)update:(CFTimeInterval)currentTime {
// Find your player
SKNode* Mynode = (SKSpriteNode *)[self childNodeWithName:#"player1"];
// Check if our player is moving. Lower the number if you are not getting a trail.
if (Mynode.physicsBody.velocity.dx>10|
Mynode.physicsBody.velocity.dy>10|
Mynode.physicsBody.velocity.dx<-10|
Mynode.physicsBody.velocity.dy<-10){
// Unpack your particle
NSString *myParticlePath = [[NSBundle mainBundle] pathForResource:#"trail" ofType:#"sks"];
//Create your emitter
SKEmitterNode *myTrail = [NSKeyedUnarchiver unarchiveObjectWithFile:myParticlePath];
// This ensures your trail doesn't stay forever . Adjust this number for different effects
myTrail.numParticlesToEmit=10;
// The length of your trail - higher number, longer trail.
myTrail.particleLifetime = 2.0;
//Set the trail position to the player position
myTrail.position=Mynode.position;
//Add the trail to the scene
[self addChild:myTrail];
}
}

SpriteKit 1 Dimensional Movement

I'm using apple's Sprite Kit and I need to move a SKSprite Node in horizontal movement only. I want the rest of the physics to apply but only in the horizontal component.
Context: This is for an object supposedly on a slider that can bounce back and forth. I have everything done but if it is hit the end the wrong way it simply floats off vertically, how can I simply make it ignore all forces in the vertical direction.
By putting the node's position back at the desired Y coordinate every frame after physics has been simulated:
-(void) didSimulatePhysics
{
CGPoint pos = horizontalMoveNode.position;
pos.y = fixedVerticalPosY;
horizontalMoveNode.position = pos;
}
Add this method to your scene class and apply it to whichever node(s) you want to lock in at a given Y coordinate.
You could use constraints for this purpose. I made a short sample with a node that only moves in a fixed X range and never leaves a specified Y position:
SKSpriteNode* node = [SKSpriteNode node];
node.color = [SKColor greenColor];
node.size = CGSizeMake(20, 20);
SKRange* rangeX = [[SKRange alloc] initWithLowerLimit: 100 upperLimit: 400];
SKRange* rangeY = [SKRange rangeWithConstantValue: 100];
SKConstraint* positionConstraint = [SKConstraint positionX: rangeX Y: rangeY];
NSArray* constraintArray = [NSArray arrayWithObject: positionConstraint];
node.constraints = constraintArray;
[self addChild: node];
This method is from SKAction class to move objects only on Horizontal or X-axis:-
[mySpriteNode runAction:[SKAction moveToX:260 duration:0.5]];
I hope this work's for you.