Swift Spritekit - Only collide sprites when they are in certain positions? - ios

Hi go easy on me as I'm still pretty new to swift. I'm creating a game in which your character (on the left of the screen) must swipe up or down to change lanes (4 lanes) or jump by tapping the right side of the screen, in order to dodge obstacles coming from the right side of the screen. This has presented the problem that when the character sprite is in one lane, some of it's pixels are in the lane above, so being in a different lane doesn't shield the character from collision. (This problem also exists for when the character jumps) Is there a way to program it so the character and obstacles only collide if they are in the same lane?
I've tried to make it so the .collisionBitMask and .categoryBitMask change depending on the position of either the obstacle or character but I can't figure out how to code it correctly. I'm pretty lost to be honest. Here is some code:
runningDoggo.physicsBody = SKPhysicsBody(texture: doggoTexture, size: runningDoggo.size)
runningDoggo.physicsBody?.affectedByGravity = false
runningDoggo.physicsBody?.isDynamic = true
runningDoggo.physicsBody?.collisionBitMask = 0x1 << 2
bush.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "8-bit bush"), size: bush.size)
bush.physicsBody?.isDynamic = true
bush.physicsBody?.affectedByGravity = false
bush.physicsBody?.categoryBitMask = 0x1 << 1
And then I have a set of switch cases in a function for randomly selecting which lane the bush comes in
switch randomLane{
case 1:
bush.position = CGPoint(x: 0.4*UIScreen.main.bounds.width, y:-0.07*UIScreen.main.bounds.height)
if(bush.position.y < runningDoggo.position.y) {
bush.zPosition = 6
}
self.addChild(bush)
case 2:
bush.position = CGPoint(x: 0.4*UIScreen.main.bounds.width, y:-0.18*UIScreen.main.bounds.height)
if(bush.position.y < runningDoggo.position.y) {
bush.zPosition = 6
}
self.addChild(bush)
case 3:
bush.position = CGPoint(x: 0.4*UIScreen.main.bounds.width, y:-0.29*UIScreen.main.bounds.height)
if(bush.position.y < runningDoggo.position.y) {
bush.zPosition = 6
}
self.addChild(bush)
case 4:
bush.position = CGPoint(x: 0.4*UIScreen.main.bounds.width, y:-0.4*UIScreen.main.bounds.height)
if(bush.position.y < runningDoggo.position.y) {
bush.zPosition = 6
}
self.addChild(bush)
default:
print("How is this not 1,2,3, or 4?")
}
bush.run(moveAndRemoveBush)
I want it to only collide (EDIT: Not to collide but to contact) the runningDoggo sprite with the bush sprite if they were in the same lane and pass by without colliding if the character isn't in the obstacle's lane.

Firstly, that complicated switch could be simplified to something like this (anytime you write the same code more than once, think if you can simplify it):
bush.position.x = 0.40 * UIScreen.main.bounds.width
bush.position.y = 0.07 * randomLane * UIScreen.main.bounds.height) // 0.07, 0.14, 0.21 or 0.28 for lanes 1-4
if (bush.position.y < runningDoggo.position.y) bush.zPosition = 6
self.addChild(bush)
bush.run(moveAndRemoveBush)
OK - I know that the y values of 0.07, 0.14, 0.21 and 0.28 don't match up with your values of 0.07, 0.18, 0.29 and 0.40 but maybe that's some thing you can tweek, or define an offset array as a property:
let bushYOffset = [0.07, 0.18, 0.29, 0.40]
and then just pick the offset corresponding to the lane
bush.position.x = 0.40 * UIScreen.main.bounds.width
bush.position.y = bushYOffset[randonLane-1] * UIScreen.main.bounds.height)
if (bush.position.y < runningDoggo.position.y) bush.zPosition = 6
self.addChild(bush)
bush.run(moveAndRemoveBush)
To address your main problem, you could create a bitmask for each lane in an array:
let laneMask: [UInt32] = [1<<1, 1<<2, 1<<3, 1<<4]
When Doggo moves to a new lane, give Doggo's physicsBody corresponding to collisonBitmask the lane it has just moved into:
runningDoggo.physicsBody?.collisionBitMask = laneMask[doggoLane - 1] // Arrays start at 0
When you spawn an obstacle, also give it the collision bitMask for the lane it is in:
bush.physicsBody?.collisionBitMask = laneMask[randomLane - 1] // Arrays start at 0
Now Doggo and the bush will only collide if they are in the same lane. If they are in different lanes they won't, even if some of their pixels overlap.
You may have to adjust this if there are other sprites on screen that doggo and the bush need to interact with.
NOTE:
Are you sure you want them to collide and not contact? Collisions occur when sprites collide and bounce off each other and fly around the screen; contacts are where 2 sprites touch and your code gets called and you can make one explode, or losee a life, or increase a score etc.
Edit: my step-by-step guide for collisions and contacts:
https://stackoverflow.com/a/51041474/1430420
And a guide to collision and contactTest bit masks:
https://stackoverflow.com/a/40596890/1430420
Manipulating bit masks to turn individual collision ans contacts off and on.
https://stackoverflow.com/a/46495864/1430420

