SpriteKit Physics: Ignore when on different z plane - ios

With SpriteKit, is it possible to have two nodes that collide with one another, but then when another node is introduced, have how the physics works change?
For example, consider the following 3 nodes:
- Object
- Object_Hole
- Player
So, a scene containing a Player (orange circle) and an Object (blue rect):
In the above scene, the Player (circle) would be affected by gravity and collide with the Object (square). I know how this works by using the physics body, category and collision bitmasks.
Now, introduce the Object_Hole (green square):
In the above scene, the Object_Hole (green square) overlays the Object (blue rect) - this can be on either the same z plane or a higher plane.
Is it possible to make the physicsBody on the Player (circle) not collide with the Object in the area that the Object_Hole is?
If there's a better way to achieve this than adding an overlaying Node, please let me know. Otherwise, is it possible?
Thanks!

You could look into using categoryBitMask and collisionBitMask:
https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKPhysicsBody_Ref/Reference/Reference.html#//apple_ref/occ/instp/SKPhysicsBody/collisionBitMask
If you want your ball to collide with the object, but not some hole in the object, you will probably want to break your object into 2 pieces with an actual hole in the middle. Set the hole sprite's categoryBitMask and collisionBitMask to be completely different from those of the object and ball.
// sprite1 and sprite2 will never collide.
SKSpriteNode *sprite1 = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(20.0f, 10.0f)];
sprite1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:sprite.size];
sprite1.physicsBody.collisionBitMask = 0xffff0000;
sprite1.physicsBody.categoryBitMask = 0xffff0000;
SKSpriteNode *sprite2 = [SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:CGSizeMake(20.0f, 10.0f)];
sprite2.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:sprite.size];
sprite2.physicsBody.collisionBitMask = 0x0000ffff;
sprite2.physicsBody.categoryBitMask = 0x0000ffff;

Create the blue bar as 2 separate objects, with a gap between them of 0 points (or even overlapping slightly in case the point where they touch causes odd physics reactions) when the 'hole' isn't around, but with a 'hole-sized' gap between them when the hole is preset.

Related

SpriteNode from class not rotating correctly in Scene

