Why Are Objects in the Same Sknode Layer Not Interacting with Each Other

Why are objects in the same SKNode layer not interacting with each other?

When a node is placed in the node tree, its position property places it within a coordinate system provided by its parent.

Sprite Kit uses a coordinate orientation that starts from the bottom left corner of the screen (0, 0), and the x and y values increase as you move up and to the right.

  • For SKScene, the default value of the origin – anchorPoint is (0, 0), which corresponds to the lower-left corner of the view’s frame rectangle. To change it to center you can specify (0.5, 0.5)

  • For SKNode, the coordinate system origin is defined by its anchorPoint which by default is (0.5, 0.5) which is center of the node.

In your project you have layerMainGame added for example to the scene, his anchorPoint is by default (0.5,0.5) so the origin for the children like your fish is the center, you can see it if you change the fish positions like:

func spawnNew(fish: SKSpriteNode) {
layerMainGame.addChild(fish)
fish.position = CGPointZero // position 0,0 = parent center
}

Hope it help to understand how to solve your issue.


Update: (after your changes to the main question)

To help you better understand what happens I will give an example right away:

override func didMoveToView(view: SKView) {
var layerMainGame = SKNode()
addChild(layerMainGame)

let pipFish = SKSpriteNode(color: UIColor.yellowColor(), size: CGSizeMake(50,50))
pipFish.name = "son"
self.addChild(pipFish)

let layerPipFish = SKSpriteNode(color: UIColor.yellowColor(), size: CGSizeMake(50,50))
layerPipFish.name = "son"
layerMainGame.addChild(layerPipFish)

enumerateChildNodesWithName("son") { [unowned me = self] node, _ in
print(node)
}
}

Output:

Sample Image

Now I will simply change the line:

layerMainGame.addChild(layerPipFish)

with:

self.addChild(layerPipFish)

Output:

Sample Image

What happened?

As you can see enumerateChildNodesWithName written as your and my code print only childs directly added to self (because actually we launch enumerateChildNodesWithName which it is equal to launch self.enumerateChildNodesWithName )

How can I search in the full node tree?

If you have a node named "GoldPiranha" then you can search through all descendants by putting a // before the name. So you would search for "//GoldPiranha":

enumerateChildNodesWithName("//GoldPiranha") { [unowned me = self] ...

children's children with enumerateChildNodes(withName:using:)

That function is called on the parent to search it's children and that's it. It does not search the children of children. You would have to call that function on the children to search it's children.

I've ran into issues with this before when I didn't know why a certain node with a name was not being found. It's because I had that node I was looking for was a child of another child.

This Link to my Stack Overflow Question on why my node was not interacting with a certain node was because the node I wanted was not being found with enumerateChildNodes and I finally realized it at the very bottom in a comment.

how-to have multiple nodes with the same name with each interacting separately with environment

Use

let touchedNode = nodeAtPoint(location)

instead of

if enemy1.containsPoint(location)

This will return an SKNode which you can then remove etc without having to check for each enemy individually.

Background of 4 images added for each other

I wrote for you but in objective-c, I think you can convert it to swift.

    SKSpriteNode *bg1 = [SKSpriteNode spriteNodeWithImageNamed:@"bg1"];
bg1.zPosition = 1;
SKSpriteNode *bg2 = [SKSpriteNode spriteNodeWithImageNamed:@"bg2"];
bg2.zPosition = 2;
SKSpriteNode *bg3 = [SKSpriteNode spriteNodeWithImageNamed:@"bg3"];
bg3.zPosition = 3;
SKSpriteNode *bg4 = [SKSpriteNode spriteNodeWithImageNamed:@"bg4"];
bg4.zPosition = 4;

SKAction *action = [SKAction fadeAlphaTo:1.0 duration:1];
SKAction *wait = [SKAction waitForDuration:1];
SKAction *resetAlpha = [SKAction fadeAlphaBy:0.0 duration:0];

[self runAction:[SKAction repeatActionForever:[SKAction sequence:@[[SKAction runBlock:^{
[bg1 runAction:resetAlpha];
[bg2 runAction:resetAlpha];
[bg3 runAction:resetAlpha];
[bg4 runAction:resetAlpha];
}], [SKAction runBlock:^{
[bg1 runAction:action];
}], wait, [SKAction runBlock:^{
[bg2 runAction:action];
}], wait, [SKAction runBlock:^{
[bg3 runAction:action];
}], wait, [SKAction runBlock:^{
[bg4 runAction:action];
}], wait]]]];

SpriteKit: SKSpriteNode containing another SKSpriteNodes but only one SKPhysicsBody

Re EDIT:

Use an SKNode to control all of your character's body parts, including the main body:

SKNode (controller)
SKSpriteNode (head)
SKSpriteNode (body)
SKSpriteNode (leg1)
SKSpriteNode (leg2)

That gives you more flexibility.

To make the head the "master" position, this should do the trick:

-(void) didSimulatePhysics
{
self.parent.position = [self convertPoint:self.position toNode:self.parent.parent];
self.position = CGPointZero;
}

How to handle multiple sprites' zPositions as they move around each other?

1) As a sprite move upwards (as in y position is increasing) I want it to appear to move further back behind other sprites, so I want the zPosition to decrease in comparison to the other sprites. How do games handle this?