Related

iOS ARKit + SceneKit physics contact detection scaling issue

I have a simple 3d area that contains 4 walls, each is a SCNNode with a simple SCNBox geometry, of rectangular shape, and matching SCNPhysicsBody attached. The SCNPhysicsBody uses a SCNPhysicsShape.ShapeType.boundingBox, and is set to static type. Here is a code snippet:
let size = (self.levelNode.boundingBox.max - self.levelNode.boundingBox.min) * self.levelNode.scale
//x //z
let geometryA = SCNBox(width: CGFloat(size.x), height: CGFloat(1 * self.levelNode.scale.x), length: 0.01, chamferRadius: 0)
let geometryB = SCNBox(width: CGFloat(size.z), height: CGFloat(1 * self.levelNode.scale.x), length: 0.01, chamferRadius: 0)
geometryA.firstMaterial?.diffuse.contents = UIColor(red: 0.0, green: 0.2, blue: 1.0, alpha: 0.65)
geometryB.firstMaterial?.diffuse.contents = UIColor(red: 0.0, green: 0.2, blue: 1.0, alpha: 0.65)
let nodeA = SCNNode(geometry: geometryA)
nodeA.position += self.levelNode.position
nodeA.position += SCNVector3(0, 0.25 * self.levelNode.scale.y, -size.z/2)
nodeA.name = "Boundary-01"
let nodeB = SCNNode(geometry: geometryA)
nodeB.position += self.levelNode.position
nodeB.position += SCNVector3(0, 0.25 * self.levelNode.scale.y, size.z/2)
nodeB.name = "Boundary-03"
let nodeC = SCNNode(geometry: geometryB)
nodeC.position += self.levelNode.position
nodeC.position += SCNVector3(-size.x/2, 0.25 * self.levelNode.scale.y, 0)
nodeC.eulerAngles = SCNVector3(0, -Float.pi/2, 0)
nodeC.name = "Boundary-02"
let nodeD = SCNNode(geometry: geometryB)
nodeD.position += self.levelNode.position
nodeD.position += SCNVector3(size.x/2, 0.25 * self.levelNode.scale.y, 0)
nodeD.eulerAngles = SCNVector3(0, Float.pi/2, 0)
nodeD.name = "Boundary-04"
let nodes = [nodeA, nodeB, nodeC, nodeD]
for node in nodes {
//
let shape = SCNPhysicsShape(geometry: node.geometry!, options: [
SCNPhysicsShape.Option.type : SCNPhysicsShape.ShapeType.boundingBox])
let body = SCNPhysicsBody(type: .static, shape: shape)
node.physicsBody = body
node.physicsBody?.isAffectedByGravity = false
node.physicsBody?.categoryBitMask = Bitmask.boundary.rawValue
node.physicsBody?.contactTestBitMask = Bitmask.edge.rawValue
node.physicsBody?.collisionBitMask = 0
scene.rootNode.addChildNode(node)
node.physicsBody?.resetTransform()
}
Inside this area, I spawn entities at a regular time interval. Each also has a SCNBox geometry, that is a cube shape this time, smaller than the walls, and same parameters for the physics body as above.
To simplify the behaviour of my entities inside this game area, I am calculating their paths to travel, then applying a SCNAction to the relevant node to move them. The SCNAction moves both the node and physics body attached to it.
I am using the SCNPhysicsWorld contact delegate to detect when an entity reaches one of the boundary walls. I then calculate a random trajectory for it from that wall in another direction, clear its actions, and apply a new move SCNAction.
This is where it gets interesting...
When this 'world' is at 1:1 scale. The contacts are detected as normal both in a standard SCNScene, and a scene projected using ARKit. The visible contact, i.e. the visible change in direction of the entity appears to be close to the boundary as expected. When I check the contact.penetrationDistance of each contact their values are e.g. 0.00294602662324905.
BUT when I change the scale of this 'world' to something smaller, say the equivalent of 10cm width, in ARKit, the simulation falls apart.
The contacts between an entity and a boundary node have a comparatively huge visible gap between them when the contact is detected. Yet the contact.penetrationDistance is of the same magnitude as before.
I switched on the ARSCNView debug options to show the physics shapes in the render, and they all appear to be the correct proportions, matching the bounding box of their node.
As you can see from the code example above, the boundary nodes are generated after I have scaled the level, during my AR user setup. They are added to the root node of the scene, not as a child of the level node. The same code is being used to generate the entities.
Previously I had tried using the resetTransform() function on the physics bodies but this did not produce a reliable scaling of the physics bodies, after I had scaled the level, so I decided to generate the nodes for the boundaries and entities after the level has been scaled.
In Apple's documentation, it does state that if the SCNPhysicsBody is not a custom shape, that it will adopt the scale of the node geometry applied to it. I am not affected by this as I am generating the geometries and their respective nodes, after the scaling has been applied to the level.
One of assumptions at the moment is that the physics simulation falls apart at such a small scale. But I am not relying on the simulation of forces to move the bodies ...
Is there a more appropriate way to scale the physics world?
Or, am I staring a bug in the SCNPhysicsWorld, that is something beyond my control, at the moment.
One solution I did think about was to run the entire simulation at 1:1 scale but hidden, then apply those movements to the smaller entities. As you can imagine, that will affect the performance of the entire scene...
The penetration distance of the first contact is a negative value, suggesting there is a gap. This gap does not appear to scale as you scale down the size of the simulation.
As a way to resolve the above excess, I have implemented an additional check on the contacts in the Contact Delegate to not take the first contact detected for a particular category, but rather ensure the penetrationDistance value is positive, so ensuring that there is overlap between the two objects, before triggering a change in direction of the entity which connected with a boundary.

