SpriteKit Physics Bodies Collisions in Multiple Layers - sprite-kit

Okay, so I'm writing code for a space blaster game, and all seems to be going well, minus these crucial issues.
First, I added a spaceship (with PhysicsBody) as a child of self (GameScene).
Then I added a background layer as a node also as a child of self, behind the spaceship, and simulated movement by moving the background around behind the spaceship via joystick control.
I added a node with a boundary physics Edge Loop body so the background wouldn't move beyond the ship, and added that node as a child of the bgLayer.
I have objects for the spaceship to shoot at, but I want them to move around as the background does, so I added them as children of the bgLayer.
Of course, I needed a laser when the spaceship fires, so I added this as well as a child of the bgLayer.
_spaceship.physicsBody = (custom physicsBody code);
[self addChild:_spaceship];
_bgLayer = [SKNode node];
[self addChild:_bgLayer];
_bounds = [SKNode node];
_bounds.physicsBody = (physicsBody edgeLoop code);
[_bgLayer addChild:_bounds];
_otherObjects.physicsBody = (custom physicsBody code);
[_bgLayer addChild:_otherObjects];
_laser.physicsBody = (custom physicsBody code);
[_bgLayer addChild:_laser];
All is well, the background doesn't move beyond the spaceship,the other objects move as the background moves, and the laser fires when called upon.
My first big dilemma is when my laser fires, it does not move as the background moves, but the background moves behind it. I could probably make do but it looks funny if the laser always moves in parallel with the ship. This seems odd to me since I added the laser as a child of _bgLayer.
Second, my laser's physicsBody doesn't seem to recognize the EdgeLoop body, and sails right on through it. Also, my spaceship's physicsBody seems to recognize the EdgeLoop body, but it does not recognize the other objects that are children of _bgLayer.
Do Physics Bodies that are not children of the same layer recognize each other?
And why doesn't my laser behave similarly to other children of the same layer?

Moving the world by changing its position will affect
Children with physics bodies
Children without physics bodies
Moving the world by applying a force/impulse or by settings its velocity will affect
Children without physics bodies
Instead of moving the world by setting its velocity, you can add a camera (an SKNode with a physics body) to the world, move the camera by setting its velocity, and then update the position of the world to center the camera. You can center the camera in the didFinishUpdate method.