This can simply be done by creating a custom class (I would not recommend extending this) and overriding the zPosition to be the inverse of Y

E.G.

class Player : SKSpriteNode
{

override var zPosition : CGFloat
{
get
{
//Yes it looks weird to be setting zPosition on get, but we want to ensure the variable storing zPosition is also changed in case any property reads it
super.zPosition = -super.position.y
return super.zPosition
}
set
{
//do nothing
}
}
}

2)What if I have 100 sprites on the screen that are all moving around randomly?

Overriding this will handle every sprite on the screen (that is of type 'Player' in my example) the y position determines the zPosition

3)How would I make sure all the arms, legs, clothes, and other layers of sprites appear correctly in front or behind every other sprite?
Now this gets a little tricky. zPosition is relative to its parent, so you would need to account for how many layers deep your sprite can get zPosition wise, and plan accordingly. Say we have a sprite with shirt pants and a belt.

If we structured the sprite like this:

body

->shirt

->pants

->belt

Then we can set all the zPositions to 0 of those child nodes, because the rendering order would use the zPosition followed by tree order, and nothing would change:

But if we structured it like this:
body

->shirt

->pants

--->belt

and we want to give the affect of the shirt being between the pants and belt, then we would need to compensate by setting a zPosition of 1 to both shirt and belt.

4)What if I have a sprite with 20 layers of sprite children and it needs to move seamlessly in front and behind other sprites with 20 sprite children?
Now we have a problem with battling other sprites, so we need to apply some math to our zPosition on the Player to fix it.
class Player : SKSpriteNode
{

    override var zPosition : CGFloat
{
get
{
//Yes it looks weird to be setting zPosition on get, but we want to ensure the variable storing zPosition is also changed in case any property reads it
super.zPosition = -super.position.y * 2 //(We need at least 2 zPosition slots reserved for every sprite)
return super.zPosition
}
set
{
//do nothing
}
}
}

5)But can I somehow make one sprite and its children layers into 1 layer (or make all the left arm sprites into 1 layer and the right arm sprites into another layer)?
6) I see games that handle this, but what's the concept behind how this is handled?
Yes, you can use view.texture(from:) to convert any node to a texture, then create a new sprite out of the texture.

7)What if I wanted these sprites to interact like one sprite's arms grab around another sprite (so one arm will be behind a second sprite person, but the opposite arm will be completely in front of the other sprite person
8)How are the layers handled to appear in the correct zPosition
9)Maybe this is not possible?

This part now gets tricky, instead of doing the multiplication, we would need every nodes zPosition to change.

We also would need to calculate the absolute position of every sprite to know where we are on the y axis

In order to do that, we have at least 3 options,

a)we can make a custom protocol + custom classes for every node we place on the scene
b) can write extensions for the the node types we place on scene (e.g. SKSpriteNode and SKShapeNode) with the exception of SKNode because that is the base node class
c) we can do something called Key Value Observation, and we need to listen to when the position changes on a sprite. The reason why we need to do KVO is because your sprite may be structured where you have SKNodes, and we can't override position on this.

All three of these methods would require extensive work and would require optimizations unique to your game environment to run efficiently, which i am not going to write in detail how to do this.

Can't tap SKSpriteNode - no touch detected ios

Have you tried setting the name property of your spritenode?

In your initialization for mySKSpriteNode:

mySKSpriteNode = [[SKSpriteNode alloc] initWithTexture:sometexture];
mySKSpriteNode.name = @"thisIsMySprite"; // set the name for your sprite
mySKSpriteNode.userInteractionEnabled = NO; // userInteractionEnabled should be disabled

Then:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:@"thisIsMySprite"]) {
NSLog(@"mySKSpriteNode was touched!");
[node runAction:[SKAction fadeOutWithDuration:0]];
}
}


Related Topics



Leave a reply



Submit