I have tried and tried to get collision detection to work the way I need but to no avail. What I want to accomplish is to get notified when the character touches an object but not stop it from moving on to the object. In GameScene, I have the physics of the object set like this:
Category Mask: 8
Collision Mask: 0
Field Mask: 0
Contact Mask: 0
I add the character to the scene in my code. I have a category structure like this:
struct PhysicsCategory {
static let None: UInt32 = 0
static let Player: UInt32 = 0b1 // 1
static let Pillar:UInt32 = 0b10// 2
static let Chest:UInt32 = 0b100// 4
static let Ladder:UInt32 = 0b1000
}
And the character physics is like this:
playerWalk.physicsBody?.categoryBitMask=PhysicsCategory.Player
playerWalk.physicsBody?.collisionBitMask=PhysicsCategory.Pillar
playerWalk.physicsBody?.contactTestBitMask=PhysicsCategory.Chest | PhysicsCategory.Ladder
playerWalk.physicsBody = SKPhysicsBody(rectangleOf:
playerWalk.frame.size)
For some reason the character stops at the ladder instead of moving on top of it. I can't figure out what I'm doing wrong.
Edit - You've set the properties of the playerWalk physics body before you've actually created the playerWalk.physicsBody. So the values for the categoryBitMask, collisionBitMask and contactTestBitMask don't get set because you've used optional chaining and so playerWalk.physicsBody? resolved to nil and nothing gets set.
Then, when you create the playerWalk physic body it just has the defaults properties i.e. no category, no contacts and collides with everything, so it is colliding with your ladder and thus cannot move over it.
Move the line:
playerWalk.physicsBody = SKPhysicsBody(rectangleOf: playerWalk.frame.size)
to before the lines where you set the physics body's properties.
Related
let dart = SKSpriteNode(imageNamed: "Dart")
dart.physicsBody?.isDynamic = true
dart.physicsBody?.categoryBitMask = 3
dart.physicsBody?.collisionBitMask = 2
when I print the darts category masks it returns nil. I have set other sprites category masks through the scene editor and they work fine. Thanks for any help.
You haven't actually made a physics body.
dart.physicsBody = SKPhysicsBody(...)
dart.physicsBody?.isDynamic = true
// etc.
The ... depends on the type of physics body you want (circle, rectangle, polygon, etc.) See examples here:
https://developer.apple.com/documentation/spritekit/sknode/getting_started_with_physics_bodies
I'm implementing a mass-spring system (many small physics bodies joined together with SKPhysicsJointSpring instances) with SpriteKit. Some of the particles would get snagged while traversing the center of the scene.
There seems to be a small, static body in the middle of the scene and I don't know why it's there.
Here's an easy way to see what I'm talking about:
In XCode 8, create a brand new project with the "Game" template.
In GameViewController.viewDidLoad(), add view.showsPhysics = true
If you run the project, you should see a little dot in the middle, which is the errant body:
Anyone know how to get rid of it?
Edit: I tried to manually create the scene object:
In GameViewController.viewDidLoad(), I replaced this:
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
view.presentScene(scene)
}
with this:
let scene = GameScene(size: view.frame.size)
scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
view.presentScene(scene)
but that didn't fix it.
Anyways, I decided to make an answer because comments are not suitable due to lot of info I want to share. Also my answer, sadly, doesn't answer the question but it gives some useful info about this unidentified, obviously capable of flying (physics body) object :)
So this is the code how to grab it (and modify it???):
//you can use self.frame here...I just copied Alessandro's code
self.physicsWorld.enumerateBodies(in:(label?.frame)!) { body, stop in
if let node = body.node {
print("Type of this node: \(type(of:node))")
print("Frame of this node: \(node.frame))")
}else{
print("This body's node property is nil")
body.affectedByGravity = true
body.isDynamic = true
body.applyImpulse(CGVector(dx: 0.003, dy: 0.0003))
}
print("Area covered by this node physicsBody: \(body.area)")
}
So if you put a break point inside of that else statement, you can scan this physics body completely and get all the info about it, like that its node property is set to nil or that its isDynamic property is set to false. But you can change that, and like in my code, set for example isDynamics to true. This makes it moveable. So if you apply some forces to it, it will move.
Still, like I said in comments, I don't have an idea why it is there and what it represents or what is its purpose.
Also, for those who are wondering how it is possible that one physics body doesn't have a node associated with it ( body.node equals nil) but is still visible on screen when showsPhysics is set to true, there is a reasonable explanation. Physics world is separated from the node tree. So we can remove a sprite from a node tree, but that doesn't mean that its physics body will be removed instantly. It may happen that physics engine haven't finished simulation... So you probably wonder, how this might happen?
Let say you have three SKSpriteNode objects intersecting at the same time (say A contacts B and A contacts C at the same time). SpriteKit can process only one contact at time. And say that you are removing A from a parent when it is contacting with B. Then, there is a contact between A and C also, so didBegin:(_ contact) will be called twice. And if you remove A from its parent in first didBegin(_ contact) call, in the next didBegin(_ contact) call, bodyA.node will be nil (bodyA is a physics body of sprite A), but its physics body will remain visible until engine finishes what needed. This is because node tree and a physics world are separated.
About the "hello world" xCode game template , it seems a little physicsBody associated to the GameScene node.
With some code I've found this:
class GameScene: SKScene {
private var label : SKLabelNode?
private var spinnyNode : SKShapeNode?
override func didMove(to view: SKView) {
...
// End part of this function:
if let b = physicsWorld.body(in: (label?.frame)!) {
if let node = b.node {
print("Type of this node: \(type(of:node))")
print("Frame of this node: \(node.frame))")
}
print("Area covered by this node physicsBody: \(b.area)")
}
}
}
With a breakpoint to the last brace, you can see two bodies (maybe an array of bodies), one of them is the physicsBody to the left debugArea (array with index 1) with the same hex address as my body b in my code : 0x79794f90, a little rectangle body with area 4.444
Printing description of ((PKPhysicsBody *)0x79794f90):
<SKPhysicsBody> type:<Rectangle> representedObject:[<SKScene> name:'(null)' frame:{{-375, -667}, {750, 1334}} anchor:{0.5, 0.5}]
(lldb)
I had a similar problem. I have a game with two sprite nodes joined together (SKPhysicsJointFixed.joint) moving around an SKEditor created scene.
As per my design, this node-pair would impact a third sprite node and be propelled smoothly away from the third sprite node, EXCEPT when the impact was in the center of the scene. For the center of the scene impact, the node-pair would compress together while be propelled away from the third sprite node, presenting a poor graphical image.
After significant time debugging my code, I found this post. Kudos for the explanations and code. I can’t answer the “why” question but for the “particles would get snagged while traversing the center of the scene” question my suggested solution is to clear the collisionBitMask instead of moving the body.
BTW categoryBitMask is 0 when loaded.
//moves the problem offscreen unless it hits another node
//body.affectedByGravity = true
//body.isDynamic = true
//body.applyImpulse(CGVector(dx: 0.003, dy: 0.0003))
//
// collisionBitMask loads with 4294967295 = default value to collide with all categories
//
body.collisionBitMask = 0
I have a spaceship object which has a complex geometry, and since SceneKit's physics doesn't work with complex bodies, I have adopted a workaround: I'm using some basic shapes like cylinders and cubes so simulate the whole spaceship's body. In Blender I created a set of objects that approximate the shape of the spaceship:
Then when I load the scene I remove these objects, but use their geometry to construct a SCNPhysicsShape to be used as the physics body of the spaceship:
// First I retrieve all of these bodies, which I named "Body1" up to 9:
let bodies = _scene.rootNode.childNodes(passingTest: { (node:SCNNode, stop:UnsafeMutablePointer<ObjCBool>) -> Bool in
if let name = node.name
{
return name.contains("Body")
}
return false
})
// Then I create an array of SCNPhysicsShape objects, and an array
// containing the transformation associated to each shape
var shapes = [SCNPhysicsShape]()
var transforms = [NSValue]()
for body in bodies
{
shapes.append(SCNPhysicsShape(geometry: body.geometry!, options: nil))
transforms.append(NSValue(scnMatrix4:body.transform))
// I remove it from the scene because it shouldn't be visible, as it has
// the sole goal is simulating the spaceship's physics
body.removeFromParentNode()
}
// Finally I create a SCNPhysicsShape that contains all of the shapes
let shape = SCNPhysicsShape(shapes: shapes, transforms: transforms)
let body = SCNPhysicsBody(type: .dynamic, shape: shape)
body.isAffectedByGravity = false
body.categoryBitMask = SpaceshipCategory
body.collisionBitMask = 0
body.contactTestBitMask = RockCategory
self.spaceship.physicsBody = body
The SCNPhysicsShape object should contain all the shapes that I created in the Blender file. But when I test the program, the spaceship just behaves like an empty body, and collisions are not detected.
PS: my goal is only to detect collisions. I don't want the physics engine to simulate physics.
You want to use a concave shape for your ship. Example below. In order to use concave your body must be static or kinematic. Kinematic is ideal for animated objects.
No need for:
body.collisionBitMask = 0
A contact bit mask is all you will need for contact only.
The code below should work for what you are looking for. This is swift 3 code fyi.
let body = SCNPhysicsBodyType.kinematic
let shape = SCNPhysicsShape(node: spaceship, options: [SCNPhysicsShape.Option.type: SCNPhysicsShape.ShapeType.concavePolyhedron])
spaceship.physicsBody = SCNPhysicsBody(type: body, shape: shape)
Without seeing your bit mask, I am unable to tell if you are handling those properly. You should have something like:
spaceship.physicsBody.categoryBitMask = 1 << 1
Anything you want to contact your spaceship should have:
otherObject.physicsBody.contactTestBitMask = 1 << 1
This will only register a contact of the "otherObject" to the "spaceship" and not the "spaceship" to the "otherObject". So make sure your physics world contact delegate is handled that way or vise versa. No need for both objects to look for each other. That would be less efficient.
Example of delegate below:
func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact) {
var tempOtherObject: SCNNode!
var tempSpaceship: SCNNode!
// This will assign the tempOtherObject and tempSpaceship to the proper contact nodes
if contact.nodeA.node == otherObject {
tempOtherObject = contact.nodeA
tempSpaceship = contact.nodeB
}else{
tempOtherObject = contact.nodeB
tempSpaceship = contact.nodeA
}
print("tempOtherObject = ", tempOtherObject)
print("tempSpaceship = ", tempSpaceship)
}
In my game I have a body that flies across the screen, and is supposed to be affected by a radial gravity field that I have created. To do this, I needed to turn the
body.dynamic = true
But this results in the body colliding with other objects (with physics bodies) that are on the screen, when I just want it to be affected by the field and not by these objects. If I turn the
body.dynamic = false
then it doesn't get affected by the field.
What should I do to make the body stay on its path?
EDIT This is my code for the main body that flies across the screen, after a node has already been created
body.physicsBody = SKPhysicsBody(rectangleOfSize: rectangle)
body.physicsBody?.dynamic = true
body.physicsBody?.categoryBitMask = PhysicsCategory.body
body.physicsBody?.contactTestBitMask = PhysicsCategory.otherBody
body.physicsBody?.affectedByGravity = false
body.physicsBody?.fieldBitMask = PhysicsCategory.shield
body.physicsBody?.allowsRotation = false
The image shows the 2 possible ways I want the body to avoid colliding with the object. Either it can pass through it, or it can pass around it and return to the earlier path. How do I do that?
You should set
body.physicsBody?.collisionBitMask = 0
collisionBitMask - A mask that defines which categories of physics bodies can collide with this physics body. IF the value is not set your PhysicsBody collides with every other PhysicsBodies, If value is set to 0 your PhysicsBody do not collide with other PhysicsBodies
I you set collisionBitMask = 0 your body node will go through all other nodes, if you whant it to collide you need to set node physicsBody?.collisionBitMask (for example if you whant your node to collide only with ball and wall physicsBody?.collisionBitMask = kCategoryWall | kCategoryBall)
I hope it was helpfull
I have a body with edgeLoopForRect and I'm giving it a categoryBitMask but when I'm debugging the object's categoryBitMask is different than what I assigned it...
self.physicsBody?.categoryBitMask = 1
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
when I'm debugging its showing up as 453453 not ``
You cannot assign physics bodies to self. you have to create a node first and then assign a category bit mask to its physics body.
IE:
let edge = SKNode()
edge.phisicsbody = SKPhisicsbody(edgeloopfromrect: frame)
edge.physicsbody!.restitution = 0
edge.physicsbody!.friction = 0
edge.physicsbody!.angularDamping = 1
edge.physicsbody!.dynamic = false
edge.physicsbody!.categotyBitMask = 2
edge.physicsbody!.collisionBitMask = 1 //The category bit mask of the object you want it to collide with I used 1 as example
addChild(edge)
Remember to follow the same procedure for the node that you want to collide with the edge and make the collision bit mask 2 if you follow this example.
Hope this helps mate.