-(void)fireLaser
{
_laser = [SKSpriteNode spriteNodeWithImageNamed: [NSString stringWithFormat:#"blueLaser"]];;
_laser.zRotation = _spaceShip.zRotation;
CGVector rotationVector = RadiansToVector(_spaceShip.zRotation);
_laser.position = (Custom code for positioning the laser just in front of the spaceship);
_laser.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_laser.size];
[_bgLayer addChild:_laser];
_laser.physicsBody.velocity = CGVectorMake(rotationVector.dx * LASER_SPEED, rotationVector.dy * LASER_SPEED);
}
[self fireLaser] is called in touchesBegan when a particular SpriteNode is touched.
The laser fires beautifully, but does not scroll with the background, but rather moves in relation to the spaceship. Background scrolls with a PhysicsBody and a setVelocity method is called when the joystick moves, thus simulating spaceship motion, when in reality the spaceship never leaves the center of the screen. Physics categories prevent the background physics body from colliding with anything. LASER_SPEED is simply a static const float.

Related

SceneKit - Movement + Gravity = Strange Movement

My Scene:
One ball (Dynamic Physics Body)
One cube as platform (Static Physics Body)
Default Scene-Gravity
Default masses
When I run the scene the ball falls correctly on the platform.
But when I add movement (delta-time movement on the x-axis in the render method), the ball falls strange/slowly down.
Did I do something wrong?
Dynamic physics bodies should only be moved using physics, e.g.applyForce(_:at:asImpulse:) You may be able to get it to work by calling resetTransform() after updating the position, but at a cost to performance.
https://developer.apple.com/documentation/scenekit/scnphysicsbody/1514782-resettransform

Small, "bouncy" lags when pushing an SKShapeNode with another shape moved by an SKAction

I am creating a 2D autoscroller and when my player (with a circle physicsBody) collides with an SKAction moveTo-powered obstacle which is moving towards it, it makes small, almost unnoticable lags. I tried lowering the friction and the density of the objects, but nothing helped. Any ideas?
You have chosen to use a circle with phycsicsBody. An SKPhysicsBody object is used to add physics simulation to a node. When a scene processes a new frame, it performs physics calculations on physics bodies attached to nodes in the scene. These calculations include gravity, friction, and collisions with other bodies. After the scene completes these calculations, it updates the positions and orientations of the node objects.
You have also chosen to use SKActions, when using the actions to move a body such changes don't go through the physics engine, indeed usually you could add unwanted actions and unexpected events as "bouncy lags".
So, if you're using physics to move a body, you shouldn't use move actions, apply an impulse or force, or set it's velocity directly
Setting restitution to zero may help. This controls the elasticity, or bounciness of a sprite.

Collision without sprites colliding

I have some simple code that adds a block sprite at the leftmost part of a tile like this :
block.position = CGPointMake((-self.size.width * 0.5), 0);
[self addChild:block];
Since the anchor point is in the middle of the block sprite. self refers to the actual tile.
This works fine and indeed adds the block on the left side of the tile sprite.
Now I also have a player sprite that can collide with that block if it tries to go through it. That also works just fine.
The problem happened when i tried to get the block sprite to show in the exact same spot using another anchor point (i need a new anchor point for a shrink effect i wanted to create - which appears to work fine btw).
The new code becomes :
block.position = CGPointMake(-(self.size.width * 0.5), -(self.size.width * 0.5));
block.anchorPoint = CGPointZero;
[self addChild:block];
The new block appears in a similar to the first case position (though not totally identical).
I am not sure why the position is not identical but i can fix that by adding/subtracting 1 or 2 from the x,y points.
The weird problem is that if my player sprite now tries to go below that block on the tile below (which is an open tile without any blocks), i get a contact between the player and the block.
I have even added debug paths with SKShapeNode to make sure that the player and block sprites do not actually collide. And they don't ! But i still get a collision event.
The player scale is (0.8, 0.9), but i don't think this would play much of a role.
I really don't get why this could be happening. Any ideas guys ?
Changing the sprite's anchor point have no effect on the physics body.
When talking about CGRect, the rect origin is at point {0, 0},
So what is happening is that you now have a sprite that its image is centred around anchor point {0, 0} but with a physics body, that starts at {0, 0} and is the size of the sprite, meaning that it is centred around {0.5, 0.5}.
So even that the player doesn't collide with the image of the sprite, it does collide with its physics body.
What is happening is that you have a physics body, that before had the same centre point as the sprite,
But as oppose to before, where the sprite anchor point were in the middle, which would 'fit' into the physics body,
Now the sprite's anchor point is {0, 0}, which causes the physics body centre point, to actually be the most bottom left point of the sprite.
To resolve this, you need to offset your physics body, so it will be centred around the new anchor point.
For example:
block.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:block.size center:CGPointZero];
CGPoint newCenter = CGPointMake(block.size.width, block.size.height);
block.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:block.size centre:newCenter];
Good luck mate.
EDIT- On a second thought, I think I got confused with the offset directions.
An edit has been made to correct it.

SKshapenode is not responding to Physicsbody