I have a Class with a SpriteNode that rotates very wide, when rotated within the main Scene(as if the anchor point is in the middle of the screen and the Sprite is rotating around it). I want it to rotate around the anchor point of itself in the main Scene(anchor point on the Sprite).
So in the Class i have something like the following
- (void)createChainWithPos:(CGPoint)pos {
SKTexture *myTex...
SKTexture *myTex2...
SKSpriteNode *chainFront = [SKSpriteNode spriteWithTexture:myTex];
chainFront.position = pos;
chainFront.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:mytex.size];
[self addChild:chainFront];
[_chainParts addObject:chainFront];
SKSpriteNode *chainSide = [SKSpriteNode spriteWithTexture:myTex2];
chainSide.position = CGPointMake(chainFront.position.x, chainFront.position.y - chainSide.size.height + 6);
chainSide.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:myTex2.size;
[self addChild:chainSide];
[_chainParts addObject:chainSide];
}
I have an loop creating the chain parts in the main file but couldn't get it rotate so stripped it down in an new project. There is actually 4 chain parts but i only did two. The other two are just mirrors of the ones above with their positions mirroring the chainSide.(to position them in a chain like fashion)
and in the Scene
self.chain1 = [chain node];
[self.chain1 createChainWithPos:CGPointMake(self.size.width/2, self.size.height/2);
self.chain1.zRotation = 3.14/4;
[self addChild:self.chain1];
I have a NSMutableArray in the chain class header that i use to hold the chains.
the physics joints
for (int i = 1; i < self.chain1.chainParts.count; i++ {
SKSpriteNode *nodeA = [[self.chain1 chainParts]objectAtindex:i-1];
SKSpriteNode *nodeB = [[self.chain1 chainParts]objectAtindex:i];
SKPhysicsJointPin *pin = [SKPhysicsJointPin jointWithBodyA:nodeA.physicsBody
bodyB:nodeB.physicsBody
anchor:CGPointMake(CGRectGetMidX(nodeA.frame), CGRectGetMinY(nodeA.Frame))];
}
I found that if i set the position of the chain in the middle in the Class, it rotates correctly in the Scene. However, the physics joints just start moving randomly across the screen and isn't correct to the anchor points set. (the physics joints are set in the Scene)
I don't know if i have to convert the coordinates or play with random anchor point positions , but if someone could shed some light if would be greatly appreciated.
You do not want to use the nodes frame, you want to use the nodes size, the frame mid is the screen coordinate + the fitted node size / 2, you want the middle of your sprite only, also for pi / 4 use M_PI_4

Set permanent SKSpritenode image orientation

Hi I have a bunch of round SKSpriteNodes with a circle physical body. Now when these balls roll down a path I want some of these SKSpritenodes image to stay upright even when rolling. So think of an arrow pointing upwards. When the ball starts rolling the arrow spins in circles. But for some balls Id like the arrow to remain pointing up even when the ball rolls. Whats the best way of doing this?
Edit
So an answer was given but from testing it turns out it is not the correct one. Not allowing the ball to rotate affects the way it rolls down the path. So I guess what I want is rotation to be on but the image to always appear to the user like its not rotating. Thanks.
This looks like a job for SupermSKConstraint. Constraints are evaluated and applied after the physics simulation runs on each frame, so you can use them for tasks like making a node point a certain direction regardless of what physics does to it. For this, you'd want a zRotation constraint.
But there's a bit more to it than that. If you set a zero-rotation constraint on the ball:
// Swift
let constraint = SKConstraint.zRotation(SKRange(constantValue: 0))
ball.constraints = [constraint]
You'll find that SpriteKit resets the physics body's transform every frame due to the constraint, so it only sort-of behaves like it's rolling. Probably not what you want. (To get a better idea what's going on here, try adding a zero-rotation constraint to a rectangular physics body in a world without gravity, applying an angular impulse to it, and watching it try to spin in a view with showsPhysics turned on. You'll see the sprite and its physics body get out of sync and shake a bit -- probably due to accumulated rounding errors as the physics engine and the constraint engine fight it out.)
Instead, you can do a bit of what's in 0x141E's answer, but use constraints to make it less code (and run more efficiently):
Give the ball node a circular physics body. (And possibly no texture, if the only art you want for the ball is a non-rotating sprite.)
Add the arrow node as a child of the ball node. (It doesn't need its own physics body.)
Put a zero-rotation constraint on the arrow.
Wait, that doesn't work -- I told the arrow to not rotate, but it's still spinning?! Remember that child nodes are positioned (and rotated and scaled) relative to their parent node. So the arrow isn't spinning relative to the ball, but the ball is spinning. Don't worry, you can still solve this with a constraint:
Tell the constraint to operate relative to the node containing the ball (probably the scene).
Now the constraint will keep the arrow in place while allowing the ball to rotate however the physics simulation wants it to.
Here's some test code to illustrate:
// Step 1: A rectangular spinner so we can see the rotation
// more easily than with a ball
let spinner = SKSpriteNode(color: SKColor.redColor(), size: CGSize(width: 300, height: 20))
spinner.position.x = scene.frame.midX
spinner.position.y = scene.frame.midY
spinner.physicsBody = SKPhysicsBody(rectangleOfSize: spinner.size)
scene.addChild(spinner)
spinner.physicsBody?.applyAngularImpulse(0.1) // wheeeeee
// Step 2: Make the arrow a child of the spinner
let arrow = SKSpriteNode(color: SKColor.greenColor(), size: CGSize(width: 20, height: 50))
spinner.addChild(arrow)
// Step 3: Constrain the arrow's rotation...
let constraint = SKConstraint.zRotation(SKRange(constantValue: 0))
arrow.constraints = [constraint]
// Step 4: ...relative to the scene, instead of to its parent
constraint.referenceNode = scene
Here are two methods to create a ball with a physics body and an arrow:
Add an arrow as a child of a ball
Add both the ball and the arrow directly to the scene
Here's what will happen when you add the above to the SpriteKit simulation:
The arrow will rotate when the ball rotates
Both the arrow and the ball will move/rotate independently
If you want the arrow to rotate with the ball, choose Option 1. If you want the arrow to remain fixed, choose Option 2. If you choose Option 2, you will need to adjust the rotation of the arrow to ensure that it points upward. Here's an example of how to do that.
-(void)didMoveToView:(SKView *)view {
self.scaleMode = SKSceneScaleModeResizeFill;
/* Create an edge around the scene */
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:view.frame];
// Show outline of all physics bodies
self.view.showsPhysics = YES;
CGFloat radius = 16;
SKNode *balls = [SKNode node];
balls.name = #"balls";
[self addChild:balls];
// Create 5 balls with stationary arrows
for (int i = 0;i<5;i++) {
// Create a shape node with a circular physics body. If you are targeting iOS 8,
// you have other options to create circular node. You can also create an SKSpriteNode
// with a texture
SKShapeNode *ball = [SKShapeNode node];
// Create a CGPath that is centered
ball.path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-radius,-radius,radius*2,radius*2)].CGPath;
ball.fillColor = [SKColor whiteColor];
ball.position = CGPointMake(100, 100+i*radius*2);
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:radius];
[balls addChild:ball];
// Create an arrow node
CGSize size = CGSizeMake(2, radius*2);
SKSpriteNode *arrow = [SKSpriteNode spriteNodeWithColor:[SKColor blackColor] size:size];
arrow.name = #"arrow";
arrow.position = CGPointZero;
[ball addChild:arrow];
// Apply angular impulse to the ball so it spins when it hits the floor
[ball.physicsBody applyAngularImpulse:-1];
}
}
- (void) didSimulatePhysics
{
SKNode *balls = [self childNodeWithName:#"balls"];
for (SKNode *ball in balls.children) {
SKNode *arrow = [ball childNodeWithName:#"arrow"];
arrow.zRotation = -ball.zRotation;
}
}
sprite.physicsBody.allowsRotation = NO;
The allowRotation property should control exactly what you are asking.

How to use a SKEmitterNode in sprite kit to achieve a "fog of war" effect?

I'm trying to use a SKEmitterNode to create a shader, kind of like in Pokemon when you are in a cave:
http://www.serebii.net/pokearth/maps/johto-hgss/38-route31.png
Here is the code I have so far :
NSString *burstPath =
[[NSBundle mainBundle] pathForResource:#"MyParticle" ofType:#"sks"];
SKNode *area = [[SKNode alloc] init];
SKSpriteNode *background = [SKSpriteNode spriteNodeWithColor:[SKColor blackColor] size:CGSizeMake(self.frame.size.width, self.frame.size.width)];
background.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
SKEmitterNode *burstNode =
[NSKeyedUnarchiver unarchiveObjectWithFile:burstPath];
burstNode.position = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame));
burstNode.particleBlendMode = SKBlendModeSubtract;
[area addChild:background];
[area addChild:burstNode];
[self addChild:area];
Here is the SKEmitterNode : http://postimg.org/image/60zflqjzt/
I've had two ideas.
The first one was to create a rectangular SKSpriteNode and remove the SKEmitterNode from the rectangular SKSpriteNode. That way, we have a black rectangle with a "hole" in the center, where we can see through.
The second one was to add the rectangular SKSpriteNode and the SKEmitter node to another SKNode (area), then set the particleBlendMode of the SKEmitterNode and finally set the alpha of the SKNode (area) in function of the color. For exemple, if the color of a pixel is black, the alpha value of that pixel will be 1.0 and another pixel is white, that other pixel's alpha value will be 0.0.
This question is a possible duplicate of How to create an alpha mask in iOS using sprite kit in some ways, but since no good answer has been given, I assume it isn't a problem.
Thank you very much.
These are not the nodes you are looking for! ;)
Particles can't be used to make a fog of war, even if you could make them behave to generate a fog of war it would be prohibitively slow.
Based on the linked screenshot you really only need an image with a "hole" in it, a transparent area. The image should be screen-sized and just cover up the borders to whichever degree you need it. This will be a non-revealing fog of war, or rather just the effect of darkness surrounding the player.
A true fog of war implementation where you uncover the world's area typically uses a pattern, in its simplest form it would just be removing (fading out) rectangular black sprites.
Now, with the powerful devices of this era (iPhone 12) is it possible to use 'SKEmitterNode' without lost too much frames per second.
You must build an SKS (SpriteKit Particle File) with this image:
Then, set your vars like this picture:
So, go to your code and add your particle with something like this example:
let fog = SKEmitterNode(fileNamed: "fog")
fog.zPosition = 6
self.addChild(fog)
fog.position.y = self.frame.midY
fog.particlePositionRange.dx = self.size.width * 2.5