Custom Particle System for iOS

I want to create a particle system on iOS using sprite kit where I define the colour of each individual particle. As far as I can tell this isn't possible with the existing SKEmitterNode.
It seems that best I can do is specify general behaviour. Is there any way I can specify the starting colour and position of each particle?
This can give you a basic idea what I was meant in my comments. But keep in mind that it is untested and I am not sure how it will behave if frame rate drops occur.
This example creates 5 particles per second, add them sequentially (in counterclockwise direction) along the perimeter of a given circle. Each particle will have different predefined color. You can play with Settings struct properties to change the particle spawning speed or to increase or decrease number of particles to emit.
Pretty much everything is commented, so I guess you will be fine:
Swift 2
import SpriteKit
struct Settings {
static var numberOfParticles = 30
static var particleBirthRate:CGFloat = 5 //Means 5 particles per second, 0.2 means one particle in 5 seconds etc.
}
class GameScene: SKScene {
var positions = [CGPoint]()
var colors = [SKColor]()
var emitterNode:SKEmitterNode?
var currentPosition = 0
override func didMoveToView(view: SKView) {
backgroundColor = .blackColor()
emitterNode = SKEmitterNode(fileNamed: "rain.sks")
if let emitter = emitterNode {
emitter.position = CGPoint(x: CGRectGetMidX(frame), y: CGRectGetMidY(frame))
emitter.particleBirthRate = Settings.particleBirthRate
addChild(emitter)
let radius = 50.0
let center = CGPointZero
for var i = 0; i <= Settings.numberOfParticles; i++ {
//Randomize color
colors.append(SKColor(red: 0.78, green: CGFloat(i*8)/255.0, blue: 0.38, alpha: 1))
//Create some points on a perimeter of a given circle (radius = 40)
let angle = Double(i) * 2.0 * M_PI / Double(Settings.numberOfParticles)
let x = radius * cos(angle)
let y = radius * sin(angle)
let currentParticlePosition = CGPointMake(CGFloat(x) + center.x, CGFloat(y) + center.y)
positions.append(currentParticlePosition)
if i == 1 {
/*
Set start position for the first particle.
particlePosition is starting position for each particle in the emitter's coordinate space. Defaults to (0.0, 0,0).
*/
emitter.particlePosition = positions[0]
emitter.particleColor = colors[0]
self.currentPosition++
}
}
// Added just for debugging purposes to show positions for every particle.
for particlePosition in positions {
let sprite = SKSpriteNode(color: SKColor.orangeColor(), size: CGSize(width: 1, height: 1))
sprite.position = convertPoint(particlePosition, fromNode:emitter)
sprite.zPosition = 2
addChild(sprite)
}
let block = SKAction.runBlock({
// Prevent strong reference cycles.
[unowned self] in
if self.currentPosition < self.positions.count {
// Set color for the next particle
emitter.particleColor = self.colors[self.currentPosition]
// Set position for the next particle. Keep in mind that particlePosition is a point in the emitter's coordinate space.
emitter.particlePosition = self.positions[self.currentPosition++]
}else {
//Stop the action
self.removeActionForKey("emitting")
emitter.particleBirthRate = 0
}
})
// particleBirthRate is a rate at which new particles are generated, in particles per second. Defaults to 0.0.
let rate = NSTimeInterval(CGFloat(1.0) / Settings.particleBirthRate)
let sequence = SKAction.sequence([SKAction.waitForDuration(rate), block])
let repeatAction = SKAction.repeatActionForever(sequence)
runAction(repeatAction, withKey: "emitting")
}
}
}
Swift 3.1
import SpriteKit
struct Settings {
static var numberOfParticles = 30
static var particleBirthRate:CGFloat = 5 //Means 5 particles per second, 0.2 means one particle in 5 seconds etc.
}
class GameScene: SKScene {
var positions = [CGPoint]()
var colors = [SKColor]()
var emitterNode: SKEmitterNode?
var currentPosition = 0
override func didMove(to view: SKView) {
backgroundColor = SKColor.black
emitterNode = SKEmitterNode(fileNamed: "rain.sks")
if let emitter = emitterNode {
emitter.position = CGPoint(x: frame.midX, y: frame.midY)
emitter.particleBirthRate = Settings.particleBirthRate
addChild(emitter)
let radius = 50.0
let center = CGPoint.zero
for var i in 0...Settings.numberOfParticles {
//Randomize color
colors.append(SKColor(red: 0.78, green: CGFloat(i * 8) / 255.0, blue: 0.38, alpha: 1))
//Create some points on a perimeter of a given circle (radius = 40)
let angle = Double(i) * 2.0 * Double.pi / Double(Settings.numberOfParticles)
let x = radius * cos(angle)
let y = radius * sin(angle)
let currentParticlePosition = CGPoint.init(x: CGFloat(x) + center.x, y: CGFloat(y) + center.y)
positions.append(currentParticlePosition)
if i == 1 {
/*
Set start position for the first particle.
particlePosition is starting position for each particle in the emitter's coordinate space. Defaults to (0.0, 0,0).
*/
emitter.particlePosition = positions[0]
emitter.particleColor = colors[0]
self.currentPosition += 1
}
}
// Added just for debugging purposes to show positions for every particle.
for particlePosition in positions {
let sprite = SKSpriteNode(color: SKColor.orange, size: CGSize(width: 1, height: 1))
sprite.position = convert(particlePosition, from: emitter)
sprite.zPosition = 2
addChild(sprite)
}
let block = SKAction.run({
// Prevent strong reference cycles.
[unowned self] in
if self.currentPosition < self.positions.count {
// Set color for the next particle
emitter.particleColor = self.colors[self.currentPosition]
// Set position for the next particle. Keep in mind that particlePosition is a point in the emitter's coordinate space.
emitter.particlePosition = self.positions[self.currentPosition]
self.currentPosition += 1
} else {
//Stop the action
self.removeAction(forKey: "emitting")
emitter.particleBirthRate = 0
}
})
// particleBirthRate is a rate at which new particles are generated, in particles per second. Defaults to 0.0.
let rate = TimeInterval(CGFloat(1.0) / Settings.particleBirthRate)
let sequence = SKAction.sequence([SKAction.wait(forDuration: rate), block])
let repeatAction = SKAction.repeatForever(sequence)
run(repeatAction, withKey: "emitting")
}
}
}
Orange dots are added just for debugging purposes and you can remove that part if you like.
Personally I would say that you are overthinking this, but I might be wrong because there is no clear description of what you are trying to make and how to use it. Keep in mind that SpriteKit can render a bunch of sprites in a single draw call in very performant way. Same goes with SKEmitterNode if used sparingly. Also, don't underestimate SKEmitterNode... It is very configurable actually.
Here is the setup of Particle Emitter Editor:
Anyways, here is the final result:
Note that nodes count comes from an orange SKSpriteNodes used for debugging. If you remove them, you will see that there is only one node added to the scene (emitter node).
What you want is completely possible, probably even in real time. Unfortunately to do such a thing the way you describe with moving particles as being a particle for each pixel would be best done with a pixel shader. I don't know of a clean method that would allow you to draw on top of the scene with a pixel shader otherwise all you would need is a pixel shader that takes the pixels and moves them out from the center. I personally wouldn't try to do this unless I built the game with my own custom game engine in place of spritekit.
That being said I'm not sure a pixel per pixel diffusion is the best thing in most cases. Expecially if you have cartoony art. Many popular games will actually make sprites for fragments of the object they expect to shader. So like if it's an airplane you might have a sprite for the wings with perhaps even wires hanging out of this. Then when it is time to shatter the plane, remove it from the scene and replace the area with the pieces in the same shape of the plane... Sorta like a puzzle. This will likely take some tweaking. Then you can add skphysicsbodies to all of these pieces and have a force push them out in all directions. Also this doesn't mean that each pixel gets a node. I would suggest creatively breaking it into under 10 pieces.
And as whirlwind said you could all ways get things looking "like" it actually disintegrated by using an emitter node. Just make the spawn area bigger and try to emulate the color as much as possible. To make the ship dissappear you could do a fade perhaps? Or Mabye an explosion sprite over it? Often with real time special effects and physics, or with vfx it is more about making it look like reality then actually simulating reality. Sometimes you have to use trickery to get things to look good and run real-time.
If you want to see how this might look I would recommend looking at games like jetpac joyride.
Good luck!

