Edit: I have been able to solve this problem by using PhysicsEditor to make a polygonal physicsbody instead of using SKPhysicsBody(... alphaThreshold: ... )
--
For some reason I'm having trouble with what I'm assuming is SKPhysicBodies being slightly off-place. While using showPhysics my stationary obstacle nodes appear to have their physicbodies in the correct position, however I am able to trigger collisions without actually touching the obstacle. If you look at the image below it shows where I have found the physicsbodies to be off centre, despite showPhysics telling me otherwise. (Note, the player node travels in the middle of these obstacle nodes).
I also thought it would be worth noting that while the player is travelling, its physicbody appears to travel slightly ahead but I figured this is probably normal.
I also use SKPhysicsBody(... alphaThreshold: ... ) to create the physicbodies from .png images.
Cheers.
Edit: Here's how I create the obstacle physicbodies. Once they're added into the worldNode they are left alone until they need to be removed. Apart from that I don't change them in any way.
let obstacleNode = SKSpriteNode(imageNamed: ... )
obstacleNode.position = CGPoint(x: ..., y: ...)
obstacleNode.name = "obstacle"
obstacleNode.physicsBody = SKPhysicsBody(texture: obstacleNode.texture!, alphaThreshold: 0.1, size: CGSize(width: obstacleNode.texture!.size().width, height: obstacleNode.texture!.size().height))
obstacleNode.physicsBody?.affectedByGravity = false
obstacleNode.physicsBody?.isDynamic = false
obstacleNode.physicsBody!.categoryBitMask = CC.wall.rawValue
obstacleNode.physicsBody!.collisionBitMask = CC.player.rawValue
obstacleNode.physicsBody!.contactTestBitMask = CC.player.rawValue
worldNode.addChild(obstacleNode)
The player node is treated the same way, here is how the player moves.
playerNode.physicsBody?.velocity = CGVector(dx: dx, dy: dy)
I'm assuming you aren't showing the exact images that you used to create your SKSpriteNode and SKPhysicsBody instances. Since you are using a texture to define the shape of your SKPhysicsBody you are likely running up against this:
SKPhysicsBody documentation
If you do not want to create your own shapes, you can use SpriteKit to create a shape for you based on the sprite’s texture.
This is easy and convenient but it can sometimes give unexpected results depending on the textures you are using for your sprite. Perhaps try making an explicit mask or using a simple shape to represent your physics body. There are very good examples and guidelines in that documentation.
I would also follow this pattern when you set the properties on your objects:
// safely unwrap and handle failure if it fails
guard let texture = obstacleNode.texture else { return }
// create the physics body
let physicsBody = SKPhysicsBody(texture: texture,
alphaThreshold: 0.1,
size: CGSize(width: texture.size().width,
height: texture.size().height))
// safely set its properties without the need to unwrap an Optional
physicsBody.affectedByGravity = false
// set the rest of the properties
// set the physics body property on the node
obstacleNode.physicsBody = physicsBody
By setting the properties on a concrete instance of SKPhysicsBody and fully unwrapping and testing Optionals you minimize the chances for a run-time crash that may be difficult to debug.
Related
I have 10 SKSpriteNodes that are connected via SKPhysicsJointPins. Together, they form a chain (each node is a chain-link image and each node has a physicsBody).
There are also some balls that bounce around and interact with the chain.
Desired behavior: The balls should never penetrate the chain.
Actual behavior: The chain-link joints allow too much leeway, and the balls go through the chain. To be clear, I'm not talking about tunneling -- the balls don't go through the nodes, themselves. Instead, the chain-nodes sort of move aside and the balls go through the opening.
The code to create the chain-links (please assume that all variables are defined and that the positioning of each node is correct. Hopefully you can see what's going on here):
let chains = mainData.getLevelChains()
let bodyA = SKSpriteNode(imageNamed: "chainLink.png")
bodyA.size = CGSize(width: chainLinkWidth, height: chainLinkHeight)
bodyA.position = CGPoint(x:chains[i].leftAnchorPosition.x + CGFloat(halfChainLinkWidth) + (chainWidth * (0.1 * CGFloat(j))), y: chains[i].leftAnchorPosition.y)
bodyA.name = "body"
bodyA.physicsBody = SKPhysicsBody(texture: bodyA.texture!, size: CGSize(width: chainLinkWidth, height: chainLinkHeight))
bodyA.physicsBody?.categoryBitMask = CollisionTypes.chain.rawValue
bodyA.physicsBody?.collisionBitMask = CollisionTypes.ball.rawValue
addChild(bodyA)
let joint = SKPhysicsJointPin.joint(withBodyA: previousChainLink.physicsBody!, bodyB: bodyA.physicsBody!, anchor: CGPoint(x: Double(previousChainLink.position.x)+halfChainLinkWidth, y: Double(previousChainLink.position.y)))
scene?.physicsWorld.add(joint)
Question: What can I do to ensure that the chain maintains its structural integrity so the balls never go through the chain?
In the end, I solved the problem by setting the mass of the chain-links to a value slightly greater than that of the balls.
ball.physicsBody?.mass = 5.0
chainLink.physicsBody?.mass = 6.0
Still new, but slowly building my first app/game, and am slowly getting there.
I'd like to be able to add a fading streak or trail as one of my SKSpriteNodes moves. Whether it is moving due to touchesMoved or being sent back to its original spot by code. I just want to add a nice visual effect.
The only thing I can think of is calculating the distance, breaking it down into x movements, then gradually move the Sprite a little, with some fade options, and then repeat in a loop till it gets back to home base, using a lot of nested SKActions and sequences.
I know that has to be wrong because it's just so ugly.
When I look at the Sprite Kit's Particle File, it has so few options. And I'm not really sure that's what I should be looking at. I have also looked at SKAction's options, and if there's an option there I'm missing it.
Surely in Swift's huge animation library there has to be something?
Let's create a basic sprite and an emitter, and make the emitter a child of the sprite so that it follows it:
let sprite = SKSpriteNode(color: .white, size: CGSize(width: 20, height: 10))
let emitter = SKEmitterNode() // better to use the visual designer in Xcode...
emitter.particleLifetime = 1.0
emitter.particleBirthRate = 50.0
emitter.particleSpeed = 100.0
emitter.emissionAngleRange = .pi / 5
emitter.particleTexture = SKTexture(imageNamed: "spark")
emitter.particleScale = 0.1
emitter.particleAlphaSpeed = -1.0
emitter.emissionAngle = -.pi
sprite.addChild(emitter) // attach it to the sprite
emitter.position.x = -15 // but not to the center
scene.addChild(sprite)
sprite.run(SKAction.group([ // let's run some actions to test it:
SKAction.sequence([
SKAction.move(to: CGPoint(x: 200, y: 200), duration: 5),
SKAction.move(to: CGPoint(x: 50, y: 50), duration: 5),
]),
SKAction.rotate(byAngle: .pi * 2.0, duration: 10)
]))
(Click to open animated GIF if it doesn't display correctly:)
To the casual observer, it looks fine… Except that, after some scrutiny, you'll realize what's off: the particles emitted live in the universe of the parent sprite, moving and rotating with it, even long after they were emitted! That's not right!
That's because the targetNode of the emitter is its parent, and it should be the scene!
So let's insert this line somewhere:
emitter.targetNode = scene // otherwise the particles would keep moving with the sprite
(Click to open animated GIF if it doesn't display correctly:)
Okay, this is a no-go: the particles now stay in the "universe" of the scene, but apparently their emission angle fails to follow that of the parent (which looks like a bug to me).
Luckily, we can attach a custom action to the emitter which keeps aligning this angle to the parent sprite:
emitter.run(SKAction.repeatForever(
SKAction.customAction(withDuration: 1) {
($0 as! SKEmitterNode).emissionAngle = sprite.zRotation + .pi
_ = $1
}))
(Click to open animated GIF if it doesn't display correctly:)
Okay, now new particles are launched in the correct direction, and keep moving that way even if the sprite moves or rotates in the meantime. This seems to be the most realistic simulation so far, though there may still be ways to improve it by modifying the behavior of the emitter on the fly.
Sorry for the jaggy GIFs, apparently my Mac is too slow to render and capture at the same time. The animations themselves run just fine.
I have a SceneKit game in swift and in it I have a car with a dynamic physics body that is set up like this:
let carScene = SCNScene(named: "art.scnassets/truck.scn")!
let carNode = carScene.rootNode.childNode(withName: "Cube", recursively: true)!
let carPhysicsBody = SCNPhysicsBody(type: .dynamic,shape: SCNPhysicsShape(geometry: SCNBox(width: 5.343, height: 12.125, length: 4.373, chamferRadius: 0)))
carPhysicsBody.mass = 3
carPhysicsBody.friction = 2
carPhysicsBody.contactTestBitMask = 1
carNode.physicsBody = carPhysicsBody
carNode.position = SCNVector3(x: 0, y: 0, z: 5)
carNode.physicsBody?.applyForce(SCNVector3(x: 0, y: 50, z: 0), asImpulse: true)
car = carNode
floorScene.rootNode.addChildNode(car)
The floor's physics looks like this:
As you can see the car gets launched into the air. Then the gravity in the scene makes it fall, and instead of colliding with the floor, it goes right through it.
The gravity looks like this:
What should I change so it will collide with the floor?
I've made a sample macOS playground, available at github.com/heckj/scenekit-physics-playground that shows a physics body interacting. I suspect the core of the problem in your local example is that the floor object doesn't actually have an SCNPhysicsBody associated with it. Until I explicitly set it on SCNFloor (as static), the text blob "fell through" as you originally described.
I recommend adding sceneView.debugOptions = [.showPhysicsShapes] to see the relevant debugging shapes. I've made (and pushed) a few updates to the above repo. Using stock SCNFloor to establish geometry for the physics collection made a tiny target (which is why the slight horizontal impulse made it appear to "pass through"). This last update sets the floor geometry to a long, wide, thin box.
You want to set the floor to be of the type kinematic, not static. With the car being dynamic and the floor being static, the floor isn't interacting with other objects in any fashion, and you explicitly want a collision.
UPDATE: static vs. kinematic doesn't make a difference for the collision interaction, either work effectively the same way, but having a physics body, and verifying it's size or viewing the interaction with .showPhysicsShapes may answer the underlying question of why they're not interacting.
One of posible solutions for u is to set collisionMargin property for your car from 0.0 to probably 0.01, I had the same problem for ball and plane.
I am testing out the features of SpriteKit and I ran into a problem. I was reading into bit masks, colliding, category, and contact. I get what they are, mostly at least, I don't get the point of category bitmasks, but I get colliding bitmasks which are the ones I need to solve my problem.
Ok so my problem is I have two different types of sprites: object and second. The names don't really make much sense but it is just for the sake of testing. I want second to have an impulse, and I want object to have a force. I was able to apply the respective vectors on the sprites, but I do not want them to collide with each other. I want them to pass right through and ignore the existence of each other.
I tried to solve that issue by assigning different collision bitmasks to each other:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let texture = SKTexture(imageNamed: "pokeball")
let object = SKSpriteNode(texture: texture)
object.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: object.size.width,height: object.size.height))
object.physicsBody?.affectedByGravity = false
object.yScale = 0.5
object.xScale = 0.5
for t in touches {
object.position = t.location(in: self)
}
self.addChild(object)
object.physicsBody?.collisionBitMask = UInt32(4)
object.physicsBody?.applyForce(CGVector(dx: 0, dy: 10))
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let texture = SKTexture(imageNamed: "purple")
let second = SKSpriteNode(texture: texture)
let impulse : Double = 20
let x = (impulse * Double(cosf(45)))
let y = Double(impulse * Double(sinf(45)))
let vector = CGVector(dx: x, dy: y)
second.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: second.size.width,height: second.size.height))
second.yScale = 1.5
second.xScale = 1.5
second.physicsBody?.isDynamic = true
for t in touches {
second.position = t.location(in: self)
}
self.addChild(second)
second.physicsBody?.collisionBitMask = UInt32(1)
second.physicsBody?.applyImpulse(vector)
}
So object has a bitmask of 4:
object.physicsBody?.collisionBitMask = UInt32(4)
And second has a bitmask of 1:
second.physicsBody?.collisionBitMask = UInt32(1)
I ran the simulator and they are still colliding with each other, so I went online and tried to look for some answers: I found one that says I must use numbers like:
these are bitmasks, you can't use arbitrary numbers 1,2,3,4,5 - you must use 1,2,4,8,16 and so on –
Can someone explain why? However, that wasn't the issue because I was using 1 and 4
Next question I ran into said that I had to use binary numbers (0100) and (0010), I tried them, same issue: still colliding.
I will leave a picture of the collisions:
Collisions
Does anyone know why this is happening? My apologies in advance if this is a really dumb mistake or something that has already been asked, I just couldn't find it.
There is a lot of documentation on these topics, but here is a practical example.
The power of categoryBitMasks
Pretend you have a collection of three nodes pool, basketball and bowlingball. Now, obviously, we want the basketball and bowlingball to collide with the each other. So you set the collisionBitMasks like so:
basketball.physicsBody?.collisionBitMask = UInt32(2)
bowlingball.physicsBody?.collisionBitMask = UInt32(2)
Great. Now, we want the bowlingball to sink to the bottom of the pool, and the basketball to collide with the pool (might be more of a splash, but bear with me). How would we do this? We could try:
pool.physicsBody?.collisionBitMask = UInt32(2) // ?
But wait, that would make the basketball AND the bowlingball collide with the pool. We only want the basketball to collide with the pool , whereas we want the bowlingball to ignore the pool and sink straight to the bottom with no collisions. This is where categoryBitMasks come in handy:
let basketballBitMask = UInt32(1)
let bowlingballBitMask = UInt32(2)
let poolBitMask = UInt32(4) // Why 4? See next section
basketball.physicsBody?.categoryBitMask = basketballBitMask
bowlingball.physicsBody?.categoryBitMask = bowlingballBitMask
pool.physicsBody?.categoryBitMask = poolBitMask
Because each object has a unique number assigned to it, you can now pick and choose which objects you'd like another object to collide with:
// basketball physics body collides with bowlingball(2) OR pool(4)
basketball.physicsBody?.collisionBitMask = bowlingballBitMask | poolBitMask
// ( '|' = logical OR operator)
// bowlingball physics body only collides with basketball(1)
bowlingball.physicsBody?.collisionBitMask = basketballBitMask
// pool physics body only collides with basketball(1)
pool.physicsBody?.collisionBitMask = basketballBitmask
If you're not sure what the strange '|' symbol is doing, I highly recommend the swift documentation on advanced operators to help you understand what's happening here.
Why not just use collisionBitMasks?
Okay so we've set some bit masks. But how are they used? If we only have two objects why can't we just compare collisionBitMasks?
Simply put, that's just not how it works. When a bowlingball comes into contact with the pool, the SpriteKit physics engine will AND ('&') together the bowlingball's categoryBitMask with the pool's collisionBitMask (or vice versa; the result is the same):
objectsShouldCollide = (bowlingball.physicsBody?.categoryBitMask &
pool.physicsBody?.collisionBitMask)
// objectsShouldCollide = (ob010 & 0b100) = 0b000
Because the bowlingball's categoryBitMask and the pool's collisionBitMask have zero bits in common, objectsShouldCollide is equal to zero, and SpriteKit will stop the objects from colliding.
But, in your case, you're not setting your objects' categoryBitMasks. So they have a default value of 2^32, or 0xFFFFFFFF (hexadecimal representation) or in binary, 0b11111111111111111111111111111111. So when an "object" hits a "second" object, SpriteKit does this:
objectsShouldCollide = (0b11111111111111111111111111111111 & // Default categoryBitMask for "object"
0b00000000000000000000000000000001) // collisionBitMask for "second" object
// = 0b00000000000000000000000000000001
So when you haven't defined the object's categoryBitMask, no matter what you set as the second object's collisionBitMask, objectsShouldCollide will never be zero, and they will always collide.
Note: you could set an object's collisionBitMask to 0; but then that object would never be able to collide with anything.
Using powers of 2 (0,1,2,4,8, etc.) for categoryBitMasks
Now let's say we wanted to include multiple bowlingballs that collided with each other. Easy:
bowlingball.physicsBody?.collisionBitMask = basketballBitMask | bowlingballBitMask
// bowlingball collision bit mask (in binary) = 0b10 | 0b01 = 0b11
// bowlingball collision bit mask (in decimal) = 2 | 1 = 3
Here you can see that if we had set the pools physicsCategory to UInt32(3), it would no longer be distinguishable from a bowlingball or basketball.
Further suggestions
Learn to name variables with purpose, even if you're just using them for testing (although, coincidentally, "object and second object" worked quite well).
Use a struct for bitmasks to simplify your code and improve readability:
struct PhysicsCategory {
static let Obj1 : UInt32 = 0b1 << 0
static let Obj2 : UInt32 = 0b1 << 1
static let Obj3 : UInt32 = 0b1 << 2
static let Obj4 : UInt32 = 0b1 << 3
}
obj1.physicsBody?.categoryBitmask = PhysicsCategory.Obj1 // etc
Why don't you download and play with this simple Sprite-Kit project? It creates various geometric shapes, sets some of them to collide, some to contact, uses a checkPhysics() function to show what's going to happen and then lets you flick the shapes around.
Attack button in SpriteKit
Any questions as to it's workings and I'll be more than happy to try and explain.
I currently have two SKSpriteNodes on top of each other like so (The white part is one and the brown round circle part is the other one):
The white part is positioned 1/3 the way down on top of the round brown sprite node. In the picture, the brown round part has a SKPhysicsBody applied to it already as seen by the light blue outline around it. When I add a SKPhysicsBody around the top ovalish white part it pushes it up and not in the position I wanted it.
How can I have a SKPhysics body coving both bodies of sprites but not have the physics bodies push on one another which makes the white part move upwards? I would like the white part to stay in the position it was in the first image.
Thanks for anyone help!
Here's the code I used for the SKPhysicsBody's:
// create, position, scale & add the round body
roundBody = SKSpriteNode( imageNamed: "roundBody" )
roundBody.position = CGPoint( x: 207, y: 70 )
roundBody.zPosition = 1
roundBody.xScale = 0.3
roundBody.yScale = 0.3
// add sprite node to view
self.addChild( roundBody )
// create, position, scale & add the head
theHead!.position = CGPoint( x: 207, y: roundBody.frame.maxY / 1.15 )
theHead!.zPosition = 2
theHead!.xScale = 0.3
theHead!.yScale = 0.3
// setting up a SKPhysicsBody for the round body
roundBody.physicsBody = SKPhysicsBody( circleOfRadius: roundBody.size.width / 4 )
roundBody.physicsBody!.dynamic = true
roundBody.physicsBody!.affectedByGravity = true
roundBody.physicsBody!.allowsRotation = false
roundBody.physicsBody!.pinned = false
// setting up a SKPhysicsBody for the head
theHead!.physicsBody = SKPhysicsBody(circleOfRadius: theHead!.size.width / 2 )
theHead!.physicsBody!.dynamic = true
theHead!.physicsBody!.affectedByGravity = false
theHead!.physicsBody!.allowsRotation = false
theHead!.physicsBody!.pinned = false
I was able to figure out that if you use SKPhysicsJointPin it does the exact thing I needed! (Which was to basically pin a sprite head on it's body and share a physics body)
let joinTogether = SKPhysicsJointPin.jointWithBodyA(
roundBody.physicsBody!,
bodyB:theHead!.physicsBody!,
anchor: GPointMake(CGRectGetMidX(roundBody.frame),
CGRectGetMinY(theHead!.frame)))
scene!.physicsWorld.addJoint(joint)
Hope this helps someone in the future!
If you never want it to move, set it's .dynamic property to false. Then other objects may or may not bounce/collide with it (depending upon their collisionBitMask) but it won't move in response to those collisions.
Your own answer is correct and a better solution but just to explain further.
The reason of the bodies colliding is that by default a physics body's collision bit mask is set to all categories which means it will collide with everything. In your code you are not calling
roundBody.physicsBody?.collisionBitMask = ...
which is why its using the default values.
To change that you could give your body and head a different collisionBitMask.
Im sure you will deal with this sooner or later when you handle collisions
Also as a tip it's a better idea to not force unwrap the physics bodies unless you have too, even though you know they exist. So you should replace your ! with ? whenever possible.