I have created a SKShapeNode and I have assigned a physicsBody to it. However, it is not being triggered when there is contact.
Creation of SKShapeNode code:
-(SKShapeNode*)gravityline{
//SKSpriteNode *lolo=[[SKSpriteNode alloc]init];
SKShapeNode *lolo = [[SKShapeNode alloc] init];
CGPoint fff=CGPointMake(ray1.position.x, ray1.position.y);
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, fff.x, fff.y);
CGPathAddLineToPoint(path, 0,rayoriginpoint.x,rayoriginpoint.y );
CGPathCloseSubpath(path);
lolo.path = path;
lolo.name=#"gravityline";
lolo.strokeColor=[SKColor greenColor];
lolo.glowWidth=.1;
CGPathRelease(path);
lolo.physicsBody=[SKPhysicsBody bodyWithEdgeFromPoint:fff toPoint:rayoriginpoint];
//lolo.physicsBody=[SKPhysicsBody bodyWithEdgeLoopFromPath:path];
//lolo.physicsBody=[SKPhysicsBody bodyWithPolygonFromPath:path];
lolo.physicsBody.categoryBitMask=raylightCategory;
lolo.physicsBody.collisionBitMask=batCategory;
lolo.physicsBody.contactTestBitMask=batCategory;
lolo.physicsBody.usesPreciseCollisionDetection=YES;
lolo.physicsBody.linearDamping=0;
lolo.physicsBody.restitution=1.0;
lolo.physicsBody.dynamic=NO;
return lolo;
}
Here is the trigering code :
- (void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody, *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA;
secondBody = contact.bodyB;
}
else
{
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
if (firstBody.categoryBitMask == raylightCategory && secondBody.categoryBitMask==batCategory)
{
NSLog(#"Contact with bat have been made");
[secondBody.node removeFromParent];
}
}
If anybody has a clue what I did wrong, why the SKShapeNode is not activating the physicsBody, please let me know.
This certainly won't work:
lolo.physicsBody=[SKPhysicsBody bodyWithEdgeFromPoint:fff toPoint:rayoriginpoint];
If anything this will return a body already assigned to a different node. But I guess it simply returns nil.
This commented line will not work either:
//lolo.physicsBody=[SKPhysicsBody bodyWithEdgeLoopFromPath:path];
Edge shapes will create static (as in: immovable) bodies. Hence this node won't move through physics and if I'm not mistaken you also won't get contact response from contacts with static bodies, only dynamic bodies.
This one should work:
//lolo.physicsBody=[SKPhysicsBody bodyWithPolygonFromPath:path];
But then you're setting the body to a static body here:
lolo.physicsBody.dynamic=NO;
Hence the same rules apply as if you were creating a body with an edge loop.
Your code doesn't quite show what your "gravityLine" is colliding with. I can only speculate from its description that it's not detecting a collision with a round shaped object (a ball and a bat).
The "gravityLine" method seems to be returning a SKShapeNode that is defined by an edge-based shape:
lolo.physicsBody=[SKPhysicsBody bodyWithEdgeFromPoint:fff toPoint:rayoriginpoint];
When it comes to collisions, it is important to read Apple's Sprite Kit Programming Guide, specifically it's explanation of three types of shapes.
1) A dynamic volume simulates a physical object with volume and mass that can be affected by forces and collisions in the system. Use dynamic volumes to represent items in the scene that need to move around and collide with each other.
2) A static volume is similar to a dynamic volume, but its velocity is ignored and it is unaffected by forces or collisions. However, because it still has volume, other objects can bounce off it or interact with it. Use static volumes to represent items that take up space in the scene, but that should not be moved by the simulation. For example, you might use static volumes to represent the walls of a maze.
While it is useful to think of static and dynamic volumes as distinct entities, in practice these are two different modes you can apply to any volume-based physics body. This can be useful because you can selectively enable or disable effects for a body.
3) An edge is a static volume-less body. Edges are never moved by the simulation and their mass doesn’t matter. Edges are used to represent negative space within a scene (such as a hollow spot inside another entity) or an uncrossable, invisibly thin boundary. For example, edges are frequently used to represent the boundaries of your scene.
The main difference between a edge and a volume is that an edge permits movement inside its own boundaries, while a volume is considered a solid object. If edges are moved through other means, they only interact with volumes, not with other edges.
Based on the above info, if you read the documentation for the method that you used bodyWithEdgeFromPoint:toPoint:, you will see that you are creating an "Edge-based" physics body.
Return Value
A new edge-based physics body.
Discussion
An edge has no volume or mass and is always treated as if the dynamic property is equal to NO. Edges may only collide with volume-based physics bodies.
To make your collision work, you have to make sure that your edge is colliding with a volume-based physics body. Every physics body shape creation method documents what type if shape it's creating.
If you are using a volume-based physics body that is colliding with your edge, then another possibility may be due to the size or speed of the involved objects. Again, reading Apple's docs makes it clear.
Specify High Precision Collisions for Small or Fast-Moving Objects:
When Sprite Kit performs collision detection, it first determines the locations of all of the physics bodies in the scene. Then it determines whether collisions or contacts occurred. This computational method is fast, but can sometimes result in missed collisions. A small body might move so fast that it completely passes through another physics body without ever having a frame of animation where the two touch each other.
If you have physics bodies that must collide, you can hint to Sprite Kit to use a more precise collision model to check for interactions. This model is more expensive, so it should be used sparingly. When either body uses precise collisions, multiple movement positions are contacted and tested to ensure that all contacts are detected
ship.physicsBody.usesPreciseCollisionDetection = YES;
Other possibilities might mess up collisions as well, such as wrong position info for the path that you're assigning to the physics body. It's important to understand that when you set a path to a node, that the shape is being set using the local coordinate system of the node. It's important to remember that the origin in Sprite Kit is located bottom left corner (not UIKit's top left corner), and when you assign a path to a physics body of a node, that the path is placed relative to the anchor point of the node.
For example:
SKShapeNode *ball = [[SKShapeNode alloc] init];
CGRect ballFrame = CGRectMake(-25.0, -25.0, 50.0, 50.0);
[ball setPath:[UIBezierPath bezierPathWithOvalInRect:ballFrame].CGPath];
[ball setPosition:CGPointMake(100.0, 450.0)];
[ball setFillColor:[UIColor redColor]];
[ball setPhysicsBody:[SKPhysicsBody bodyWithCircleOfRadius:25.0]];
If I had set the origin of "ballFrame" at (0,0), then the circle of the physics body with radius of 25.0, would not coincide with the shape of the ball, as the bottom left corner of the physics body will be placed at the anchor point of the ball, which is at points x = 0 and y = 0, in the local coordinate system of the ball.