Nodes Spawning off of screen in sprite kit swift

I am building a ios game with swift and I have run into a bit of a problem. I am trying to spawn balls from the top of the screen and have them come down towards the ground. They are supposed to have random x values and go down at random rates but instead of spawning on the screen the nodes spawn on an x value which is not encompassed by the screen. Please help me as I think I have done everything right.
Here is the code for my addball function...
func addBall(){
//create ball sprite
var ball = SKSpriteNode(imageNamed: "ball.png")
//create physics for ball
ball.physicsBody = SKPhysicsBody(rectangleOfSize: ball.size) // 1
ball.physicsBody?.dynamic = true // 2
ball.physicsBody?.categoryBitMask = PhysicsCategory.Ball // 3
ball.physicsBody?.contactTestBitMask = PhysicsCategory.Person & PhysicsCategory.Ground
ball.physicsBody?.collisionBitMask = PhysicsCategory.None // 5
//generate random postion along x axis for ball to spawn
let actualX = random(min:ball.frame.size.width/2+1, max: self.frame.size.width - ball.frame.size.width/2-1)
println(actualX)
//set balls positon
ball.position = CGPoint(x: actualX, y: size.height - ball.size.width/2)
//add ball to scene
addChild(ball)
//determine speed of ball
let actualDuration = random(min: CGFloat(3.0), max: CGFloat(5.0))
//create movement actions
let actionMove = SKAction.moveTo(CGPoint(x:actualX, y: -ball.size.width/2), duration: NSTimeInterval(actualDuration))
let actionMoveDone = SKAction.removeFromParent()
ball.runAction(SKAction.sequence([actionMove, actionMoveDone]), withKey: "action")
}
here is the code for my random functions
func random() -> CGFloat {
return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
}
func random(#min: CGFloat, max: CGFloat) -> CGFloat {
return random() * (max - min) + min
}
The problem here is that your SKScene likely takes up much more space than the screen of your device. Thus, when you calculate a random value using the whole scene, some of the time the ball will spawn in the area of the scene not visible to you.
The two main properties that control the scene's size are its size and scaleMode properties. The scaleMode property relates to how the scene is mapped. Unless you initialized and presented this scene yourself, you can check the scaleMode in your view controller. It will likely be set to aspectFill, which according to Apple means:
The scaling factor of each dimension is calculated and the larger of the two is chosen. Each axis of the scene is scaled by the same scaling factor. This guarantees that the entire area of the view is filled but may cause parts of the scene to be cropped.
If you don't like this, there are other scaleModes. However, in most cases this mode would actually be preferable since SpriteKit's internal scaling is able to make universal apps. If this is fine for you, then the easiest thing to do is set hardcoded values for something like the spawn locations for your ball node.

