Detect collision between SKNode and Particles? - swift

I have this function that creates some lava:
func setupLava() {
let emitter = SKEmitterNode(fileNamed: "Lava.sks")!
emitter.particlePositionRange = CGVector(dx: 200, dy: 0.0)
emitter.advanceSimulationTime(3.0)
emitter.zPosition = 4
emitter.position = CGPoint(x: self.frame.width / 2, y: 300)
lava.addChild(emitter)
}
And I want to detect when the player collides with it. How do I do that?

From the documentation:
Particles are not represented by objects in SpriteKit. This means you
cannot perform node-related tasks on particles, nor can you associate
physics bodies with particles to make them interact with other
content. Although there is no visible class representing particles
added by the emitter node, you can think of a particle as having
properties like any other object.
So you cannot use SpriteKit to detect collisions with the emitted Lava, but you can associate a physics body to the Lava object and collide with it not it individual emitted nodes. Use categoryContactMask, contactBitMask fields of the physicsBody of your nodes to detect contacts.

Related

Why does SKNode() when assigned to a variable spawns an endless horde of enemies?

So I'm making a game where enemies fly past you and despawn when they die or leave the screen, and I was also making a play/pause button, and in order to do so I made a variable called worldNode and assigned it SKNode(). I made the variable inside no function but inside the GameScene class. Then every time I added something, I would do
worldNode.addChild(thing)
Since that allows me to pick and choose what gets paused and unpaused without freezing the whole game. But when I do it to these lines of code it spawns every enemy in at once, immediately killing the player. This code is from the update function.
if currentWave.enemies.isEmpty {
for (index, position) in positions.shuffled().enumerated() {
let enemy = EnemyNode(type: enemyTypes[enemyType], startPosition: CGPoint(x: enemyStartX, y: position), xOffset: enemyOffsetX * CGFloat(index * 3), moveStraight: true)
worldNode.addChild(enemy)
}
} else {
for enemy in currentWave.enemies {
let node = EnemyNode(type: enemyTypes[enemyType], startPosition: CGPoint(x: enemyStartX, y: positions[enemy.position]), xOffset: enemyOffsetX * enemy.xOffset, moveStraight: enemy.moveStraight)
worldNode.addChild(node)
}
}
only for the addChild(node) does this happen, and I can't leave it out because then they keep flying around the screen. What would I do to fix this?

SpriteKit Particle Emitter Multi Image

I'm trying to use SKSprite Particle Emitter with Swift.
But I want use a number of different textures in my emitter.
Is it possible to: have many images, and, have the emitter use the images randomly, instead of using only one image?
Thanks
Suppose you designed your emitter with one texture and saved it as "original.sks", and you have an array with the textures called textures:
var emitters:[SKEmitterNode] = []
for t in textures {
let emitter = SKEmitterNode(fileNamed: "original.sks")!
emitter.particleTexture = t
emitter.numParticlesToEmit /= CGFloat(emitters.count)
emitter.particleBirthRate /= CGFloat(emitters.count)
emitters.append(emitter)
}
Now you have an array of emitters instead of a single one. Whatever you'd do with your emitter, just do it with the array:
// What you'd do with a single emitter:
addChild(someNormalEmitter)
someNormalEmitter.run(someAction)
...
// How to do the same with the array:
emitters.forEach {
self.addChild($0)
$0.run(someAction)
...
}
Of course, you can also subclass SKEmitterNode so that it contains other SKEmitterNode children and propagates all the usual emitter methods and actions and properties to the children… depending on your needs.

Accessing properties of multiple SKShapeNodes

