As far as I'm aware the default for Physics bodies is to bounce off each other when they hit each other until you set their collisionBitMask to an equal number.
However, I'm having a huge issue accomplishing what seems like it should be very simple because of collisionBitmasks I believe.
let RedBallCategory : UInt32 = 0x1 << 1
let GreenBallCategory: UInt32 = 0x1 << 2
let RedBarCategory : UInt32 = 0x1 << 3
let GreenBarCategory : UInt32 = 0x1 << 4
let WallCategory : UInt32 = 0x1 << 5
greenBall.physicsBody?.categoryBitMask = GreenBallCategory
greenBall.physicsBody?.contactTestBitMask = RedBarCategory
greenBall.physicsBody?.collisionBitMask = GreenHealthCategory
redBall.physicsBody?.categoryBitMask = RedBallCategory
redBall.physicsBody?.contactTestBitMask = GreenBarCategory
redBall.physicsBody?.collisionBitMask = RedHealthCategory
let borderBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
self.physicsBody = borderBody
self.physicsBody?.friction = 0
borderBody.contactTestBitMask = RedBallCategory | GreenBallCategory
borderBody.categoryBitMask = WallCategory
So here I've got my 2 balls and my border body. I can get collision detection which I want, but when I add the border body's category bit mask, it allows the balls to both go through and off the screen, which I don't want.
I also want the balls to bounce off each other, but only when I comment out one of the ball's categoryBitMasks do they bounce. Otherwise they go through each other.
That makes absolutely no sense to me because each of these items has a different collisionbitmask. I also had it sometimes where setting all the numbers equal to 5 would allow everything to pass through each other, but then setting it all to 6 would allow everything to hit each other.
How exactly do collision bitmasks work and is there a proper way to manage a lot of crisscrossing collision rules?
You can't get desired behaviour because you haven't set category, contact and collision bit masks properly. Here is an example on how you can set this to work:
greenBall.physicsBody?.categoryBitMask = GreenBallCategory //Category is GreenBall
greenBall.physicsBody?.contactTestBitMask = RedBarCategory | WallCategory //Contact will be detected when GreenBall make a contact with RedBar or a Wall (assuming that redBar's masks are already properly set)
greenBall.physicsBody?.collisionBitMask = GreenBallCategory | RedBallCategory | WallCategory //Collision will occur when GreenBall hits GreenBall, RedBall or hits a Wall
redBall.physicsBody?.categoryBitMask = RedBallCategory //Category is RedBall
redBall.physicsBody?.contactTestBitMask = GreenBarCategory | GreenBallCategory | WallCategory //Contact will be detected when RedBall make a contact with GreenBar , GreenBall or a Wall
redBall.physicsBody?.collisionBitMask = RedBallCategory | GreenBallCategory | WallCategory //Collision will occur when RedBall meets RedBall, GreenBall or hits a Wall
let borderBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
self.physicsBody = borderBody
self.physicsBody?.friction = 0
borderBody.contactTestBitMask = RedBallCategory | GreenBallCategory //Contact will be detected when red or green ball hit the wall
borderBody.categoryBitMask = WallCategory
borderBody.collisionBitMask = RedBallCategory | GreenBallCategory // Collisions between RedBall GreenBall and a Wall will be detected
I would recommend you to read docs about categoryBitMask which is a mask that defines which categories a physics body belongs to:
Every physics body in a scene can be assigned to up to 32 different
categories, each corresponding to a bit in the bit mask. You define
the mask values used in your game. In conjunction with the
collisionBitMask and contactTestBitMask properties, you define which
physics bodies interact with each other and when your game is notified
of these interactions.
contactTestBitMask - A mask that defines which categories of bodies cause intersection notifications with a current physics body.
When two bodies share the same space, each body’s category mask is
tested against the other body’s contact mask by performing a logical
AND operation. If either comparison results in a nonzero value, an
SKPhysicsContact object is created and passed to the physics world’s
delegate. For best performance, only set bits in the contacts mask for
interactions you are interested in.
collisionBitmask - A mask that defines which categories of physics bodies can collide with this physics body.
When two physics bodies contact each other, a collision may occur.
This body’s collision mask is compared to the other body’s category
mask by performing a logical AND operation. If the result is a nonzero
value, this body is affected by the collision. Each body independently
chooses whether it wants to be affected by the other body. For
example, you might use this to avoid collision calculations that would
make negligible changes to a body’s velocity.
So basically, to set up all these, you should ask your self something like:
Okay, I have a green ball, and a red ball , and wall objects on the scene. Between which bodies I want collisions to occur, or when I want to register contacts? I want a green and a red ball to collide with each other and to collide against the walls. Not a problem. I will first set up categories properly, and then I will set collision bit masks like this:
greenBall.collisionBitMask = GreenBallCategory | RedBallCategory | WallCategory; //greenBall will collide with greenBall, redBall and a wall
redBall.collisionBitMask = GreenBallCategory | RedBallCategory | WallCategory
wall.collisionBitMask = GreenBall | RedBall
Now, I want to detect when some contacts occur (in didBeginContact: method)... But I don't want to get notified about all possible contacts, but rather to be notified just about contacts between balls (contacts between balls and a wall will be ignored). So lets set contactTestBitMasks to achieve this:
greenBall.contactTestBitMask = GreenBallCategory | RedBallCategory;
redBall.contactTestBitMask = GreenBallCategory | RedBallCategory;
And that's it. The important thing is when you not using contact detection, you should not set contactTestBitMask. This is because of performance reasons. If you don't need collision detection, and you are interested only in detecting contacts, you can set collisionBitMask = 0.
Important:
Make sure you have set physics world's contact delegate in order to use didBeginContact and didEndContact methods:
self.physicsWorld.contactDelegate = self; //where self is a current scene
Hope this helps a bit.
Just want to add something here, that if the contact or collision are not able to work properly, you need to make sure that you assign the physicsBody shape first before applying the bitMasks.
Example of not working bitmask:
SKSpriteNode *bird = [SKSpriteNode spriteNodeWithImageNamed:#"bird1.png"];
bird.name = #"bird";
bird.physicsBody.categoryBitMask = birdCategory;
bird.physicsBody.contactTestBitMask = wallCategory | scoreCategory;
bird.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:bird.size.width/2.7];
bird.position = CGPointMake(_mySize.width/3.0, 2*_mySize.height/3.0);
[self addChild:bird];
And working one:
SKSpriteNode *bird = [SKSpriteNode spriteNodeWithImageNamed:#"bird1.png"];
bird.name = #"bird";
bird.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:bird.size.width/2.7];
bird.physicsBody.categoryBitMask = birdCategory;
bird.physicsBody.contactTestBitMask = wallCategory | scoreCategory;
bird.position = CGPointMake(_mySize.width/3.0, 2*_mySize.height/3.0);
[self addChild:bird];
Anyway probably just a newbie mistake, but I'd share here cause this is what caused mine not to work properly.
Related
I'm trying to create an arcade game, where a ball moves at a constant speed and is unaffected by gravity or friction. So I created the ball as an SKShapeNode and set its linearDamping and friction to 0. I also set the game scene to have no gravity. But when playing, if the ball hits another shape node (a circle) at a low angle, it can slow down. The ball's restitution is 1, and allowsRotation is false.
I am keeping the ball moving by applying one impulse at the beginning of the game, that is a random direction.
This might not be the most ideal fix but you could set the fixed speed of the object every update to a specific value which is your constant speed.
The other alternative way to solve this would be to set the fixed speed of the object under the collision delegate functions.
I had a similar problem that was resolved by setting physicsBody?.isDynamic = false on the node that the ball makes contact with.
For example if you have a ball and a brick:
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.frame.width / 2) // (diameter / 2) = radius
ball.physicsBody?.categoryBitMask = ballCategoryBitMask
// Detect contact with the bottom of the screen or a brick
//
ball.physicsBody?.contactTestBitMask = bottomCategoryBitMask | brickCategoryBitMask
ball.physicsBody?.friction = 0
ball.physicsBody?.allowsRotation = false
ball.physicsBody?.linearDamping = 0
ball.physicsBody?.restitution = 1
ball.physicsBody?.applyImpulse(CGVector(dx: 10, dy: -10))
brick.physicsBody = SKPhysicsBody(rectangleOf: brick.frame.size)
brick.physicsBody?.linearDamping = 0
brick.physicsBody?.allowsRotation = false
brick.physicsBody?.isDynamic = false // Prevents the ball slowing down when it gets hit
brick.physicsBody?.affectedByGravity = false
brick.physicsBody?.friction = 0.0
I am trying to make a simple maze game in Scenekit. I have a sphere node and wall nodes. The sphere moves by SCNActions. The actions I use on the sphere are moveByX actions. My problem is that whenever the sphere hits a wall, it just moves the wall back with it. My sphere is kinematic and my walls are dynamic. I don't have any forces in the scene, only my moveByX actions. How can I make it so my sphere bounces off of the walls?
I am using Swift.
Change walls to .static and sphere to .dynamic
One way you could do it would be to have variables for xSpeed and ySpeed, and whenever you collide with a wall make the corresponding variable negative. This is a pretty basic approach and doesn't take diagonal walls into account.
For example:
var xSpeed = 5.0 var ySpeed = 5.0
if collision with wall in x direction { xSpeed = -xSpeed }
if collision with wall in y direction { ySpeed = -ySpeed }
It might be a bit tricky to detect the collisions, you might have to look up some tutorials on SpriteKit collisions.
For Swift3
let border = SKPhysicsBody(edgeLoopFrom: self.frame)
border.friction = 0
border.restitution = 1
self.physicsBody = border
In SceneKit, it is possible to make a SCNPhysicsBody be unaffected by a SCNPhysicsField by giving them categoryBitMasks that when compared using bitwise AND, result in a value of zero.
Bodies who's category bit mask produce a non zero value when compared with that of the field are still affected by it.
Is it possible to do the same for the particles in a SCNParticleSystem, so that the particles will be unaffected by a SCNPhysicsField (edit: and still have the particles affected by other physics fields), but certain physics bodies or even other particle systems are affected by it?
To clarify:
What I want: A SCNParticleSystem that is affected by a SCNPhysicsField (specifically a linear gravity field). This can be done by setting the particle system's affectedByPhysicsFields property to true. However, this will make the particle system affected by all physics fields in the scene. I have another field (also a linear gravity field) that I want to only affect a SCNPhysicsBody which I don't want to be affected by the particle systems's field.
As it stands both the particle system and the physics body will be affected by both fields. Unless I give them categoryBitMasks to tell them which fields to interact with. Except particle systems don't have categoryBitMasks, and if a physics field's categoryBitMask is anything other than the default, it will be ignored by any particles.
So I can set it so that one of the fields only affects the body by giving them the same category. But I can't do the same for the particles. Is there any way to accomplish this? (Using the world gravity as one of the fields doesn't work because it doesn't have a categoryBitMask either.)
I've actually figured out a workaround (for now) by just using the particle system's acceleration property instead of a physics field. But I curious to see if it actually is possible to do it using a physics field.
edit: As requested here is some sample code. This code results in a sphere affected by two physics fields, and a particle system affected by one physics field. The comments explain the problem.
let PhysicsCategorySphere = 1 << 0
let PhysicsCategoryParticles = 1 << 1
scene.physicsWorld.gravity = SCNVector3(x: 0, y: 0, z: 0)
let sphere = SCNNode(geometry: SCNSphere(radius: 1))
sphere.physicsBody = SCNPhysicsBody.dynamicBody()
sphere.physicsBody?.categoryBitMask = PhysicsCategorySphere
//only physics fields with categoryBitMasks that match the sphere's can affect it
scene.rootNode.addChildNode(sphere)
let field = SCNPhysicsField.linearGravityField()
field.strength = 9.8
field.direction = SCNVector3(x: 0, y: 1, z: 0)
field.categoryBitMask = PhysicsCategorySphere
//only things with categories that match the field's will be affected by it
let fieldNode = SCNNode()
fieldNode.physicsField = field
scene.rootNode.addChildNode(fieldNode)
let particleSystem = SCNParticleSystem(named: "particles", inDirectory: "")
particleSystem.affectedByPhysicsFields = true
//there is no way that I know of to give the particle system a physics category
let particleNode = SCNNode()
particleNode.addParticleSystem(particleSystem)
scene.rootNode.addChildNode(particleNode)
let particleField = SCNPhysicsField.linearGravityField()
particleField.strength = 20
particleField.direction = SCNVector3(x: 0, y: 0, z: 1)
//particleField.categoryBitMask = PhysicsCategoryParticles
//if the field is given a category, it will no longer affect the particles
//the default is to match all categories, thus the particle field will also affect the sphere
let particleFieldNode = SCNNode()
particleFieldNode.physicsField = particleField
scene.rootNode.addChildNode(particleFieldNode)
Ok, I don't know how I overlooked this, but the answer is right in the documentation for SCNPhysicsField's categoryBitMask property.
To determine whether a field affects the particles spawned by an
SCNParticleSystem object, SceneKit performs the same check using the
categoryBitMask property of the node containing the particle system.
The documentation for SceneKit's collisionBitMask property of SCNPhysicsBody states the following:
When two physics bodies contact each other, a collision may occur.
SceneKit compares the body’s collision mask to the other body’s
category mask by performing a bitwise AND operation. If the result is
a nonzero value, then the body is affected by the collision. Each body
independently chooses whether it wants to be affected by the other
body.
That last line indicates that if I have two objects, I can set it up so that when they collide, only one of them should be affected by the collision.
let CollisionCategoryPlane = 1 << 0
let CollisionCategorySphere1 = 1 << 1
let CollisionCategorySphere2 = 1 << 2
let plane = SCNNode(geometry: SCNPlane(width: 10, height: 10))
plane.position = SCNVector3(x: 0, y: -10, z: 0)
plane.eulerAngles = SCNVector3(x: Float(-M_PI/2), y: 0, z: 0)
plane.physicsBody = SCNPhysicsBody.staticBody()
plane.physicsBody?.categoryBitMask = CollisionCategoryPlane
plane.physicsBody?.collisionBitMask = CollisionCategorySphere1 | CollisionCategorySphere2
// the plane should be affected by collisions with both spheres (but the plane is static so it doesn't matter)
scene.rootNode.addChildNode(plane)
let sphere1 = SCNNode(geometry: SCNSphere(radius: 1))
sphere1.physicsBody = SCNPhysicsBody.dynamicBody()
sphere1.physicsBody?.categoryBitMask = CollisionCategorySphere1
sphere1.physicsBody?.collisionBitMask = CollisionCategoryPlane
// sphere1 should only be affected by collisions with the plane, not with sphere2
scene.rootNode.addChildNode(sphere1)
let sphere2 = SCNNode(geometry: SCNSphere(radius: 1))
sphere2.position = SCNVector3(x: 1, y: 10, z: 0)
sphere2.physicsBody = SCNPhysicsBody.dynamicBody()
sphere2.physicsBody?.categoryBitMask = CollisionCategorySphere2
sphere2.physicsBody?.collisionBitMask = CollisionCategoryPlane | CollisionCategorySphere1
// sphere2 should be affected by collisions with the plane and sphere1
scene.rootNode.addChildNode(sphere2)
Sphere1 should fall onto the plane, then sphere2 should fall onto sphere1 and bounce off, and sphere1 should be unaffected by the collision with sphere2. However, the observed behaviour is both spheres falling onto the plane and coming to rest inside each other - no collision event between the two spheres is registered.
What is going on here?
On related notes, some even stranger behaviour is observed when I make a couple small modifications to the above code.
If remove the line that defines the plane's collsionBitMask, leaving it as the default SCNPhysicsCollisionCategoryAll, sphere1 no longer collides with the plane.
If I move the lines that define the objects' physics bodies, categoryBitMasks, and collisionBiMasks to after the objects have each been added to the the scene, all the objects will collide with every other object. Even if I set every collisionBitMask to zero.
I have two phyicsbodies in my spritekit game which are colliding and I am having a little difficulty getting them to stop.
I am posting the physics code for them below.
Problem is when the swarm touches my coins it pushes it, not exactly sure why since as far as my know my enemy is told to only contact with the player and my coin is told to only contact with the wall and the player (correct me if im wrong)
(P.S I commented out the collisionBitMask for the coin because when I dont my coins fall thru my walls )
Thanks
1St sprite
enemy = [SKSpriteNode spriteNodeWithImageNamed:#"Swarm"];
enemy.physicsBody =
[SKPhysicsBody bodyWithRectangleOfSize:enemy.size];
enemy.physicsBody.dynamic=NO;
enemy.name=#"Eagle";
enemy.physicsBody.categoryBitMask = PCFallersCategory;
// enemy.physicsBody.collisionBitMask =
// PCPlayerCategory;
enemy.physicsBody.contactTestBitMask = PCPlayerCategory;
enemy.physicsBody.restitution=0;
enemy.physicsBody.friction=0;
2nd Sprite.
self.name = #"coin";
CGFloat minDiam = MIN(self.size.width, self.size.height);
minDiam = MAX(minDiam-8, 8);
self.physicsBody =
[SKPhysicsBody bodyWithCircleOfRadius:minDiam/2.0];
self.physicsBody.dynamic=YES;
self.physicsBody.restitution =0;
self.physicsBody.friction = 0;
self.physicsBody.linearDamping = 0;
self.physicsBody.categoryBitMask = PCCollectableCategotry;
//self.physicsBody.collisionBitMask =PCPlayerCategory;
self.physicsBody.contactTestBitMask = PCPlayerCategory|PCWallCategory;
You need to explicitly set the collisionBitMask to 0.
enemy.physicsBody.collisionBitMask = 0;
According to the documentation
When two physics bodies contact each other, a collision may occur.
This body’s collision mask is compared to the other body’s category
mask by performing a logical AND operation. If the result is a
non-zero value, then this body is affected by the collision. Each body
independently chooses whether it wants to be affected by the other
body. For example, you might use this to avoid collision calculations
that would make negligible changes to a body’s velocity.
The default value is 0xFFFFFFFF (all bits set).
This means that by default, an SKPhysicsBody is configured to collide with all objects.