Physics vs graphics fighting each other (box2d)

so I have a ball (sprite subclass) that can be dragged around the screen and updates it body to tell the world its position.
The problem is, when I apply a force to the ball while the ball is being touched (thus being controlled by a finger), I have two pieces of code fighting against each other to update the sprite's visual position.
The world stepper is trying to follow through the physics and update the ball's position. The touch methods however are also trying to update the ball's body and graphic position. Ideally, when touched I would like to kill off any physics affecting the ball.
So what exactly should I be trying to do to consolidate this? This is also creating issues for collisions and filtering. Any help appreciated. Thanks
Do you want the ball to have an accurate physics simulation? If not, you could always use the SetTransform method of the b2body class like this.
CGPoint ballLocation = ;//use the touched location for this
b2Vec2 vecBallLocation = b2Vec2(ballLocation.x / 32, ballLocation.y / 32);//calculate this on your own
b2Body * ballBody = ;//replace this variable with your ball's b2Body;
ballBody->SetTransform(vecBallLocation, ballBody->GetAngle());
Do not set the ball sprite's position manually. Leave this up to the physics simulation. Your code will get really messy over time if you have to keep track of what sprites you set the position of manually and which ones use the physics simulation to update their positions. This way is also much easier.
When the physics object is being dragged, set its velocity and angular velocity to 0,0 before running the physics step simulation. You may also want to temporarily get the physics object's position, then step, then set the body's position back, then apply whatever touch dragging offset you got from moving the finger to the body.