How to resolve an unwanted collision in a complicated SpriteKit simulation?

(If you want to cut to the chase go to the bold section).
I am having an unwanted collision in my simulation. My simulation has the following groups:
TeethTop (this is a group of objects)
TeethBottom (this is a group of objects)
Ball (the main object that is moved in the game)
Line (one line that is drawn occasionally only to detect if user swiped to a tooth)
Boundries (boundaries of screen)
Here are the rules I need
I need SKcontacts generated when
ball hits any teeth (top or bottom)
TeethTop hits any teeth
Line hits any teeth
As far as physical contacts and reactions (who can hit who and have physics simulated)
The line can not impede movement or hit the ball
The ball must land on the teeth but never move the teeth
No tooth must ever physically move due to physics
No tooth must ever react to a contact with another tooth
The ball needs to never pass through the boundaries
Ill throw in gravity
The ball is the only thing that reacts to gravity
Things that are dynamic (not in code)
The ball is dynamic because it reacts to gravity and hitting other teeth
Everything else stays put no matter what (however I had to tell the teeth that they were dynamic so I could have SKPhysicsContacts when the top hits the bottom)
Basically what happens is their are two rows of teeth, top and bottom. And they do their thing and become a stage. The walls are the same way. The ball moves around inside the mouth are like a platformer hitting and bounding off teeth.
On swipes a SKPhysicsNode line is created following the swipe and detecting which tooth the user swiped towards (**** MAYBE their is a better way then making a SKPhyiscsNode for the swipe line... I still need to detect if the user swiped in the direction of a tooth). And everything is dandy for awhile, then the mouth closes and stops when a tooth from the top hits a tooth from the bottom.
I had things all fine and dandy with things set as thus
BottomTeethGroup = 10
TopTeethGroup = 31
playergroup = 2
wallgroup = 1
lineleart = 99
Top Teeth
affectedbygravity = false
collisionbitmask = 0
categorybitmask = BottomTeethGroup
contacttTest = TopTeethGroup
dynamic = false
Bottom Teeth
affectedbygravity = false
collisionbitmask = 0
categorybitmask = TopTeethGroup
contacttTest = BottomTeethGroup
dynamic = false
Ball
affectedbygravity = false
the collision bit mask is never set so it collides with everything
categorybitmask = playergroup
contacttTest = playergroup
Dynamic = true
Boundaries
categorybitmask = wallgroup
dynamic = false
Line
collisionbitmask = 0
categorybitmask = lineleart
I have tried setting physics properties to crazy values but I just cant get the ball to pass through
The problem arises when I add the line. Everything with detection works fine until you add the line. Unfortunately the ball collides with the line creating a physical reaction which just isn't good! The ball should not physically collide with the line!
The way I see it their are some options:
Make is to the ball passes through the line, or ignores collisions between the two
Find a different way to detect which tooth the player swiped at (the current method is flawed anyway because the line will detect multiple teeth, just because it has a slope and doesn't stop once it hits the first). I have the coordinates of the start and end and the direction if someone has a better way.
Probably if we could make it so the top teeth and bottom teeth only have physical contact with ones from the other side then things would get easier.
Make it look like the ball is passing through the line
Edit: Perhaps my definitions of things are flawed so here is how I think contacts work:
collisionbitmask: will only collide with other objects that have the number you specify after it. If 0 then it collides with no objects, if not set then it collides with all
categorybitmask: defines what category is, if an object has a category test bit mask with this value then a SKPhysics contact will be created, however their might not necessarily be a physics reaction
contactTest: If it hits and the other object has the same categorybitmask as the one set here a SKphysics contact will be made
dynamic objects have physical reactions when they hit other dynamic objects, or non dynamic objects. SKPhysicsContacts are written up for all collisions
non dynamic objects will not move in any collision, and will only write up SKPhysicscontacts if the other object is static. Two non dynamic objects are not expected to collide.
List of solutions I have tried:
Lines collisionbitmask not set (same problem)
Cavities collisionbitmask set to 0 (falls through random teeth however it doesn't collide with the line but still generates a SKPhysicsContact)
All teeths' collision bit mask set to 4 (A BIIG MESS)
Top teeth collision bit mask set to 2 and bottom to 4 (less of a mess and when cavity hits a tooth it drifts like its in 0 g (it is in 0 g))
Edit:
I have been trying a bunch of things and none of them work. I got fed up and created a new project with four balls. Basically you set each ball to a specific group type and run the simulation. I have it so their are variables at the top for all the categories. If the simulation is set up right (I believe it is) the program WI simulate what would happen if all the values at the top are correct.
You can also find the code below
//
//  GameScene.swift
//  CollisionTesting
//
//  Created on 8/13/15.
//  Copyright (c) 2015 BroccoliPresentations. All rights reserved.
//
 
import SpriteKit
enum type
{
    case _toptooth
    case _bottomtooth
    case _ball
    case _line
    case _boundary
    case _none
}
class GameScene: SKScene {
    override func didMoveToView(view: SKView) {
        
        //The physics categories for each of them
        var topteethcategory:UInt32 = (0x01 << 1)
        var bottomteethcategory:UInt32 = (0x01 << 2)
        var ballcategory:UInt32 = (0x01 << 3)
        var boundriesgroup:UInt32 = (0x01 << 4)
        var linegroup:UInt32 = (0x01 << 5)
        
        //When collision reports should be written up
        var topteethcolisionreport:UInt32 = bottomteethcategory
        var bottomteethcollisionreport:UInt32 = topteethcategory
        var ballcollisionreport:UInt32 = bottomteethcategory | topteethcategory
        var boundriescollisionreport:UInt32 = 0
        var linecollisionreport:UInt32 = topteethcategory | bottomteethcategory
        
        //When they should bounce off of eachother
        var topteethphysics:UInt32 = bottomteethcategory
        var bottomteethphysics:UInt32 = topteethcategory
        var ballphysics:UInt32 = bottomteethcategory | topteethcategory | boundriesgroup
        var boundriesphysics:UInt32 = boundriesgroup
        var linecollisionphysics:UInt32 = linegroup
        
        
        //Here you can set what tope of object each color is
        var blue:type = type._toptooth
        var red:type = type._bottomtooth
        var yellow:type = type._ball
        var green:type = type._none
        
        
        var xy = CGRectGetMidX(self.frame) * 1.5
        var ball1 = SKSpriteNode(texture: SKTexture(imageNamed: "Blue"))
        ball1.position = CGPoint(x: xy, y:CGRectGetMidY(self.frame));
        ball1.size = CGSizeMake(100, 100)
        ball1.physicsBody?.affectedByGravity = false
        ball1.physicsBody = SKPhysicsBody(circleOfRadius: 50)
        ball1.physicsBody?.velocity = CGVector(dx: -100, dy: 0)
        
        var ball2 = SKSpriteNode(texture: SKTexture(imageNamed: "Red"))
        ball2.position = CGPoint(x:xy / 2, y:CGRectGetMidY(self.frame));
        ball2.size = CGSizeMake(100, 100)
        ball2.physicsBody?.affectedByGravity = false
        ball2.physicsBody = SKPhysicsBody(circleOfRadius: 50)
        ball2.physicsBody?.velocity = CGVector(dx: 100, dy: 0)
        
        var ball3 = SKSpriteNode(texture: SKTexture(imageNamed: "Yellow"))
        ball3.position = CGPoint(x:((xy / 4) + xy) / 2, y:CGRectGetMidY(self.frame) / 2);
        ball3.size = CGSizeMake(100, 100)
        ball3.physicsBody?.affectedByGravity = false
        ball3.physicsBody = SKPhysicsBody(circleOfRadius: 50)
        ball3.physicsBody?.velocity = CGVector(dx: 0, dy: 100)
        
        var ball4 = SKSpriteNode(texture: SKTexture(imageNamed: "Green"))
        ball4.position = CGPoint(x:((xy / 4) + xy) / 2, y:CGRectGetMidY(self.frame) * 1.5);
        ball4.size = CGSizeMake(100, 100)
        ball4.physicsBody?.affectedByGravity = false
        ball4.physicsBody = SKPhysicsBody(circleOfRadius: 50)
        ball4.physicsBody?.velocity = CGVector(dx: 0, dy: -100)
        self.addChild(ball1)
        self.addChild(ball2)
        self.addChild(ball3)
        self.addChild(ball4)
        
        
        switch (blue)
        {
        case type._toptooth:
                ball1.physicsBody?.categoryBitMask = topteethcategory
                ball1.physicsBody?.collisionBitMask = topteethphysics
                ball1.physicsBody?.contactTestBitMask = topteethcolisionreport
        case type._bottomtooth:
            ball1.physicsBody?.categoryBitMask = bottomteethcategory
            ball1.physicsBody?.collisionBitMask = bottomteethphysics
            ball1.physicsBody?.contactTestBitMask = bottomteethcollisionreport
        case type._ball:
            ball1.physicsBody?.categoryBitMask = ballcategory
            ball1.physicsBody?.collisionBitMask = ballphysics
            ball1.physicsBody?.contactTestBitMask = ballcollisionreport
        case type._boundary:
            ball1.physicsBody?.categoryBitMask = boundriesgroup
            ball1.physicsBody?.collisionBitMask = boundriesphysics
            ball1.physicsBody?.contactTestBitMask = boundriescollisionreport
        case type._line:
            ball1.physicsBody?.categoryBitMask = linegroup
            ball1.physicsBody?.collisionBitMask = linecollisionphysics
            ball1.physicsBody?.contactTestBitMask = linecollisionreport
        case type._none:
            ball1.position = CGPointMake(100000, 10000)
        }
        
        switch (red)
        {
        case type._toptooth:
            ball2.physicsBody?.categoryBitMask = topteethcategory
            ball2.physicsBody?.collisionBitMask = topteethphysics
            ball2.physicsBody?.contactTestBitMask = topteethcolisionreport
        case type._bottomtooth:
            ball2.physicsBody?.categoryBitMask = bottomteethcategory
            ball2.physicsBody?.collisionBitMask = bottomteethphysics
            ball2.physicsBody?.contactTestBitMask = bottomteethcollisionreport
        case type._ball:
            ball2.physicsBody?.categoryBitMask = ballcategory
            ball2.physicsBody?.collisionBitMask = ballphysics
            ball2.physicsBody?.contactTestBitMask = ballcollisionreport
        case type._boundary:
            ball2.physicsBody?.categoryBitMask = boundriesgroup
            ball2.physicsBody?.collisionBitMask = boundriesphysics
            ball2.physicsBody?.contactTestBitMask = boundriescollisionreport
        case type._line:
            ball2.physicsBody?.categoryBitMask = linegroup
            ball2.physicsBody?.collisionBitMask = linecollisionphysics
            ball2.physicsBody?.contactTestBitMask = linecollisionreport
        case type._none:
            ball2.position = CGPointMake(100000, 10000)
        }
        
        switch (blue)
        {
        case type._toptooth:
            ball3.physicsBody?.categoryBitMask = topteethcategory
            ball3.physicsBody?.collisionBitMask = topteethphysics
            ball3.physicsBody?.contactTestBitMask = topteethcolisionreport
        case type._bottomtooth:
            ball3.physicsBody?.categoryBitMask = bottomteethcategory
            ball3.physicsBody?.collisionBitMask = bottomteethphysics
            ball3.physicsBody?.contactTestBitMask = bottomteethcollisionreport
        case type._ball:
            ball3.physicsBody?.categoryBitMask = ballcategory
            ball3.physicsBody?.collisionBitMask = ballphysics
            ball3.physicsBody?.contactTestBitMask = ballcollisionreport
        case type._boundary:
            ball3.physicsBody?.categoryBitMask = boundriesgroup
            ball3.physicsBody?.collisionBitMask = boundriesphysics
            ball3.physicsBody?.contactTestBitMask = boundriescollisionreport
        case type._line:
            ball3.physicsBody?.categoryBitMask = linegroup
            ball3.physicsBody?.collisionBitMask = linecollisionphysics
            ball3.physicsBody?.contactTestBitMask = linecollisionreport
        case type._none:
            ball3.position = CGPointMake(100000, 10000)
        }
        
        switch (green)
        {
        case type._toptooth:
            ball4.physicsBody?.categoryBitMask = topteethcategory
            ball4.physicsBody?.collisionBitMask = topteethphysics
            ball4.physicsBody?.contactTestBitMask = topteethcolisionreport
        case type._bottomtooth:
            ball4.physicsBody?.categoryBitMask = bottomteethcategory
            ball4.physicsBody?.collisionBitMask = bottomteethphysics
            ball4.physicsBody?.contactTestBitMask = bottomteethcollisionreport
        case type._ball:
            ball4.physicsBody?.categoryBitMask = ballcategory
            ball4.physicsBody?.collisionBitMask = ballphysics
            ball4.physicsBody?.contactTestBitMask = ballcollisionreport
        case type._boundary:
            ball4.physicsBody?.categoryBitMask = boundriesgroup
            ball4.physicsBody?.collisionBitMask = boundriesphysics
            ball4.physicsBody?.contactTestBitMask = boundriescollisionreport
        case type._line:
            ball4.physicsBody?.categoryBitMask = linegroup
            ball4.physicsBody?.collisionBitMask = linecollisionphysics
            ball4.physicsBody?.contactTestBitMask = linecollisionreport
        case type._none:
            ball4.position = CGPointMake(100000, 10000)
        }
        
        
        
        physicsWorld.gravity = CGVector(dx: 0, dy: 0)
    }
    
 
    override func update(currentTime: CFTimeInterval) {
        /* Called before each frame is rendered */
    }
}
 
//nothing set they collide to eachother
//contactTestbitmask has nothing to do with bumping against eachother
//Category is the most important it gives it a type
//The other two decide when reactions happen, either physical or in code
//Collision bitmask is a list of groups that it actually should collide with
//Contacttestbitmask is list of groups that can trigger the code
I have figured it out! My problem is I was making things much too complicated. I actually didnt need all of these groups! Here is what I did!
var stage:UInt32 = (0x1 << 1)
var player:UInt32 = (0x1 << 2)
var sensors:UInt32 = (0x1 << 3)
//Physics collision bitmask
var p_stage:UInt32 = 0
var p_player:UInt32 = 0 | stage
var p_sensors:UInt32 = 0
//Test bitmask
var t_stage:UInt32 = stage
var t_player:UInt32 = stage
var t_sensors:UInt32 = stage
I actually didnt need as many groups as I thought I did! However I did have to store some data in the nodes name in order to tell wether it was from the top or bottom or which row, but overall it worked awesome!
Thank you guys who looked over everything, it really means a lot!
Something I found odd was that doing
(0x1 << 1)
(0x1 << 2)
(0x1 << 3)
was better then
1
2
3
I guess I really dont know what I am doing with the (0x1 << 1) thingy but I saw it somewhere else and copied it!

Random movements / turbulences - SWIFT

I'm developing a game on Iphone and Ipad like a space invaders.
Balloons to destroy are falling from the top of the screen in a straight line.
Here my codes to add them :
func addBalloonv(){
var balloonv:SKSpriteNode = SKSpriteNode (imageNamed: "ballonvert.png")
balloonv.physicsBody = SKPhysicsBody (circleOfRadius: balloonv.size.width/2)
balloonv.physicsBody.dynamic = true
balloonv.physicsBody.categoryBitMask = balloonCategory | greenCategory
balloonv.physicsBody.contactTestBitMask = flechetteCategory
balloonv.physicsBody.collisionBitMask = balloonCategory
balloonv.physicsBody.mass = 1
balloonv.physicsBody.restitution = 1
balloonv.physicsBody.allowsRotation = true
let minX = balloonv.size.width/2
let maxX = self.frame.size.width - balloonv.size.width/2
let rangeX = maxX - minX
let position:CGFloat = CGFloat(arc4random()) % CGFloat(rangeX) + CGFloat(minX)
balloonv.position = CGPointMake(position, self.frame.size.height+balloonv.size.height)
self.addChild(balloonv)
I have one func by balloon color.
So for the moment they move in straight line and I'm looking for random movements (with turbulences like balloon in air) from the top and both sides.
How can I do that?
Thank you very much !!
This is exactly what the new Physics Fields feature in SpriteKit (as of iOS 8 / OS X Yosemite) is for. These let you apply different kinds of forces to all physics bodies in region, like gravity, drag, and turbulence. See the SKFieldNode class docs for details.
Fields are a kind of node, so to get what you're after, you'd add one noise (or turbulence) field to your scene, covering the area that the balloons fall through, and it'll perturb the path of each balloon that passes. The simplest way to do it goes something like this:
let field = SKFieldNode.noiseFieldWithSmoothness(0.5, animationSpeed: 0.1)
scene.addChild(field)
You'll want to tweak the smoothness, animation speed, and field.strength till you get just the level of noise you want. You might also look into whether you want just a noise field, which applies random forces in random directions, or a turbulence field, which does the same thing, but with forces that get stronger when bodies are moving faster.
The above code gets you a field whose region of effect is infinite. You might want to limit it to a specific area (for example, so it doesn't keep knocking your balloons around after they land). I did this to make a field that covers only the top 3/4 of a 300x200 scene:
field.region = SKRegion(size: CGSize(width: 300, height: 100))
field.position = CGPoint(x: 150, y: 150)

Resources