Sprite Kit - Do not allow node to move out of screen

I am creating a game where I do not want the player to be able to move out of the screen. The node follows the players moving touch. The code I have "under construction" so the player can't move out on the top or right side, but I do not want the player to be able to move out on any of the sides.
- (void)movementPlayer {
SKAction * actionMoveX = [SKAction moveToX:MIN(location2.x - playerPositionX, self.size.width - (_player.size.width/2)) duration:0];
SKAction * actionMoveY = [SKAction moveToY:MIN(location2.y - playerPositionY, self.size.height - (_player.size.height/2)) duration:0];
[_player runAction:[SKAction sequence:#[actionMoveX, actionMoveY]]];
}
You should create a physics world and add a border rectangle around the screen. This border must have a physics body that is set to collide with the physics body collision category given to your player node. If the player node starts inside the border, the player cannot leave the screen and no additional coding is required (besides properly setting up collision categories for each physics body)
RayWenderlich.com has easy to understand game tutorials that show how to handle collisions following the sprite kit manual.
In your SKScene:
self.physicsBody=[SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
self.physicsWorld.gravity=CGVectorMake=(0,0);
_player.physicsBody=[SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(10,10)];//example; use bodyWithPolygonFromPath:yourCGPath if player has more complex form, or bodyWithCircleOfRadius:radius if its shape is circle
_player.physicsBody.velocity=CGVectorMake(10,10); //or more then 10,10
Good luck!
just add the bottom code to your method that is called it should stop all physical bodies from leaving the screen!! veryuseful!
SKPhysicsBody* gameborderBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
// 2 Set physicsBody of scene to borderBody
self.physicsBody = gameborderBody;
// 3 Set the friction of that physicsBody to 0
self.physicsBody.friction = 1.0f;

SKPhysicBody In the Wrong position

I am creating a simple Sprite Kit game however when i am adding the PhysicsBody to one of my sprites it seems to be going in the wrong position. i know that it is in the wrong position as i have have set
skView.showsPhysics = YES;
and it is showing up in the wrong position.
The Square in the bottom corner is the physics body for the first semicircle. I am using a square at the moment just for testing purposes.
My app includes view following and follows my main sprite when it moves. I implemented this by following apples documentation and creating a 'myworld' node and creating all other nodes from that node.
myWorld = [SKNode node];
[self addChild:myWorld];
semicircle = [SKSpriteNode spriteNodeWithImageNamed:#"SEMICRICLE.png"];
semicircle.size = CGSizeMake(semicircle.frame.size.width/10, semicircle.frame.size.height/10);
semicircle.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:semicircle.frame.size];
semicircle.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
semicircle.physicsBody.dynamic = YES;
semicircle.physicsBody.collisionBitMask = 0;
semicircle.name = #"semicircle";
[myWorld addChild:semicircle];
To centre on the node I call these methods
- (void)didSimulatePhysics
{
[self centerOnNode: [self childNodeWithName: #"//mainball"]];
}
- (void) centerOnNode: (SKNode *) node
{
CGPoint cameraPositionInScene = [node.scene convertPoint:node.position fromNode:node.parent];
node.parent.position = CGPointMake(node.parent.position.x - cameraPositionInScene.x, node.parent.position.y - cameraPositionInScene.y);
}
I don't know if the my world thing makes any difference to the SkPhysics body...
SKPhysicsBody starts at coordinates 0,0 which is at the bottom left hand corner. If you make the area smaller, as you did by width/10 and height/10, you decrease the size but from the bottom left.
I think what you are looking for is bodyWithRectangleOfSize:center: which allows you to manually set the center from which you base your physics body area on.
Update:
Based on what I understand, your smallest semi circle pic size is the same as the screen size. I would suggest you modify the image size to something like the example I have. You can then set the sprite's position as required and set the physics body to the half of the image containing your semi circle.
Your centerOnNode call should be put in the didEvaluateActions function instead of the didSimulatePhysics function. This is because you need to move the world before the physics are drawn so that they stay in sync. Similar question found here: https://stackoverflow.com/a/24804793/5062806

Resources