In my program I have a method called addObstacle, which creates a rectangular SKShapeNode with an SKPhysicsBody, and a leftward velocity.
func addObstacle(bottom: CGFloat, top: CGFloat, width: CGFloat){
let obstacleRect = CGRectMake(self.size.width + 100, bottom, width, (top - bottom))
let obstacle = SKShapeNode(rect: obstacleRect)
obstacle.name = "obstacleNode"
obstacle.fillColor = UIColor.grayColor()
obstacle.physicsBody = SKPhysicsBody(edgeLoopFromPath: obstacle.path!)
obstacle.physicsBody?.dynamic = false
obstacle.physicsBody?.affectedByGravity = false
obstacle.physicsBody?.contactTestBitMask = PhysicsCatagory.Ball
obstacle.physicsBody?.categoryBitMask = PhysicsCatagory.Obstacle
obstacle.physicsBody?.usesPreciseCollisionDetection = true
self.addChild(obstacle)
obstacle.runAction(SKAction.moveBy(obstacleVector, duration: obstacleSpeed))
}
In a separate method, called endGame, I want to fade out all the obstacles currently in existence on the screen. All the obstacle objects are private, which makes accessing their properties difficult. If there is only one on the screen, I can usually access it by its name. However, when I say childNodeWithName("obstacleNode")?.runAction(SKAction.fadeAlphaBy(-1.0, duration: 1.0)), only one of the "obstacles" fades away; the rest remain completely opaque. Is there a good way of doing this? Thanks in advance (:
You could probably go with:
self.enumerateChildNodesWithName("obstacleNode", usingBlock: {
node, stop in
//do your stuff
})
More about this method can be found here.
In this example I assumed that you've added obstacles to the scene. If not, then instead of scene, run this method on obstacle's parent node.
And one side note...SKShapeNode is not performant solution in many cases because it requires at least one draw pass to be rendered by the scene (it can't be drawn in batches like SKSpriteNode). If using a SKShapeNode is not "a must" in your app, and you can switch them with SKSpriteNode, I would warmly suggest you to do that because of performance.
SpriteKit can render hundreds of nodes in a single draw pass if you are using same atlas and same blending mode for all sprites. This is not the case with SKShapeNodes. More about this here. Search SO about this topic, there are some useful posts about all this.

SceneKit – Making a custom physics body

So I've designed a hallway that I want my player to walk through, but I can
't seem to get a physics body to work for it. Either the player walks through the walls or he can't walk down the hallway because it sees the object as a giant cube. How do I get the physics body to go around the object.
let chessPieces = SCNScene(named: "art.scnassets/hallway.dae")
if let knight2 = chessPieces?.rootNode.childNodeWithName("Room", recursively: true) {
knight2.position = SCNVector3Make(150, 30, 0)
knight2.scale = SCNVector3Make(knight2.scale.x * 200, knight2.scale.y * 200, knight2.scale.z * 200)
var nodeScale = NSValue(SCNVector3:SCNVector3Make(200, 200, 200));
var nodeGeometry = knight2.geometry;
var shape = SCNPhysicsShape(geometry: nodeGeometry!, options: [SCNPhysicsShapeScaleKey:nodeScale])
knight2.physicsBody = SCNPhysicsBody(type:SCNPhysicsBodyType.Static, shape: shape)
knight2.physicsBody?.categoryBitMask = rockCategory
knight2.physicsBody?.angularVelocityFactor = SCNVector3Make(0.0,0.0,0.0)
knight2.physicsBody?.collisionBitMask = 3
knight2.name = "Student"
knight2.physicsBody?.mass = 1000
scene?.rootNode.addChildNode(knight2)
}
SceneKit physics bodies model solid shapes. If you're trying to model an open space enclosed by boundaries — like a room or hallway — a single physics body won't help you. That'd fill in the volume of the room with an impassible area, and other physics bodies (with overlapping collision masks) would be forced out of that area.
If you want to make an open space enclosed by boundaries, you need to create physics bodies for the boundaries. The SceneKitVehicle sample code illustrates doing this, creating separate physics bodies for the floor and walls of a room using SCNFloor and SCNBox geometries for each.

SKAction.moveByX not setting physicsBody.velocity

I'm writing a SpriteKit game using Swift, and am using the following code to move my sprite - however, it doesn't seem to be updating velocity.dx:
func walk(isRight: Bool, speed: Float) {
var newV = (isRight ? 1 : -1) * 20 * speed;
let moveAction = SKAction.moveByX(newV, y: 0, duration: 2.0)
self.runAction(SKAction.repeatActionForever(moveAction), withKey: "walking")
println("self.physicsBody.velocity.dx: \(self.physicsBody.velocity.dx)")
}
Here's what I get in the console:
self.physicsBody.velocity.dx: 0.0
Is there something I need to do to get the moveByX to also update the velocity?
Actions and physics are largely separate mechanisms in SpriteKit. Generally, if you're using physics to move a body, you shouldn't use move actions on it. If you want to move a body that's already using physics, use physics to move it -- apply a force or impulse, or set its velocity directly.
Conversely, when you use actions to move a body, or set its position directly, those changes don't go through the physics engine. If you want to find the speed of a node during a move action, you'll have to calculate it yourself by observing the change in its position between frames.